From d70e5ac6c54c568bebf393a820b4ff7327a5cad4 Mon Sep 17 00:00:00 2001 From: fosol Date: Mon, 30 Mar 2020 09:31:52 -0700 Subject: [PATCH] PIMS-1284, PIMS-1227 Resolving AutoMapper issues --- backend/.gitignore | 30 +- backend/Pims.sln | 60 +++- .../Admin/Controllers/ParcelController.cs | 139 +++------ .../Areas/Admin/Controllers/RoleController.cs | 8 +- .../Areas/Admin/Controllers/UserController.cs | 16 +- .../Areas/Admin/Models/Parcel/AddressModel.cs | 53 ++++ .../Admin/Models/{ => Parcel}/AgencyModel.cs | 2 +- .../Models/Parcel/BuildingEvaluationModel.cs | 55 ++++ .../Admin/Models/Parcel/BuildingModel.cs | 91 ++++++ .../Models/{ => Parcel}/CodeLookupModel.cs | 2 +- .../Models/Parcel/ParcelBuildingModel.cs | 97 ++++++ .../Models/Parcel/ParcelEvaluationModel.cs | 56 ++++ .../Areas/Admin/Models/Parcel/ParcelModel.cs | 107 +++++++ .../Models/Parcel/PartialBuildingModel.cs | 43 +++ .../Admin/Models/Parcel/PartialParcelModel.cs | 52 ++++ .../Admin/Models/{ => Role}/RoleModel.cs | 6 +- .../Admin/Models/User/AccessRequestModel.cs | 18 ++ .../Models/User/AccessRequestRoleModel.cs | 39 +++ .../Models/User/AccessRequestUserModel.cs | 73 +++++ .../Areas/Admin/Models/User/AgencyModel.cs | 65 ++++ .../api/Areas/Admin/Models/User/RoleModel.cs | 16 + .../Admin/Models/{ => User}/UserModel.cs | 5 +- .../Admin/Profiles/Parcel/AddressProfile.cs | 30 ++ .../Profiles/{ => Parcel}/AgencyProfile.cs | 5 +- .../Parcel/BuildingEvaluationProfile.cs | 28 ++ .../Admin/Profiles/Parcel/BuildingProfile.cs | 87 ++++++ .../Parcel/ParcelEvaluationProfile.cs | 28 ++ .../Admin/Profiles/Parcel}/ParcelProfile.cs | 33 +- .../Resolvers/ParcelSubAgencyResolver.cs | 14 + .../Admin/Profiles/{ => Role}/RoleProfile.cs | 4 +- .../Profiles/User/AccessRequestProfile.cs | 45 +++ .../Admin/Profiles/User/AgencyProfile.cs | 24 ++ .../Areas/Admin/Profiles/User/RoleProfile.cs | 32 ++ .../Admin/Profiles/{ => User}/UserProfile.cs | 4 +- .../Keycloak/Controllers/RoleController.cs | 2 +- .../Keycloak/Controllers/UserController.cs | 9 +- .../Keycloak/Models/{ => Role}/RoleModel.cs | 5 +- .../Models/{ => Role}/Update/BaseModel.cs | 2 +- .../Models/{ => Role}/Update/RoleModel.cs | 2 +- .../Models/User/AccessRequestModel.cs | 17 ++ .../Models/User/AccessRequestRoleModel.cs | 34 +++ .../Models/User/AccessRequestUserModel.cs | 72 +++++ .../Keycloak/Models/{ => User}/AgencyModel.cs | 5 +- .../Areas/Keycloak/Models/User/RoleModel.cs | 39 +++ .../Keycloak/Models/User/Update/BaseModel.cs | 37 +++ .../Keycloak/Models/User/Update/RoleModel.cs | 44 +++ .../{ => User}/Update/UserAgencyModel.cs | 2 +- .../Models/{ => User}/Update/UserModel.cs | 8 +- .../Models/{ => User}/Update/UserRoleModel.cs | 2 +- .../Keycloak/Models/{ => User}/UserModel.cs | 5 +- .../Resolvers/UpdateAgencyToEntityResolver.cs | 23 -- .../Resolvers/UpdateRoleToEntityResolver.cs | 23 -- .../Keycloak/Profiles/Role/BaseProfile.cs | 35 +++ .../Keycloak/Profiles/Role/RoleProfile.cs | 26 ++ .../Profiles/Role/UpdateRoleProfile.cs | 26 ++ .../Profiles/User/AccessRequestProfile.cs | 47 +++ .../Profiles/{ => User}/AgencyProfile.cs | 8 +- .../Profiles/{ => User}/BaseProfile.cs | 8 +- .../Resolvers/AgencyToAgencyResolver.cs | 4 +- .../Resolvers/AttributeMapToAgencyResolver.cs | 2 +- .../Resolvers/EntityAgencyResolver.cs | 2 +- .../Resolvers/EntityRoleResolver.cs | 2 +- .../Resolvers/KeycloakDisplayNameResolver.cs | 2 +- .../Resolvers/KeycloakGroupResolver.cs | 2 +- .../Resolvers/UpdateAgencyToEntityResolver.cs | 34 +++ .../Resolvers/UpdateRoleToEntityResolver.cs | 34 +++ .../Resolvers/UserRoleToRoleResolver.cs | 6 +- .../Profiles/{ => User}/RoleProfile.cs | 8 +- .../Profiles/User/UpdateUserProfile.cs | 69 +++++ .../Profiles/{ => User}/UserProfile.cs | 27 +- .../Tools/Controllers/ImportController.cs | 6 +- .../Tools/Helpers/ImportPropertiesHelper.cs | 10 +- .../Areas/Tools/Models/Import/AddressModel.cs | 51 ++++ .../Models/Import/BuidlingEvaluationModel.cs | 54 ++++ .../Models/Import/ParcelBuildingModel.cs | 96 ++++++ .../Models/Import/ParcelEvaluationModel.cs | 54 ++++ .../Areas/Tools/Models/Import/ParcelModel.cs | 106 +++++++ .../Models/{ => Import}/PropertyModel.cs | 2 +- .../Tools/Profiles/Import/AddressProfile.cs | 28 ++ .../Import/BuildingEvaluationProfile.cs | 27 ++ .../Profiles/Import/ParcelBuildingProfile.cs | 31 ++ .../Import/ParcelEvaluationProfile.cs | 27 ++ .../Tools/Profiles/Import/ParcelProfile.cs | 37 +++ .../Resolvers/ParcelSubAgencyResolver.cs | 14 + backend/api/Controllers/AuthController.cs | 11 +- backend/api/Controllers/HealthController.cs | 6 +- backend/api/Controllers/LookupController.cs | 12 +- backend/api/Controllers/ParcelController.cs | 23 +- backend/api/Controllers/PropertyController.cs | 4 +- backend/api/Controllers/UserController.cs | 8 +- .../Extensions/MappingExpressionExtensions.cs | 2 +- backend/api/Models/Building/AddressModel.cs | 51 ++++ .../Building/BuildingEvaluationModel.cs | 55 ++++ .../Models/{ => Building}/BuildingModel.cs | 4 +- .../PartialBuildingModel.cs} | 8 +- .../api/Models/{ => Parcel}/AddressModel.cs | 4 +- .../Models/Parcel/BuildingEvaluationModel.cs | 55 ++++ .../{Parts => Parcel}/ParcelBuildingModel.cs | 8 +- .../ParcelEvaluationModel.cs} | 14 +- .../api/Models/{ => Parcel}/ParcelModel.cs | 8 +- .../PartialParcelModel.cs} | 10 +- backend/api/Models/Property/PropertyModel.cs | 2 +- .../Models/{ => User}/AccessRequestModel.cs | 8 +- .../{Parts => User}/AccessRequestRoleModel.cs | 2 +- .../{Parts => User}/AccessRequestUserModel.cs | 3 +- backend/api/Models/{ => User}/AgencyModel.cs | 7 +- backend/api/Models/{ => User}/RoleModel.cs | 2 +- backend/api/Models/{ => User}/UserModel.cs | 4 +- backend/api/Pims.Api.csproj | 11 +- backend/api/Profiles/AgencyProfile.cs | 20 -- backend/api/Profiles/BaseProfile.cs | 4 +- .../api/Profiles/Building/AddressProfile.cs | 28 ++ .../Building/BuildingEvaluationProfile.cs | 27 ++ .../api/Profiles/Building/BuildingProfile.cs | 27 ++ backend/api/Profiles/BuildingProfile.cs | 60 ---- backend/api/Profiles/CodeProfile.cs | 12 +- .../Converters/ParcelAgencyConverter.cs | 2 +- .../Profiles/Converters/ParcelIdConverter.cs | 2 +- .../Converters/ParcelSubAgencyConverter.cs | 2 +- backend/api/Profiles/EvaluationProfile.cs | 30 -- .../Profiles/{ => Lookup}/LookupProfile.cs | 12 +- .../Profiles/{ => Parcel}/AddressProfile.cs | 13 +- .../Parcel/BuildingEvaluationProfile.cs | 32 ++ .../Profiles/Parcel/ParcelBuildingProfile.cs | 54 ++++ .../Parcel/ParcelEvaluationProfile.cs | 32 ++ backend/api/Profiles/Parcel/ParcelProfile.cs | 39 +++ .../Profiles/Parcel/PartialParcelProfile.cs | 31 ++ .../Parcel/Resolvers/ParceIdResolver.cs | 14 + .../Resolvers/ParcelSubAgencyResolver.cs | 14 + .../{ => Property}/PropertyProfile.cs | 7 +- .../api/Profiles/Resolvers/ParceIdResolver.cs | 14 - .../Resolvers/ParcelSubAgencyResolver.cs | 15 - .../{ => User}/AccessRequestProfile.cs | 25 +- backend/api/Profiles/User/AgencyProfile.cs | 20 ++ .../api/Profiles/{ => User}/RoleProfile.cs | 26 +- .../api/Profiles/{ => User}/UserProfile.cs | 8 +- backend/api/Properties/launchSettings.json | 14 +- .../core/Extensions/EnumerableExtensions.cs | 28 ++ backend/core/Properties/launchSettings.json | 27 ++ .../Partials/PimsKeycloakRoleService.cs | 7 +- .../Partials/PimsKeycloakUserService.cs | 18 +- .../Helpers/Extensions/DbContextExtensions.cs | 33 ++ .../Helpers/Extensions/EntityExtensions.cs | 12 +- backend/dal/PIMSContext.cs | 8 +- backend/dal/Properties/launchSettings.json | 27 ++ .../Services/Admin/Concrete/AddressService.cs | 1 - .../Services/Admin/Concrete/AgencyService.cs | 12 +- .../Services/Admin/Concrete/BaseService`.cs | 30 +- .../BuildingConstructionTypeService.cs | 12 +- .../Concrete/BuildingPredominateUseService.cs | 12 +- .../Admin/Concrete/BuildingService.cs | 42 +-- .../Services/Admin/Concrete/CityService.cs | 3 +- .../Services/Admin/Concrete/ParcelService.cs | 59 +--- .../Concrete/PropertyClassificationService.cs | 12 +- .../Admin/Concrete/PropertyStatusService.cs | 12 +- .../Admin/Concrete/PropertyTypeService.cs | 11 +- .../Admin/Concrete/ProvinceService.cs | 10 +- .../Services/Admin/Concrete/RoleService.cs | 10 +- .../Services/Admin/Concrete/UserService.cs | 70 +++-- backend/dal/Services/Admin/IAddressService.cs | 2 - backend/dal/Services/Admin/IAgencyService.cs | 1 - backend/dal/Services/Admin/IBaseService`.cs | 7 - .../Admin/IBuildingConstructionTypeService.cs | 1 - .../Admin/IBuildingPredominateUseService.cs | 1 - .../dal/Services/Admin/IBuildingService.cs | 4 +- backend/dal/Services/Admin/ICityService.cs | 1 - backend/dal/Services/Admin/IParcelService.cs | 4 +- .../Admin/IPropertyClassificationService.cs | 1 - .../Services/Admin/IPropertyStatusService.cs | 1 - .../Services/Admin/IPropertyTypeService.cs | 1 - .../dal/Services/Admin/IProvinceService.cs | 1 - backend/dal/Services/Admin/IRoleService.cs | 4 +- backend/dal/Services/Admin/IUserService.cs | 9 +- backend/dal/Services/BaseService.cs | 1 - .../dal/Services/Concrete/BuildingService.cs | 8 +- .../dal/Services/Concrete/LookupService.cs | 18 +- .../dal/Services/Concrete/ParcelService.cs | 8 +- backend/dal/Services/Concrete/UserService.cs | 2 +- backend/dal/Services/IBuildingService.cs | 6 +- backend/dal/Services/ILookupService.cs | 18 +- backend/dal/Services/IParcelService.cs | 6 +- .../entities/Comparers/AgencyIdComparer.cs | 21 ++ backend/entities/Comparers/RoleIdComparer.cs | 21 ++ .../Comparers/UserAgencyAgencyIdComparer.cs | 21 ++ .../Comparers/UserRoleRoleIdComparer.cs | 21 ++ backend/entities/ParcelEvaluation.cs | 1 - backend/entities/Pims.Dal.Entities.csproj | 2 +- .../Controllers/Admin/ParcelControllerTest.cs | 282 +++++++++++++++++- .../Controllers/Admin/UserControllerTest.cs | 52 ++-- .../Keycloak/RoleControllerTest.cs | 6 +- .../Keycloak/UserControllerTest.cs | 80 +++-- ...trollerTest.cs => LookupControllerTest.cs} | 31 +- .../test/Controllers/ParcelControllerTest.cs | 178 +++++++---- .../Controllers/PropertyControllerTest.cs | 69 +++-- .../{ => Tools}/ImportControllerTest.cs | 10 +- .../test/Controllers/UserControllerTest.cs | 6 +- backend/test/Helpers/EntityHelper.cs | 48 ++- .../test/Routes/Admin/ParcelControllerTest.cs | 39 ++- backend/test/Routes/ParcelControllerTest.cs | 76 ++++- backend/test/Routes/PropertyControllerTest.cs | 4 + 200 files changed, 3968 insertions(+), 1050 deletions(-) create mode 100644 backend/api/Areas/Admin/Models/Parcel/AddressModel.cs rename backend/api/Areas/Admin/Models/{ => Parcel}/AgencyModel.cs (97%) create mode 100644 backend/api/Areas/Admin/Models/Parcel/BuildingEvaluationModel.cs create mode 100644 backend/api/Areas/Admin/Models/Parcel/BuildingModel.cs rename backend/api/Areas/Admin/Models/{ => Parcel}/CodeLookupModel.cs (96%) create mode 100644 backend/api/Areas/Admin/Models/Parcel/ParcelBuildingModel.cs create mode 100644 backend/api/Areas/Admin/Models/Parcel/ParcelEvaluationModel.cs create mode 100644 backend/api/Areas/Admin/Models/Parcel/ParcelModel.cs create mode 100644 backend/api/Areas/Admin/Models/Parcel/PartialBuildingModel.cs create mode 100644 backend/api/Areas/Admin/Models/Parcel/PartialParcelModel.cs rename backend/api/Areas/Admin/Models/{ => Role}/RoleModel.cs (76%) create mode 100644 backend/api/Areas/Admin/Models/User/AccessRequestModel.cs create mode 100644 backend/api/Areas/Admin/Models/User/AccessRequestRoleModel.cs create mode 100644 backend/api/Areas/Admin/Models/User/AccessRequestUserModel.cs create mode 100644 backend/api/Areas/Admin/Models/User/AgencyModel.cs create mode 100644 backend/api/Areas/Admin/Models/User/RoleModel.cs rename backend/api/Areas/Admin/Models/{ => User}/UserModel.cs (96%) create mode 100644 backend/api/Areas/Admin/Profiles/Parcel/AddressProfile.cs rename backend/api/Areas/Admin/Profiles/{ => Parcel}/AgencyProfile.cs (84%) create mode 100644 backend/api/Areas/Admin/Profiles/Parcel/BuildingEvaluationProfile.cs create mode 100644 backend/api/Areas/Admin/Profiles/Parcel/BuildingProfile.cs create mode 100644 backend/api/Areas/Admin/Profiles/Parcel/ParcelEvaluationProfile.cs rename backend/api/{Profiles => Areas/Admin/Profiles/Parcel}/ParcelProfile.cs (67%) create mode 100644 backend/api/Areas/Admin/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs rename backend/api/Areas/Admin/Profiles/{ => Role}/RoleProfile.cs (92%) create mode 100644 backend/api/Areas/Admin/Profiles/User/AccessRequestProfile.cs create mode 100644 backend/api/Areas/Admin/Profiles/User/AgencyProfile.cs create mode 100644 backend/api/Areas/Admin/Profiles/User/RoleProfile.cs rename backend/api/Areas/Admin/Profiles/{ => User}/UserProfile.cs (88%) rename backend/api/Areas/Keycloak/Models/{ => Role}/RoleModel.cs (90%) rename backend/api/Areas/Keycloak/Models/{ => Role}/Update/BaseModel.cs (94%) rename backend/api/Areas/Keycloak/Models/{ => Role}/Update/RoleModel.cs (95%) create mode 100644 backend/api/Areas/Keycloak/Models/User/AccessRequestModel.cs create mode 100644 backend/api/Areas/Keycloak/Models/User/AccessRequestRoleModel.cs create mode 100644 backend/api/Areas/Keycloak/Models/User/AccessRequestUserModel.cs rename backend/api/Areas/Keycloak/Models/{ => User}/AgencyModel.cs (92%) create mode 100644 backend/api/Areas/Keycloak/Models/User/RoleModel.cs create mode 100644 backend/api/Areas/Keycloak/Models/User/Update/BaseModel.cs create mode 100644 backend/api/Areas/Keycloak/Models/User/Update/RoleModel.cs rename backend/api/Areas/Keycloak/Models/{ => User}/Update/UserAgencyModel.cs (95%) rename backend/api/Areas/Keycloak/Models/{ => User}/Update/UserModel.cs (93%) rename backend/api/Areas/Keycloak/Models/{ => User}/Update/UserRoleModel.cs (95%) rename backend/api/Areas/Keycloak/Models/{ => User}/UserModel.cs (96%) delete mode 100644 backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateAgencyToEntityResolver.cs delete mode 100644 backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateRoleToEntityResolver.cs create mode 100644 backend/api/Areas/Keycloak/Profiles/Role/BaseProfile.cs create mode 100644 backend/api/Areas/Keycloak/Profiles/Role/RoleProfile.cs create mode 100644 backend/api/Areas/Keycloak/Profiles/Role/UpdateRoleProfile.cs create mode 100644 backend/api/Areas/Keycloak/Profiles/User/AccessRequestProfile.cs rename backend/api/Areas/Keycloak/Profiles/{ => User}/AgencyProfile.cs (76%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/BaseProfile.cs (79%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/Resolvers/AgencyToAgencyResolver.cs (91%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/Resolvers/AttributeMapToAgencyResolver.cs (94%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/Resolvers/EntityAgencyResolver.cs (92%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/Resolvers/EntityRoleResolver.cs (90%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/Resolvers/KeycloakDisplayNameResolver.cs (92%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/Resolvers/KeycloakGroupResolver.cs (90%) create mode 100644 backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateAgencyToEntityResolver.cs create mode 100644 backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateRoleToEntityResolver.cs rename backend/api/Areas/Keycloak/Profiles/{ => User}/Resolvers/UserRoleToRoleResolver.cs (86%) rename backend/api/Areas/Keycloak/Profiles/{ => User}/RoleProfile.cs (80%) create mode 100644 backend/api/Areas/Keycloak/Profiles/User/UpdateUserProfile.cs rename backend/api/Areas/Keycloak/Profiles/{ => User}/UserProfile.cs (68%) create mode 100644 backend/api/Areas/Tools/Models/Import/AddressModel.cs create mode 100644 backend/api/Areas/Tools/Models/Import/BuidlingEvaluationModel.cs create mode 100644 backend/api/Areas/Tools/Models/Import/ParcelBuildingModel.cs create mode 100644 backend/api/Areas/Tools/Models/Import/ParcelEvaluationModel.cs create mode 100644 backend/api/Areas/Tools/Models/Import/ParcelModel.cs rename backend/api/Areas/Tools/Models/{ => Import}/PropertyModel.cs (96%) create mode 100644 backend/api/Areas/Tools/Profiles/Import/AddressProfile.cs create mode 100644 backend/api/Areas/Tools/Profiles/Import/BuildingEvaluationProfile.cs create mode 100644 backend/api/Areas/Tools/Profiles/Import/ParcelBuildingProfile.cs create mode 100644 backend/api/Areas/Tools/Profiles/Import/ParcelEvaluationProfile.cs create mode 100644 backend/api/Areas/Tools/Profiles/Import/ParcelProfile.cs create mode 100644 backend/api/Areas/Tools/Profiles/Import/Resolvers/ParcelSubAgencyResolver.cs rename backend/api/{Profiles => Helpers}/Extensions/MappingExpressionExtensions.cs (95%) create mode 100644 backend/api/Models/Building/AddressModel.cs create mode 100644 backend/api/Models/Building/BuildingEvaluationModel.cs rename backend/api/Models/{ => Building}/BuildingModel.cs (94%) rename backend/api/Models/{Parts/BuildingModel.cs => Building/PartialBuildingModel.cs} (77%) rename backend/api/Models/{ => Parcel}/AddressModel.cs (94%) create mode 100644 backend/api/Models/Parcel/BuildingEvaluationModel.cs rename backend/api/Models/{Parts => Parcel}/ParcelBuildingModel.cs (92%) rename backend/api/Models/{EvaluationModel.cs => Parcel/ParcelEvaluationModel.cs} (72%) rename backend/api/Models/{ => Parcel}/ParcelModel.cs (90%) rename backend/api/Models/{Parts/ParcelModel.cs => Parcel/PartialParcelModel.cs} (80%) rename backend/api/Models/{ => User}/AccessRequestModel.cs (73%) rename backend/api/Models/{Parts => User}/AccessRequestRoleModel.cs (97%) rename backend/api/Models/{Parts => User}/AccessRequestUserModel.cs (96%) rename backend/api/Models/{ => User}/AgencyModel.cs (86%) rename backend/api/Models/{ => User}/RoleModel.cs (96%) rename backend/api/Models/{ => User}/UserModel.cs (94%) delete mode 100644 backend/api/Profiles/AgencyProfile.cs create mode 100644 backend/api/Profiles/Building/AddressProfile.cs create mode 100644 backend/api/Profiles/Building/BuildingEvaluationProfile.cs create mode 100644 backend/api/Profiles/Building/BuildingProfile.cs delete mode 100644 backend/api/Profiles/BuildingProfile.cs delete mode 100644 backend/api/Profiles/EvaluationProfile.cs rename backend/api/Profiles/{ => Lookup}/LookupProfile.cs (61%) rename backend/api/Profiles/{ => Parcel}/AddressProfile.cs (69%) create mode 100644 backend/api/Profiles/Parcel/BuildingEvaluationProfile.cs create mode 100644 backend/api/Profiles/Parcel/ParcelBuildingProfile.cs create mode 100644 backend/api/Profiles/Parcel/ParcelEvaluationProfile.cs create mode 100644 backend/api/Profiles/Parcel/ParcelProfile.cs create mode 100644 backend/api/Profiles/Parcel/PartialParcelProfile.cs create mode 100644 backend/api/Profiles/Parcel/Resolvers/ParceIdResolver.cs create mode 100644 backend/api/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs rename backend/api/Profiles/{ => Property}/PropertyProfile.cs (74%) delete mode 100644 backend/api/Profiles/Resolvers/ParceIdResolver.cs delete mode 100644 backend/api/Profiles/Resolvers/ParcelSubAgencyResolver.cs rename backend/api/Profiles/{ => User}/AccessRequestProfile.cs (64%) create mode 100644 backend/api/Profiles/User/AgencyProfile.cs rename backend/api/Profiles/{ => User}/RoleProfile.cs (62%) rename backend/api/Profiles/{ => User}/UserProfile.cs (74%) create mode 100644 backend/core/Properties/launchSettings.json create mode 100644 backend/dal/Helpers/Extensions/DbContextExtensions.cs create mode 100644 backend/dal/Properties/launchSettings.json create mode 100644 backend/entities/Comparers/AgencyIdComparer.cs create mode 100644 backend/entities/Comparers/RoleIdComparer.cs create mode 100644 backend/entities/Comparers/UserAgencyAgencyIdComparer.cs create mode 100644 backend/entities/Comparers/UserRoleRoleIdComparer.cs rename backend/test/Controllers/{CodeLookupControllerTest.cs => LookupControllerTest.cs} (85%) rename backend/test/Controllers/{ => Tools}/ImportControllerTest.cs (91%) diff --git a/backend/.gitignore b/backend/.gitignore index e9bd63c540..972053e895 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -27,20 +27,20 @@ nupkg/ *.sln.docstates # Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -build/ -bld/ -[Bb]in/ -[Oo]bj/ -[Oo]ut/ -msbuild.log -msbuild.err -msbuild.wrn +**/[Dd]ebug/ +**/[Dd]ebugPublic/ +**/[Rr]elease/ +**/[Rr]eleases/ +**/x64/ +**/x86/ +**/build/ +**/bld/ +**/[Bb]in/ +**/[Oo]bj/ +**/[Oo]ut/ +**/msbuild.log +**/msbuild.err +**/msbuild.wrn # Visual Studio 2015 .vs/ @@ -50,3 +50,5 @@ msbuild.wrn # NET Core Healthchecks UI healthchecksdb +healthchecksdb-shm +healthchecksdb-wal diff --git a/backend/Pims.sln b/backend/Pims.sln index 652dde54a7..7e999d0e8c 100644 --- a/backend/Pims.sln +++ b/backend/Pims.sln @@ -9,13 +9,43 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Api.Test", "test\Pims. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Dal", "dal\Pims.Dal.csproj", "{6DFFF5E1-1B87-403B-99D0-A9E03D8A8EB3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pims.Dal.Entities", "entities\Pims.Dal.Entities.csproj", "{1C724CD5-CD24-46CD-835A-A83F673F97B5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Dal.Entities", "entities\Pims.Dal.Entities.csproj", "{1C724CD5-CD24-46CD-835A-A83F673F97B5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pims.Keycloak", "keycloak\Pims.Keycloak.csproj", "{970903E9-BC53-436F-BA77-C62349546425}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Keycloak", "keycloak\Pims.Keycloak.csproj", "{970903E9-BC53-436F-BA77-C62349546425}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pims.Core", "core\Pims.Core.csproj", "{768D5D0A-A3B7-4599-B5CF-E32FC407AD9A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Core", "core\Pims.Core.csproj", "{768D5D0A-A3B7-4599-B5CF-E32FC407AD9A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pims.Dal.Keycloak", "dal.keycloak\Pims.Dal.Keycloak.csproj", "{5697DD19-62CC-4377-ABA8-1E192376F4F6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Dal.Keycloak", "dal.keycloak\Pims.Dal.Keycloak.csproj", "{5697DD19-62CC-4377-ABA8-1E192376F4F6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E5C0D5F1-5433-42D8-A9FB-A887A42F5C06}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libraries", "libraries", "{1CDE14FF-8E56-45D0-BA1D-ED4C49BDE0B2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{6F7A19B7-123A-44AF-9737-BBBD8CBD0E55}" + ProjectSection(SolutionItems) = preProject + docs\API.md = docs\API.md + docs\DAL.md = docs\DAL.md + docs\DATABASE.md = docs\DATABASE.md + docs\SETUP.md = docs\SETUP.md + docs\TOOLS.md = docs\TOOLS.md + docs\VERSIONING.md = docs\VERSIONING.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{BBBAAD5E-6C48-4B44-A35D-70571BC90C41}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .editorconfig = .editorconfig + .gitignore = .gitignore + Dockerfile = Dockerfile + entrypoint.sh = entrypoint.sh + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{8610ED92-BF22-4A27-963A-F27C961EE333}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "import", "import", "{C3863C8F-4B5B-4990-AE5D-D6E63D5214DE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Tools.Import", "tools\import\Pims.Tools.Import.csproj", "{13BF0B7D-1BA6-4B62-BA01-6150059B81EC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -111,10 +141,32 @@ Global {5697DD19-62CC-4377-ABA8-1E192376F4F6}.Release|x64.Build.0 = Release|Any CPU {5697DD19-62CC-4377-ABA8-1E192376F4F6}.Release|x86.ActiveCfg = Release|Any CPU {5697DD19-62CC-4377-ABA8-1E192376F4F6}.Release|x86.Build.0 = Release|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Debug|x64.ActiveCfg = Debug|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Debug|x64.Build.0 = Debug|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Debug|x86.ActiveCfg = Debug|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Debug|x86.Build.0 = Debug|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Release|Any CPU.Build.0 = Release|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Release|x64.ActiveCfg = Release|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Release|x64.Build.0 = Release|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Release|x86.ActiveCfg = Release|Any CPU + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {19A6829C-20D8-4F66-9889-55C553ACE2B2} = {E5C0D5F1-5433-42D8-A9FB-A887A42F5C06} + {6DFFF5E1-1B87-403B-99D0-A9E03D8A8EB3} = {1CDE14FF-8E56-45D0-BA1D-ED4C49BDE0B2} + {1C724CD5-CD24-46CD-835A-A83F673F97B5} = {1CDE14FF-8E56-45D0-BA1D-ED4C49BDE0B2} + {970903E9-BC53-436F-BA77-C62349546425} = {1CDE14FF-8E56-45D0-BA1D-ED4C49BDE0B2} + {768D5D0A-A3B7-4599-B5CF-E32FC407AD9A} = {1CDE14FF-8E56-45D0-BA1D-ED4C49BDE0B2} + {5697DD19-62CC-4377-ABA8-1E192376F4F6} = {1CDE14FF-8E56-45D0-BA1D-ED4C49BDE0B2} + {C3863C8F-4B5B-4990-AE5D-D6E63D5214DE} = {8610ED92-BF22-4A27-963A-F27C961EE333} + {13BF0B7D-1BA6-4B62-BA01-6150059B81EC} = {C3863C8F-4B5B-4990-AE5D-D6E63D5214DE} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3433C5DD-DC49-4A96-A1AE-90C1A1EBA87C} EndGlobalSection diff --git a/backend/api/Areas/Admin/Controllers/ParcelController.cs b/backend/api/Areas/Admin/Controllers/ParcelController.cs index 88d64b62ff..0342ebb89f 100644 --- a/backend/api/Areas/Admin/Controllers/ParcelController.cs +++ b/backend/api/Areas/Admin/Controllers/ParcelController.cs @@ -1,18 +1,19 @@ -using System; -using System.Linq; using AutoMapper; +using BModel = Pims.Api.Models; +using Entity = Pims.Dal.Entities; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Pims.Dal.Helpers.Extensions; -using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Models; +using Model = Pims.Api.Areas.Admin.Models.Parcel; +using Pims.Api.Helpers.Exceptions; using Pims.Api.Policies; +using Pims.Core.Helpers; +using Pims.Dal.Helpers.Extensions; using Pims.Dal.Security; using Pims.Dal.Services.Admin; -using Pims.Core.Helpers; -using System.Collections.Generic; -using Pims.Api.Helpers.Exceptions; using Swashbuckle.AspNetCore.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; namespace Pims.Api.Areas.Admin.Controllers { @@ -59,7 +60,7 @@ public ParcelController(ILogger logger, IPimsAdminService pims [HttpGet] [HasPermission(Permissions.PropertyView)] [Produces("application/json")] - [ProducesResponseType(typeof(Entity.Models.Paged), 200)] + [ProducesResponseType(typeof(Entity.Models.Paged), 200)] [SwaggerOperation(Tags = new[] { "admin-parcel" })] public IActionResult GetParcels(int page = 1, int quantity = 10, string sort = null) // TODO: sort and filter. { @@ -67,9 +68,9 @@ public IActionResult GetParcels(int page = 1, int quantity = 10, string sort = n if (quantity < 1) quantity = 1; if (quantity > 50) quantity = 50; - var result = _pimsAdminService.Parcel.GetNoTracking(page, quantity, sort); - var entities = _mapper.Map(result.Items); - var paged = new Entity.Models.Paged(entities, page, quantity, result.Total); + var result = _pimsAdminService.Parcel.Get(page, quantity, sort); + var entities = _mapper.Map(result.Items); + var paged = new Entity.Models.Paged(entities, page, quantity, result.Total); return new JsonResult(paged); } @@ -83,11 +84,11 @@ public IActionResult GetParcels(int page = 1, int quantity = 10, string sort = n [HasPermission(Permissions.PropertyView)] [Produces("application/json")] [ProducesResponseType(typeof(Model.ParcelModel), 200)] - [ProducesResponseType(typeof(Model.ErrorResponseModel), 400)] + [ProducesResponseType(typeof(BModel.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "admin-parcel" })] public IActionResult GetParcel(int id) { - var entity = _pimsAdminService.Parcel.GetNoTracking(id); + var entity = _pimsAdminService.Parcel.Get(id); var parcel = _mapper.Map(entity); @@ -107,7 +108,7 @@ public IActionResult GetParcel(int id) [SwaggerOperation(Tags = new[] { "admin-parcel" })] public IActionResult GetParcelByPid(int id) { - var entity = ExceptionHelper.HandleKeyNotFound(() => _pimsAdminService.Parcel.GetByPidNoTracking(id)); + var entity = ExceptionHelper.HandleKeyNotFound(() => _pimsAdminService.Parcel.GetByPid(id)); if (entity == null) return NoContent(); var parcel = _mapper.Map(entity); @@ -125,13 +126,13 @@ public IActionResult GetParcelByPid(int id) [Produces("application/json")] [ProducesResponseType(typeof(Model.ParcelModel), 200)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(Model.ErrorResponseModel), 400)] + [ProducesResponseType(typeof(BModel.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "admin-parcel" })] public IActionResult GetParcelByPid(string pid) { if (!int.TryParse(pid.Replace("-", ""), out int id)) throw new BadRequestException("PID is invalid"); - var entity = ExceptionHelper.HandleKeyNotFound(() => _pimsAdminService.Parcel.GetByPidNoTracking(id)); + var entity = ExceptionHelper.HandleKeyNotFound(() => _pimsAdminService.Parcel.GetByPid(id)); if (entity == null) return NoContent(); var parcel = _mapper.Map(entity); @@ -148,7 +149,7 @@ public IActionResult GetParcelByPid(string pid) [HasPermission(Permissions.PropertyAdd)] [Produces("application/json")] [ProducesResponseType(typeof(Model.ParcelModel), 201)] - [ProducesResponseType(typeof(Model.ErrorResponseModel), 400)] + [ProducesResponseType(typeof(BModel.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "admin-parcel" })] public IActionResult AddParcel([FromBody] Model.ParcelModel model) { @@ -157,7 +158,7 @@ public IActionResult AddParcel([FromBody] Model.ParcelModel model) _pimsAdminService.Parcel.Add(entity); var parcel = _mapper.Map(entity); - return new CreatedAtActionResult(nameof(GetParcel), nameof(ParcelController), new { id = parcel.Id }, parcel); + return CreatedAtAction(nameof(GetParcel), new { id = parcel.Id }, parcel); } /// @@ -169,7 +170,7 @@ public IActionResult AddParcel([FromBody] Model.ParcelModel model) [HasPermission(Permissions.PropertyAdd)] [Produces("application/json")] [ProducesResponseType(typeof(IEnumerable), 200)] - [ProducesResponseType(typeof(Model.ErrorResponseModel), 400)] + [ProducesResponseType(typeof(BModel.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "admin-parcel" })] public IActionResult AddParcels([FromBody] Model.ParcelModel[] models) { @@ -182,6 +183,7 @@ public IActionResult AddParcels([FromBody] Model.ParcelModel[] models) /// /// PUT - Update the parcel in the datasource. + /// This function will not delete evaluations, only add or update. /// /// /// The parcel model. @@ -190,17 +192,12 @@ public IActionResult AddParcels([FromBody] Model.ParcelModel[] models) [HasPermission(Permissions.PropertyEdit)] [Produces("application/json")] [ProducesResponseType(typeof(Model.ParcelModel), 200)] - [ProducesResponseType(typeof(Model.ErrorResponseModel), 400)] + [ProducesResponseType(typeof(BModel.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "admin-parcel" })] public IActionResult UpdateParcel(int id, [FromBody] Model.ParcelModel model) { - var entity = _pimsAdminService.Parcel.Get(model.Id); - - if (entity == null) return BadRequest("Item does not exist"); var userId = this.User.GetUserId(); - var address = entity.Address?.ToString(); - _mapper.Map(model, entity); foreach (var evaluation in model.Evaluations) @@ -209,40 +206,25 @@ public IActionResult UpdateParcel(int id, [FromBody] Model.ParcelModel model) var p_eval = entity.Evaluations.FirstOrDefault(e => e.FiscalYear == evaluation.FiscalYear); if (p_eval == null) { - entity.Evaluations.Add(new Entity.ParcelEvaluation(evaluation.FiscalYear, entity) // TODO: Move this logic to AutoMapper. - { - EstimatedValue = evaluation.EstimatedValue, - AssessedValue = evaluation.AssessedValue, - NetBookValue = evaluation.NetBookValue, - CreatedById = userId - }); + entity.Evaluations.Add(new Entity.ParcelEvaluation(evaluation.FiscalYear, entity, evaluation.EstimatedValue, evaluation.AppraisedValue, evaluation.AssessedValue, evaluation.NetBookValue)); } else { - p_eval.EstimatedValue = evaluation.EstimatedValue; - p_eval.AssessedValue = evaluation.AssessedValue; - p_eval.NetBookValue = evaluation.NetBookValue; - p_eval.UpdatedById = userId; // TODO: Move to DAL. - p_eval.UpdatedOn = DateTime.UtcNow; + _mapper.Map(evaluation, p_eval); } } foreach (var building in model.Buildings) { - if (building.Id == 0) + + var b_entity = entity.Buildings.FirstOrDefault(b => b.Id == building.Id); + if (b_entity == null) { // Add a new building to the parcel. - var b_entity = _mapper.Map(building); - b_entity.CreatedById = userId; + b_entity = _mapper.Map(building); foreach (var evaluation in building.Evaluations) { - b_entity.Evaluations.Add(new Entity.BuildingEvaluation(evaluation.FiscalYear, b_entity) // TODO: Move this logic to AutoMapper. - { - EstimatedValue = evaluation.EstimatedValue, - AssessedValue = evaluation.AssessedValue, - NetBookValue = evaluation.NetBookValue, - CreatedById = userId - }); + b_entity.Evaluations.Add(new Entity.BuildingEvaluation(evaluation.FiscalYear, b_entity, evaluation.EstimatedValue, evaluation.AppraisedValue, evaluation.AssessedValue, evaluation.NetBookValue)); } entity.Buildings.Add(b_entity); @@ -250,65 +232,24 @@ public IActionResult UpdateParcel(int id, [FromBody] Model.ParcelModel model) else { // Update existing building on the parcel. - var b_entity = entity.Buildings.FirstOrDefault(b => b.Id == building.Id); + _mapper.Map(building, b_entity); - // We will ignore building Ids that don't match. - if (b_entity != null) + foreach (var evaluation in building.Evaluations) { - var b_address = b_entity.Address?.ToString(); - _mapper.Map(building, b_entity); - b_entity.UpdatedById = userId; - b_entity.UpdatedOn = DateTime.UtcNow; - - foreach (var evaluation in building.Evaluations) + // Update evaluation. + var b_eval = b_entity.Evaluations.FirstOrDefault(e => e.FiscalYear == evaluation.FiscalYear); + if (b_eval == null) { - // Update evaluation. - var b_eval = b_entity.Evaluations.FirstOrDefault(e => e.FiscalYear == evaluation.FiscalYear); - - if (b_eval == null) - { - b_entity.Evaluations.Add(new Entity.BuildingEvaluation(evaluation.FiscalYear, b_entity) // TODO: Move this logic to AutoMapper. - { - EstimatedValue = evaluation.EstimatedValue, - AssessedValue = evaluation.AssessedValue, - NetBookValue = evaluation.NetBookValue, - CreatedById = userId - }); - } - else - { - b_eval.EstimatedValue = evaluation.EstimatedValue; - b_eval.AssessedValue = evaluation.AssessedValue; - b_eval.NetBookValue = evaluation.NetBookValue; - b_eval.UpdatedById = userId; // TODO: Move to DAL. - b_eval.UpdatedOn = DateTime.UtcNow; - } + b_entity.Evaluations.Add(new Entity.BuildingEvaluation(evaluation.FiscalYear, b_entity, evaluation.EstimatedValue, evaluation.AppraisedValue, evaluation.AssessedValue, evaluation.NetBookValue)); } - - _pimsAdminService.Building.UpdateOne(b_entity); - - // Check if the address was updated. - if (b_address != b_entity.Address.ToString()) + else { - b_entity.Address.UpdatedById = userId; - b_entity.Address.UpdatedOn = DateTime.UtcNow; - _pimsAdminService.Address.UpdateOne(b_entity.Address); + _mapper.Map(evaluation, b_eval); } } - else - { - _logger.LogDebug($"Invalid - Attempting to update parcel with invalid building - PID:{entity.PID}, BuildingId:{building.Id}"); - } } } - // Check if the address was updated. - if (address != entity.Address.ToString()) - { - entity.Address.UpdatedById = userId; - entity.Address.UpdatedOn = DateTime.UtcNow; - _pimsAdminService.Address.UpdateOne(entity.Address); - } _pimsAdminService.Parcel.Update(entity); var parcel = _mapper.Map(entity); @@ -325,9 +266,9 @@ public IActionResult UpdateParcel(int id, [FromBody] Model.ParcelModel model) [HasPermission(Permissions.PropertyAdd)] [Produces("application/json")] [ProducesResponseType(typeof(Model.ParcelModel), 200)] - [ProducesResponseType(typeof(Model.ErrorResponseModel), 400)] + [ProducesResponseType(typeof(BModel.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "admin-parcel" })] - public IActionResult DeleteParcel(Guid id, [FromBody] Model.ParcelModel model) + public IActionResult DeleteParcel(int id, [FromBody] Model.ParcelModel model) { var parcel = _mapper.Map(model); _pimsAdminService.Parcel.Remove(parcel); diff --git a/backend/api/Areas/Admin/Controllers/RoleController.cs b/backend/api/Areas/Admin/Controllers/RoleController.cs index 2507ea945f..c5173a3bbd 100644 --- a/backend/api/Areas/Admin/Controllers/RoleController.cs +++ b/backend/api/Areas/Admin/Controllers/RoleController.cs @@ -2,7 +2,7 @@ using Entity = Pims.Dal.Entities; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Model = Pims.Api.Areas.Admin.Models; +using Model = Pims.Api.Areas.Admin.Models.Role; using Pims.Api.Policies; using Pims.Dal.Entities; using Pims.Dal.Entities.Models; @@ -63,7 +63,7 @@ public IActionResult GetRoles(int page = 1, int quantity = 10) // TODO: sort and if (quantity < 1) quantity = 1; if (quantity > 50) quantity = 50; - var result = _pimsAdminService.Role.GetNoTracking(page, quantity); + var result = _pimsAdminService.Role.Get(page, quantity); var roles = _mapper.Map(result.Items); var paged = new Paged(roles, page, quantity, result.Total); // TODO: Better way to go from one Paged type to another. return new JsonResult(paged); @@ -81,7 +81,7 @@ public IActionResult GetRoles(int page = 1, int quantity = 10) // TODO: sort and [SwaggerOperation(Tags = new[] { "admin-role" })] public IActionResult GetRole(Guid id) { - var entity = _pimsAdminService.Role.GetNoTracking(id); + var entity = _pimsAdminService.Role.Get(id); if (entity == null) return NoContent(); @@ -105,7 +105,7 @@ public IActionResult AddRole([FromBody] Model.RoleModel model) _pimsAdminService.Role.Add(entity); var role = _mapper.Map(entity); - return new CreatedAtActionResult(nameof(GetRole), nameof(RoleController), new { id = role.Id }, role); + return CreatedAtAction(nameof(GetRole), new { id = role.Id }, role); } /// diff --git a/backend/api/Areas/Admin/Controllers/UserController.cs b/backend/api/Areas/Admin/Controllers/UserController.cs index 6f43acfe0b..2e8ee476dd 100644 --- a/backend/api/Areas/Admin/Controllers/UserController.cs +++ b/backend/api/Areas/Admin/Controllers/UserController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Model = Pims.Api.Areas.Admin.Models; +using Model = Pims.Api.Areas.Admin.Models.User; using Pims.Api.Policies; using Pims.Dal.Helpers.Extensions; using Pims.Dal.Security; @@ -76,7 +76,7 @@ public IActionResult GetUsers() [SwaggerOperation(Tags = new[] { "admin-user" })] public IActionResult GetUsers(EModel.UserFilter filter) { - var results = _pimsAdminService.User.GetNoTracking(filter); + var results = _pimsAdminService.User.Get(filter); var users = _mapper.Map(results.Items); var paged = new EModel.Paged(users, filter.Page, filter.Quantity, results.Total); return new JsonResult(paged); @@ -115,7 +115,7 @@ public IActionResult GetMyUsers(EModel.UserFilter filter) [SwaggerOperation(Tags = new[] { "admin-user" })] public IActionResult GetUser(Guid id) { - var entity = _pimsAdminService.User.GetNoTracking(id); + var entity = _pimsAdminService.User.Get(id); if (entity == null) return NoContent(); @@ -140,7 +140,7 @@ public IActionResult AddUser([FromBody] Model.UserModel model) var user = _mapper.Map(addedEntity); - return new CreatedAtActionResult(nameof(GetUser), nameof(UserController), new { id = user.Id }, user); + return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user); } /// @@ -193,7 +193,7 @@ public IActionResult DeleteUser(Guid id, [FromBody] Model.UserModel model) /// [HttpGet("access/requests")] [Produces("application/json")] - [ProducesResponseType(typeof(EModel.Paged), 200)] + [ProducesResponseType(typeof(EModel.Paged), 200)] [ProducesResponseType(typeof(Api.Models.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "admin-user" })] public IActionResult GetAccessRequests(int page = 1, int quantity = 10, string sort = null, bool? isGranted = null) @@ -201,9 +201,9 @@ public IActionResult GetAccessRequests(int page = 1, int quantity = 10, string s if (page < 1) page = 1; if (quantity < 1) quantity = 1; if (quantity > 20) quantity = 20; - EModel.Paged result = _pimsAdminService.User.GetAccessRequestsNoTracking(page, quantity, sort, isGranted); - var entities = _mapper.Map(result.Items); - var paged = new EModel.Paged(entities, page, quantity, result.Total); + EModel.Paged result = _pimsAdminService.User.GetAccessRequests(page, quantity, sort, isGranted); + var models = _mapper.Map(result.Items); + var paged = new EModel.Paged(models, page, quantity, result.Total); return new JsonResult(paged); } #endregion diff --git a/backend/api/Areas/Admin/Models/Parcel/AddressModel.cs b/backend/api/Areas/Admin/Models/Parcel/AddressModel.cs new file mode 100644 index 0000000000..3adde08c10 --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/AddressModel.cs @@ -0,0 +1,53 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class AddressModel : Model.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public string Line1 { get; set; } + + public string Line2 { get; set; } + + public int CityId { get; set; } + + public string City { get; set; } + + public string ProvinceId { get; set; } + + public string Province { get; set; } + + public string Postal { get; set; } + #endregion + + #region Methods + + public override bool Equals(object obj) + { + return Equals(obj as AddressModel); + } + + public bool Equals([AllowNull] AddressModel other) + { + return other != null && + Id == other.Id && + Line1 == other.Line1 && + Line2 == other.Line2 && + CityId == other.CityId && + City == other.City && + ProvinceId == other.ProvinceId && + Province == other.Province && + Postal == other.Postal; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Line1, Line2, CityId, City, ProvinceId, Province, Postal); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/AgencyModel.cs b/backend/api/Areas/Admin/Models/Parcel/AgencyModel.cs similarity index 97% rename from backend/api/Areas/Admin/Models/AgencyModel.cs rename to backend/api/Areas/Admin/Models/Parcel/AgencyModel.cs index 1a9b13abef..ee4cc21bf9 100644 --- a/backend/api/Areas/Admin/Models/AgencyModel.cs +++ b/backend/api/Areas/Admin/Models/Parcel/AgencyModel.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Pims.Api.Models; -namespace Pims.Api.Areas.Admin.Models +namespace Pims.Api.Areas.Admin.Models.Parcel { /// /// AgencyModel class, provides a model that represents the agency. diff --git a/backend/api/Areas/Admin/Models/Parcel/BuildingEvaluationModel.cs b/backend/api/Areas/Admin/Models/Parcel/BuildingEvaluationModel.cs new file mode 100644 index 0000000000..fa6cc284ea --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/BuildingEvaluationModel.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class BuildingEvaluationModel : Model.BaseModel, IEquatable + { + #region Properties + public int BuildingId { get; set; } + + public int FiscalYear { get; set; } + + public float EstimatedValue { get; set; } + + public float AppraisedValue { get; set; } + + public float AssessedValue { get; set; } + + public float NetBookValue { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as BuildingEvaluationModel); + } + + public bool Equals([AllowNull] BuildingEvaluationModel other) + { + return other != null && + base.Equals(other) && + BuildingId == other.BuildingId && + FiscalYear == other.FiscalYear && + EstimatedValue == other.EstimatedValue && + AppraisedValue == other.AppraisedValue && + AssessedValue == other.AssessedValue && + NetBookValue == other.NetBookValue; + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(BuildingId); + hash.Add(FiscalYear); + hash.Add(EstimatedValue); + hash.Add(AppraisedValue); + hash.Add(AssessedValue); + hash.Add(NetBookValue); + return hash.ToHashCode(); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/Parcel/BuildingModel.cs b/backend/api/Areas/Admin/Models/Parcel/BuildingModel.cs new file mode 100644 index 0000000000..1750ea5e5a --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/BuildingModel.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class BuildingModel : Model.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public int ParcelId { get; set; } + + public string LocalId { get; set; } + + public string Description { get; set; } + + public AddressModel Address { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public int BuildingConstructionTypeId { get; set; } + + public string BuildingConstructionType { get; set; } + + public int BuildingFloorCount { get; set; } + + public int BuildingPredominateUseId { get; set; } + + public string BuildingPredominateUse { get; set; } + + public string BuildingTenancy { get; set; } + + public float RentableArea { get; set; } + + public IEnumerable Evaluations { get; set; } = new List(); + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as BuildingModel); + } + + public bool Equals([AllowNull] BuildingModel other) + { + return other != null && + Id == other.Id && + ParcelId == other.ParcelId && + LocalId == other.LocalId && + Description == other.Description && + EqualityComparer.Default.Equals(Address, other.Address) && + Latitude == other.Latitude && + Longitude == other.Longitude && + BuildingConstructionTypeId == other.BuildingConstructionTypeId && + BuildingConstructionType == other.BuildingConstructionType && + BuildingFloorCount == other.BuildingFloorCount && + BuildingPredominateUseId == other.BuildingPredominateUseId && + BuildingPredominateUse == other.BuildingPredominateUse && + BuildingTenancy == other.BuildingTenancy && + RentableArea == other.RentableArea && + Enumerable.SequenceEqual(Evaluations, other.Evaluations); + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Id); + hash.Add(ParcelId); + hash.Add(LocalId); + hash.Add(Description); + hash.Add(Address); + hash.Add(Latitude); + hash.Add(Longitude); + hash.Add(BuildingConstructionTypeId); + hash.Add(BuildingConstructionType); + hash.Add(BuildingFloorCount); + hash.Add(BuildingPredominateUseId); + hash.Add(BuildingPredominateUse); + hash.Add(BuildingTenancy); + hash.Add(RentableArea); + hash.Add(Evaluations); + return hash.ToHashCode(); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/CodeLookupModel.cs b/backend/api/Areas/Admin/Models/Parcel/CodeLookupModel.cs similarity index 96% rename from backend/api/Areas/Admin/Models/CodeLookupModel.cs rename to backend/api/Areas/Admin/Models/Parcel/CodeLookupModel.cs index a85e2d5955..666d2d1320 100644 --- a/backend/api/Areas/Admin/Models/CodeLookupModel.cs +++ b/backend/api/Areas/Admin/Models/Parcel/CodeLookupModel.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Pims.Api.Models; -namespace Pims.Api.Areas.Admin.Models +namespace Pims.Api.Areas.Admin.Models.Parcel { /// /// LookupCodeModel class, provides a model that represents look up objects. diff --git a/backend/api/Areas/Admin/Models/Parcel/ParcelBuildingModel.cs b/backend/api/Areas/Admin/Models/Parcel/ParcelBuildingModel.cs new file mode 100644 index 0000000000..5017009cc8 --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/ParcelBuildingModel.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class ParcelBuildingModel : Model.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public int ParcelId { get; set; } + + public int AgencyId { get; set; } + + public string LocalId { get; set; } + + public string Description { get; set; } + + public AddressModel Address { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public int BuildingConstructionTypeId { get; set; } + + public string BuildingConstructionType { get; set; } + + public int BuildingFloorCount { get; set; } + + public int BuildingPredominateUseId { get; set; } + + public string BuildingPredominateUse { get; set; } + + public string BuildingTenancy { get; set; } + + public float RentableArea { get; set; } + + public bool IsSensitive { get; set; } + + public IEnumerable Evaluations { get; set; } = new List(); + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as ParcelBuildingModel); + } + + public bool Equals([AllowNull] ParcelBuildingModel other) + { + return other != null && + Id == other.Id && + LocalId == other.LocalId && + ParcelId == other.ParcelId && + AgencyId == other.AgencyId && + Description == other.Description && + EqualityComparer.Default.Equals(Address, other.Address) && + Latitude == other.Latitude && + Longitude == other.Longitude && + BuildingConstructionTypeId == other.BuildingConstructionTypeId && + BuildingConstructionType == other.BuildingConstructionType && + BuildingFloorCount == other.BuildingFloorCount && + BuildingPredominateUseId == other.BuildingPredominateUseId && + BuildingPredominateUse == other.BuildingPredominateUse && + BuildingTenancy == other.BuildingTenancy && + RentableArea == other.RentableArea && + Enumerable.SequenceEqual(Evaluations, other.Evaluations); + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Id); + hash.Add(LocalId); + hash.Add(ParcelId); + hash.Add(AgencyId); + hash.Add(Description); + hash.Add(Address); + hash.Add(Latitude); + hash.Add(Longitude); + hash.Add(BuildingConstructionTypeId); + hash.Add(BuildingConstructionType); + hash.Add(BuildingFloorCount); + hash.Add(BuildingPredominateUseId); + hash.Add(BuildingPredominateUse); + hash.Add(BuildingTenancy); + hash.Add(RentableArea); + hash.Add(Evaluations); + return hash.ToHashCode(); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/Parcel/ParcelEvaluationModel.cs b/backend/api/Areas/Admin/Models/Parcel/ParcelEvaluationModel.cs new file mode 100644 index 0000000000..7425804b64 --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/ParcelEvaluationModel.cs @@ -0,0 +1,56 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class ParcelEvaluationModel : Model.BaseModel, IEquatable + { + #region Properties + public int ParcelId { get; set; } + + public int FiscalYear { get; set; } + + public float EstimatedValue { get; set; } + + public float AppraisedValue { get; set; } + + public float AssessedValue { get; set; } + + public float NetBookValue { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as ParcelEvaluationModel); + } + + public bool Equals([AllowNull] ParcelEvaluationModel other) + { + return other != null && + base.Equals(other) && + ParcelId == other.ParcelId && + FiscalYear == other.FiscalYear && + EstimatedValue == other.EstimatedValue && + AppraisedValue == other.AppraisedValue && + AssessedValue == other.AssessedValue && + NetBookValue == other.NetBookValue; + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(ParcelId); + hash.Add(FiscalYear); + hash.Add(EstimatedValue); + hash.Add(AppraisedValue); + hash.Add(AssessedValue); + hash.Add(NetBookValue); + return hash.ToHashCode(); + } + + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/Parcel/ParcelModel.cs b/backend/api/Areas/Admin/Models/Parcel/ParcelModel.cs new file mode 100644 index 0000000000..ab08e18236 --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/ParcelModel.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class ParcelModel : Model.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public string PID { get; set; } + + public string PIN { get; set; } + + public int StatusId { get; set; } + + public string Status { get; set; } + + public int ClassificationId { get; set; } + + public string Classification { get; set; } + + public int AgencyId { get; set; } + + public string SubAgency { get; set; } + + public string Agency { get; set; } + + public AddressModel Address { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public float LandArea { get; set; } + + public string Description { get; set; } + + public string LandLegalDescription { get; set; } + + public bool IsSensitive { get; set; } + + public IEnumerable Evaluations { get; set; } = new List(); + public IEnumerable Buildings { get; set; } = new List(); + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as ParcelModel); + } + + public bool Equals([AllowNull] ParcelModel other) + { + return other != null && + base.Equals(other) && + Id == other.Id && + PID == other.PID && + PIN == other.PIN && + StatusId == other.StatusId && + Status == other.Status && + ClassificationId == other.ClassificationId && + Classification == other.Classification && + AgencyId == other.AgencyId && + SubAgency == other.SubAgency && + Agency == other.Agency && + EqualityComparer.Default.Equals(Address, other.Address) && + Latitude == other.Latitude && + Longitude == other.Longitude && + LandArea == other.LandArea && + Description == other.Description && + LandLegalDescription == other.LandLegalDescription && + Enumerable.SequenceEqual(Buildings, other.Buildings) && + Enumerable.SequenceEqual(Evaluations, other.Evaluations); + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(Id); + hash.Add(PID); + hash.Add(PIN); + hash.Add(StatusId); + hash.Add(Status); + hash.Add(ClassificationId); + hash.Add(Classification); + hash.Add(AgencyId); + hash.Add(SubAgency); + hash.Add(Agency); + hash.Add(Address); + hash.Add(Latitude); + hash.Add(Longitude); + hash.Add(LandArea); + hash.Add(Description); + hash.Add(LandLegalDescription); + hash.Add(Buildings); + hash.Add(Evaluations); + return hash.ToHashCode(); + } + + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/Parcel/PartialBuildingModel.cs b/backend/api/Areas/Admin/Models/Parcel/PartialBuildingModel.cs new file mode 100644 index 0000000000..8b6156bfd2 --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/PartialBuildingModel.cs @@ -0,0 +1,43 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class PartialBuildingModel : Model.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public string LocalId { get; set; } + + public string Description { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as PartialBuildingModel); + } + + public bool Equals([AllowNull] PartialBuildingModel other) + { + return other != null && + Id == other.Id && + LocalId == other.LocalId && + Description == other.Description && + Latitude == other.Latitude && + Longitude == other.Longitude; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, LocalId, Description, Latitude, Longitude); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/Parcel/PartialParcelModel.cs b/backend/api/Areas/Admin/Models/Parcel/PartialParcelModel.cs new file mode 100644 index 0000000000..4be626ffda --- /dev/null +++ b/backend/api/Areas/Admin/Models/Parcel/PartialParcelModel.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Model = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Models.Parcel +{ + public class PartialParcelModel : Model.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public string PID { get; set; } + + public string PIN { get; set; } + + public int StatusId { get; set; } + + public int ClassificationId { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public string Description { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as PartialParcelModel); + } + + public bool Equals([AllowNull] PartialParcelModel other) + { + return other != null && + Id == other.Id && + PID == other.PID && + PIN == other.PIN && + StatusId == other.StatusId && + ClassificationId == other.ClassificationId && + Latitude == other.Latitude && + Longitude == other.Longitude && + Description == other.Description; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, PID, PIN, StatusId, ClassificationId, Latitude, Longitude, Description); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/RoleModel.cs b/backend/api/Areas/Admin/Models/Role/RoleModel.cs similarity index 76% rename from backend/api/Areas/Admin/Models/RoleModel.cs rename to backend/api/Areas/Admin/Models/Role/RoleModel.cs index ef96f9e1f4..bdf571b6fa 100644 --- a/backend/api/Areas/Admin/Models/RoleModel.cs +++ b/backend/api/Areas/Admin/Models/Role/RoleModel.cs @@ -1,11 +1,9 @@ -using Pims.Api.Models; - -namespace Pims.Api.Areas.Admin.Models +namespace Pims.Api.Areas.Admin.Models.Role { /// /// RoleModel class, provides a model that represents a role. /// - public class RoleModel : CodeModel + public class RoleModel : Pims.Api.Models.CodeModel { #region Properties /// diff --git a/backend/api/Areas/Admin/Models/User/AccessRequestModel.cs b/backend/api/Areas/Admin/Models/User/AccessRequestModel.cs new file mode 100644 index 0000000000..57269a9781 --- /dev/null +++ b/backend/api/Areas/Admin/Models/User/AccessRequestModel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Admin.Models.User +{ + public class AccessRequestModel : Pims.Api.Models.BaseModel + { + #region Properties + public Guid Id { get; set; } + public bool? IsGranted { get; set; } + public bool IsDisabled { get; set; } + public AccessRequestUserModel User { get; set; } + public IEnumerable Agencies { get; set; } + public IEnumerable Roles { get; set; } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/User/AccessRequestRoleModel.cs b/backend/api/Areas/Admin/Models/User/AccessRequestRoleModel.cs new file mode 100644 index 0000000000..d4151c8bbd --- /dev/null +++ b/backend/api/Areas/Admin/Models/User/AccessRequestRoleModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Admin.Models.User +{ + /// + /// AccessRequestRoleModel class, provides a model that represents a role attached to an access request. + /// + public class AccessRequestRoleModel : Pims.Api.Models.CodeModel, IEquatable + { + #region Properties + public string Description { get; set; } + public ICollection Users { get; } = new List(); + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as AccessRequestRoleModel); + } + + public bool Equals([AllowNull] AccessRequestRoleModel other) + { + return other != null && + Id.Equals(other.Id) && + Name == other.Name && + Description == other.Description && + IsDisabled == other.IsDisabled && + EqualityComparer>.Default.Equals(Users, other.Users); + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Name, Description, IsDisabled, Users); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/User/AccessRequestUserModel.cs b/backend/api/Areas/Admin/Models/User/AccessRequestUserModel.cs new file mode 100644 index 0000000000..88626d3c11 --- /dev/null +++ b/backend/api/Areas/Admin/Models/User/AccessRequestUserModel.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Admin.Models.User +{ + /// + /// AccessRequestUserModel class, provides a model that represents a user attached to an access request. + /// + public class AccessRequestUserModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + /// + /// get/set - The user's unique identifier. + /// + /// + public Guid Id { get; set; } + + /// + /// get/set - The user's display name. + /// + /// + public string DisplayName { get; set; } + + /// + /// get/set - The user's given name. + /// + /// + public string FirstName { get; set; } + + /// + /// get/set - The user's middlename. + /// + /// + public string MiddleName { get; set; } + + /// + /// get/set - The user's surname. + /// + /// + public string LastName { get; set; } + + /// + /// get/set - The user's email. + /// + /// + public string Email { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as AccessRequestUserModel); + } + + public bool Equals([AllowNull] AccessRequestUserModel other) + { + return other != null && + Id.Equals(other.Id) && + DisplayName == other.DisplayName && + FirstName == other.FirstName && + MiddleName == other.MiddleName && + LastName == other.LastName && + Email == other.Email; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, DisplayName, FirstName, MiddleName, LastName, Email); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/User/AgencyModel.cs b/backend/api/Areas/Admin/Models/User/AgencyModel.cs new file mode 100644 index 0000000000..3770d3bacf --- /dev/null +++ b/backend/api/Areas/Admin/Models/User/AgencyModel.cs @@ -0,0 +1,65 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Admin.Models.User +{ + /// + /// AgencyModel class, provides a model that represents the agency. + /// + public class AgencyModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + /// + /// get/set - The unique identity of the agency. + /// + /// + public int Id { get; set; } + + /// + /// get/set - The unique name of the agency. + /// + /// + public string Name { get; set; } + + /// + /// get/set - The unique code to identify the agency. + /// + /// + public string Code { get; set; } + + /// + /// get/set - The agency description. + /// + /// + public string Description { get; set; } + + /// + /// get/set - The parent agency. + /// + /// + public int? ParentId { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as AgencyModel); + } + + public bool Equals([AllowNull] AgencyModel other) + { + return other != null && + Id.Equals(other.Id) && + Name == other.Name && + Code == other.Code && + Description == other.Description && + ParentId == other.ParentId; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Name, Code, Description, ParentId); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/User/RoleModel.cs b/backend/api/Areas/Admin/Models/User/RoleModel.cs new file mode 100644 index 0000000000..55ba2b8041 --- /dev/null +++ b/backend/api/Areas/Admin/Models/User/RoleModel.cs @@ -0,0 +1,16 @@ +namespace Pims.Api.Areas.Admin.Models.User +{ + /// + /// RoleModel class, provides a model that represents a role. + /// + public class RoleModel : Pims.Api.Models.CodeModel + { + #region Properties + /// + /// get/set - The role description. + /// + /// + public string Description { get; set; } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Models/UserModel.cs b/backend/api/Areas/Admin/Models/User/UserModel.cs similarity index 96% rename from backend/api/Areas/Admin/Models/UserModel.cs rename to backend/api/Areas/Admin/Models/User/UserModel.cs index ff6a4cd94e..059b61f20a 100644 --- a/backend/api/Areas/Admin/Models/UserModel.cs +++ b/backend/api/Areas/Admin/Models/User/UserModel.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Pims.Api.Models; -namespace Pims.Api.Areas.Admin.Models +namespace Pims.Api.Areas.Admin.Models.User { /// /// UserModel class, provides a model that represents a user. /// - public class UserModel : BaseModel, IEquatable + public class UserModel : Pims.Api.Models.BaseModel, IEquatable { #region Properties /// diff --git a/backend/api/Areas/Admin/Profiles/Parcel/AddressProfile.cs b/backend/api/Areas/Admin/Profiles/Parcel/AddressProfile.cs new file mode 100644 index 0000000000..7c4077f432 --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/Parcel/AddressProfile.cs @@ -0,0 +1,30 @@ +using AutoMapper; +using Pims.Api.Models; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.Parcel; +using BModel = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Profiles.Parcel +{ + public class AddressProfile : Profile + { + #region Constructors + public AddressProfile() + { + CreateMap() + .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City.Name)) + .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province.Name)) + .ForMember(dest => dest.Line1, opt => opt.MapFrom(src => src.Address1)) + .ForMember(dest => dest.Line2, opt => opt.MapFrom(src => src.Address2)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.City, opt => opt.Ignore()) + .ForMember(dest => dest.Province, opt => opt.Ignore()) + .ForMember(dest => dest.Address1, opt => opt.MapFrom(src => src.Line1)) + .ForMember(dest => dest.Address2, opt => opt.MapFrom(src => src.Line2)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Profiles/AgencyProfile.cs b/backend/api/Areas/Admin/Profiles/Parcel/AgencyProfile.cs similarity index 84% rename from backend/api/Areas/Admin/Profiles/AgencyProfile.cs rename to backend/api/Areas/Admin/Profiles/Parcel/AgencyProfile.cs index f2cd95b6b5..fc97e9bb11 100644 --- a/backend/api/Areas/Admin/Profiles/AgencyProfile.cs +++ b/backend/api/Areas/Admin/Profiles/Parcel/AgencyProfile.cs @@ -1,9 +1,8 @@ using AutoMapper; -using Pims.Api.Profiles.Extensions; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Admin.Models; +using Model = Pims.Api.Areas.Admin.Models.Parcel; -namespace Pims.Api.Areas.Admin.Profiles +namespace Pims.Api.Areas.Admin.Profiles.Parcel { /// /// AgencyProfile class, provides a way to configure automapper to convert agencies. diff --git a/backend/api/Areas/Admin/Profiles/Parcel/BuildingEvaluationProfile.cs b/backend/api/Areas/Admin/Profiles/Parcel/BuildingEvaluationProfile.cs new file mode 100644 index 0000000000..86e1852105 --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/Parcel/BuildingEvaluationProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.Parcel; +using BModel = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Profiles.Parcel +{ + public class BuildingEvaluationProfile : Profile + { + #region Constructors + public BuildingEvaluationProfile() + { + CreateMap() + .ForMember(dest => dest.BuildingId, opt => opt.MapFrom(src => src.BuildingId)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .ForMember(dest => dest.BuildingId, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Profiles/Parcel/BuildingProfile.cs b/backend/api/Areas/Admin/Profiles/Parcel/BuildingProfile.cs new file mode 100644 index 0000000000..c4ea700a6e --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/Parcel/BuildingProfile.cs @@ -0,0 +1,87 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.Parcel; +using BModel = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Profiles.Parcel +{ + public class BuildingProfile : Profile + { + #region Constructors + public BuildingProfile() + { + CreateMap(); + + CreateMap() + .ForMember(dest => dest.ParcelId, opt => opt.Ignore()) + .ForMember(dest => dest.Parcel, opt => opt.Ignore()) + .ForMember(dest => dest.AddressId, opt => opt.Ignore()) + .ForMember(dest => dest.Address, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingConstructionTypeId, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingFloorCount, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingPredominateUseId, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingTenancy, opt => opt.Ignore()) + .ForMember(dest => dest.RentableArea, opt => opt.Ignore()) + .ForMember(dest => dest.AgencyId, opt => opt.Ignore()) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.IsSensitive, opt => opt.Ignore()) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.BuildingConstructionType, opt => opt.MapFrom(src => src.BuildingConstructionType.Name)) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.MapFrom(src => src.BuildingPredominateUse.Name)); + + CreateMap() // TODO: Map evaluation + .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) + .ForMember(dest => dest.Parcel, opt => opt.Ignore()) + .ForMember(dest => dest.AgencyId, opt => opt.Ignore()) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.IsSensitive, opt => opt.Ignore()) + .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.ParcelId, opt => opt.MapFrom(src => src.ParcelId)) + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => src.AgencyId)) + .ForMember(dest => dest.LocalId, opt => opt.MapFrom(src => src.LocalId)) + .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.Latitude, opt => opt.MapFrom(src => src.Latitude)) + .ForMember(dest => dest.Longitude, opt => opt.MapFrom(src => src.Longitude)) + .ForMember(dest => dest.BuildingConstructionTypeId, opt => opt.MapFrom(src => src.BuildingConstructionTypeId)) + .ForMember(dest => dest.BuildingConstructionType, opt => opt.MapFrom(src => src.BuildingConstructionType.Name)) + .ForMember(dest => dest.BuildingFloorCount, opt => opt.MapFrom(src => src.BuildingFloorCount)) + .ForMember(dest => dest.BuildingPredominateUseId, opt => opt.MapFrom(src => src.BuildingPredominateUseId)) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.MapFrom(src => src.BuildingPredominateUse.Name)) + .ForMember(dest => dest.BuildingTenancy, opt => opt.MapFrom(src => src.BuildingTenancy)) + .ForMember(dest => dest.RentableArea, opt => opt.MapFrom(src => src.RentableArea)) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Evaluations, opt => opt.MapFrom(src => src.Evaluations)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.ParcelId, opt => opt.MapFrom(src => src.ParcelId)) + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => src.AgencyId)) + .ForMember(dest => dest.LocalId, opt => opt.MapFrom(src => src.LocalId)) + .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.Latitude, opt => opt.MapFrom(src => src.Latitude)) + .ForMember(dest => dest.Longitude, opt => opt.MapFrom(src => src.Longitude)) + .ForMember(dest => dest.BuildingConstructionTypeId, opt => opt.MapFrom(src => src.BuildingConstructionTypeId)) + .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingFloorCount, opt => opt.MapFrom(src => src.BuildingFloorCount)) + .ForMember(dest => dest.BuildingPredominateUseId, opt => opt.MapFrom(src => src.BuildingPredominateUseId)) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingTenancy, opt => opt.MapFrom(src => src.BuildingTenancy)) + .ForMember(dest => dest.RentableArea, opt => opt.MapFrom(src => src.RentableArea)) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Evaluations, opt => opt.MapFrom(src => src.Evaluations)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Profiles/Parcel/ParcelEvaluationProfile.cs b/backend/api/Areas/Admin/Profiles/Parcel/ParcelEvaluationProfile.cs new file mode 100644 index 0000000000..2a4c4a1951 --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/Parcel/ParcelEvaluationProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.Parcel; +using BModel = Pims.Api.Models; + +namespace Pims.Api.Areas.Admin.Profiles.Parcel +{ + public class ParcelEvaluationProfile : Profile + { + #region Constructors + public ParcelEvaluationProfile() + { + CreateMap() + .ForMember(dest => dest.ParcelId, opt => opt.MapFrom(src => src.ParcelId)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .ForMember(dest => dest.ParcelId, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/ParcelProfile.cs b/backend/api/Areas/Admin/Profiles/Parcel/ParcelProfile.cs similarity index 67% rename from backend/api/Profiles/ParcelProfile.cs rename to backend/api/Areas/Admin/Profiles/Parcel/ParcelProfile.cs index 222bb789e8..e47deeaa4f 100644 --- a/backend/api/Profiles/ParcelProfile.cs +++ b/backend/api/Areas/Admin/Profiles/Parcel/ParcelProfile.cs @@ -1,20 +1,22 @@ using AutoMapper; -using Pims.Api.Helpers.Profiles.Converters; -using Pims.Api.Models; -using Pims.Api.Profiles.Extensions; +using BModel = Pims.Api.Models; using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.Parcel; +using Pims.Api.Profiles.Converters; +using Pims.Api.Helpers.Extensions; +using Pims.Api.Areas.Admin.Profiles.Parcel.Resolvers; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Areas.Admin.Profiles.Parcel { public class ParcelProfile : Profile { #region Constructors public ParcelProfile() { - CreateMap() + CreateMap() .ForMember(dest => dest.PID, opt => opt.MapFrom(src => src.ParcelIdentity)); - CreateMap() + CreateMap() .IgnoreAllUnmapped() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) .ForMember(dest => dest.ParcelIdentity, opt => opt.Ignore()) @@ -24,27 +26,28 @@ public ParcelProfile() .ForMember(dest => dest.Longitude, opt => opt.MapFrom(src => src.Longitude)) .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) .ForMember(dest => dest.PID, opt => opt.ConvertUsing(new ParcelIdConverter(), src => src.PID)) - .IncludeBase(); + .IncludeBase(); - CreateMap() + CreateMap() .ForMember(dest => dest.PID, opt => opt.MapFrom(src => src.ParcelIdentity)) .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.Name)) .ForMember(dest => dest.Classification, opt => opt.MapFrom(src => src.Classification.Name)) .ForMember(dest => dest.Agency, opt => opt.ConvertUsing(new ParcelAgencyConverter())) .ForMember(dest => dest.SubAgency, opt => opt.MapFrom()) - .IncludeBase(); + .IncludeBase(); - CreateMap() - .ForMember(dest => dest.PID, opt => opt.MapFrom()) + CreateMap() + .ForMember(dest => dest.PID, opt => opt.ConvertUsing(new ParcelIdConverter(), src => src.PID)) .ForMember(dest => dest.ParcelIdentity, opt => opt.Ignore()) .ForMember(dest => dest.Status, opt => opt.Ignore()) .ForMember(dest => dest.Classification, opt => opt.Ignore()) .ForMember(dest => dest.Agency, opt => opt.Ignore()) .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)) - .ForMember(dest => dest.IsSensitive, opt => opt.Ignore()) - .ForMember(dest => dest.Buildings, opt => opt.MapFrom(src => src.Buildings)) - .ForMember(dest => dest.Evaluations, opt => opt.MapFrom(src => src.Evaluations)) - .IncludeBase(); + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Buildings, opt => opt.Ignore()) + .ForMember(dest => dest.Evaluations, opt => opt.Ignore()) + .IncludeBase(); } #endregion } diff --git a/backend/api/Areas/Admin/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs b/backend/api/Areas/Admin/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs new file mode 100644 index 0000000000..52393d1eab --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.Parcel; + +namespace Pims.Api.Areas.Admin.Profiles.Parcel.Resolvers +{ + public class ParcelSubAgencyResolver : IValueResolver + { + public string Resolve(Entity.Parcel source, Model.ParcelModel destination, string destMember, ResolutionContext context) + { + return source?.Agency?.ParentId == null ? null : source.Agency.Code; + } + } +} diff --git a/backend/api/Areas/Admin/Profiles/RoleProfile.cs b/backend/api/Areas/Admin/Profiles/Role/RoleProfile.cs similarity index 92% rename from backend/api/Areas/Admin/Profiles/RoleProfile.cs rename to backend/api/Areas/Admin/Profiles/Role/RoleProfile.cs index 29498bd875..77ff981d54 100644 --- a/backend/api/Areas/Admin/Profiles/RoleProfile.cs +++ b/backend/api/Areas/Admin/Profiles/Role/RoleProfile.cs @@ -1,9 +1,9 @@ using AutoMapper; using Pims.Api.Models; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Admin.Models; +using Model = Pims.Api.Areas.Admin.Models.Role; -namespace Pims.Api.Areas.Admin.Profiles +namespace Pims.Api.Areas.Admin.Profiles.Role { /// /// RoleProfile class, provides a way to configure automapper to convert roles. diff --git a/backend/api/Areas/Admin/Profiles/User/AccessRequestProfile.cs b/backend/api/Areas/Admin/Profiles/User/AccessRequestProfile.cs new file mode 100644 index 0000000000..39e7a04e35 --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/User/AccessRequestProfile.cs @@ -0,0 +1,45 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using System.Linq; +using System; +using Model = Pims.Api.Areas.Admin.Models.User; + +namespace Pims.Api.Areas.Admin.Profiles.User +{ + public class AccessRequestProfile : Profile + { + #region Constructors + public AccessRequestProfile() + { + CreateMap() + .ForMember(dest => dest.Agencies, opt => opt.MapFrom(src => src.Agencies.Select(a => a.Agency))) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles.Select(r => r.Role))) + .ForMember(dest => dest.User, opt => opt.MapFrom(src => src.User)); + + CreateMap() + .ForMember(dest => dest.User, opt => opt.Ignore()) + .ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.User.Id)); + + CreateMap() + .ForMember(dest => dest.AccessRequest, opt => opt.Ignore()) + .ForMember(dest => dest.AccessRequestId, opt => opt.Ignore()) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => src.Id)); + CreateMap() + .ForMember(dest => dest.AccessRequest, opt => opt.Ignore()) + .ForMember(dest => dest.AccessRequestId, opt => opt.Ignore()) + .ForMember(dest => dest.Role, opt => opt.Ignore()) + .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => Guid.Parse(src.Id))); + + CreateMap(); + CreateMap(); + + CreateMap() + .IncludeBase(); + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id.ToString())) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Profiles/User/AgencyProfile.cs b/backend/api/Areas/Admin/Profiles/User/AgencyProfile.cs new file mode 100644 index 0000000000..91261bdf36 --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/User/AgencyProfile.cs @@ -0,0 +1,24 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.User; + +namespace Pims.Api.Areas.Admin.Profiles.User +{ + /// + /// AgencyProfile class, provides a way to configure automapper to convert agencies. + /// + public class AgencyProfile : Profile + { + #region Constructors + public AgencyProfile() + { + CreateMap(); + + CreateMap() + .ForMember(dest => dest.Parent, opt => opt.Ignore()) + .ForMember(dest => dest.IsDisabled, opt => opt.Ignore()) + .ForMember(dest => dest.SortOrder, opt => opt.Ignore()); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Profiles/User/RoleProfile.cs b/backend/api/Areas/Admin/Profiles/User/RoleProfile.cs new file mode 100644 index 0000000000..ca5f2aa2c8 --- /dev/null +++ b/backend/api/Areas/Admin/Profiles/User/RoleProfile.cs @@ -0,0 +1,32 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Admin.Models.User; + +namespace Pims.Api.Areas.Admin.Profiles.User +{ + /// + /// RoleProfile class, provides a way to configure automapper to convert roles. + /// + public class RoleProfile : Profile + { + #region Constructors + public RoleProfile() + { + CreateMap() + .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.RoleId)); + + CreateMap(); + + CreateMap() + .ForMember(dest => dest.User, opt => opt.Ignore()) + .ForMember(dest => dest.UserId, opt => opt.Ignore()) + .ForMember(dest => dest.Role, opt => opt.Ignore()) + .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => src.Id)); + } + #endregion + } +} diff --git a/backend/api/Areas/Admin/Profiles/UserProfile.cs b/backend/api/Areas/Admin/Profiles/User/UserProfile.cs similarity index 88% rename from backend/api/Areas/Admin/Profiles/UserProfile.cs rename to backend/api/Areas/Admin/Profiles/User/UserProfile.cs index d8f450bc09..96877a007b 100644 --- a/backend/api/Areas/Admin/Profiles/UserProfile.cs +++ b/backend/api/Areas/Admin/Profiles/User/UserProfile.cs @@ -1,9 +1,9 @@ using AutoMapper; using System.Linq; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Admin.Models; +using Model = Pims.Api.Areas.Admin.Models.User; -namespace Pims.Api.Areas.Admin.Profiles +namespace Pims.Api.Areas.Admin.Profiles.User { /// /// UserProfile class, provides a way to configure automapper to convert users. diff --git a/backend/api/Areas/Keycloak/Controllers/RoleController.cs b/backend/api/Areas/Keycloak/Controllers/RoleController.cs index 68555f4eda..889d3737eb 100644 --- a/backend/api/Areas/Keycloak/Controllers/RoleController.cs +++ b/backend/api/Areas/Keycloak/Controllers/RoleController.cs @@ -3,7 +3,7 @@ using AutoMapper; using Microsoft.AspNetCore.Mvc; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.Role; using Pims.Api.Policies; using Pims.Dal.Security; using Pims.Dal.Keycloak; diff --git a/backend/api/Areas/Keycloak/Controllers/UserController.cs b/backend/api/Areas/Keycloak/Controllers/UserController.cs index 09e1a6d934..99c105fd97 100644 --- a/backend/api/Areas/Keycloak/Controllers/UserController.cs +++ b/backend/api/Areas/Keycloak/Controllers/UserController.cs @@ -4,7 +4,7 @@ using Pims.Dal.Security; using Pims.Dal.Keycloak; using Pims.Api.Policies; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.User; using Microsoft.AspNetCore.Mvc; using Entity = Pims.Dal.Entities; using AutoMapper; @@ -120,7 +120,6 @@ public async Task GetUserAsync(Guid id) public async Task UpdateUserAsync(Guid id, [FromBody] Model.Update.UserModel model) { var user = _mapper.Map(model); - user.Id = id; await _keycloakService.UpdateUserAsync(user); var result = _mapper.Map(user); @@ -134,15 +133,15 @@ public async Task UpdateUserAsync(Guid id, [FromBody] Model.Updat /// [HttpPut("access/request")] [Produces("application/json")] - [ProducesResponseType(typeof(Api.Models.AccessRequestModel), 200)] + [ProducesResponseType(typeof(Model.AccessRequestModel), 200)] [ProducesResponseType(typeof(Api.Models.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "keycloak-user" })] - public async Task UpdateAccessRequestAsync(Pims.Api.Models.AccessRequestModel updateModel) + public async Task UpdateAccessRequestAsync(Model.AccessRequestModel updateModel) { var entity = _mapper.Map(updateModel); var updatedEntity = await _keycloakService.UpdateAccessRequestAsync(entity); - var user = _mapper.Map(updatedEntity); + var user = _mapper.Map(updatedEntity); return new JsonResult(user); } #endregion diff --git a/backend/api/Areas/Keycloak/Models/RoleModel.cs b/backend/api/Areas/Keycloak/Models/Role/RoleModel.cs similarity index 90% rename from backend/api/Areas/Keycloak/Models/RoleModel.cs rename to backend/api/Areas/Keycloak/Models/Role/RoleModel.cs index ad32c4bbd1..b6cbe174a1 100644 --- a/backend/api/Areas/Keycloak/Models/RoleModel.cs +++ b/backend/api/Areas/Keycloak/Models/Role/RoleModel.cs @@ -1,13 +1,12 @@ using System; using System.Diagnostics.CodeAnalysis; -using Pims.Api.Models; -namespace Pims.Api.Areas.Keycloak.Models +namespace Pims.Api.Areas.Keycloak.Models.Role { /// /// RoleModel class, provides a model that represents a role. /// - public class RoleModel : BaseModel, IEquatable + public class RoleModel : Pims.Api.Models.BaseModel, IEquatable { #region Properties /// diff --git a/backend/api/Areas/Keycloak/Models/Update/BaseModel.cs b/backend/api/Areas/Keycloak/Models/Role/Update/BaseModel.cs similarity index 94% rename from backend/api/Areas/Keycloak/Models/Update/BaseModel.cs rename to backend/api/Areas/Keycloak/Models/Role/Update/BaseModel.cs index c511282587..fd7f9d98a7 100644 --- a/backend/api/Areas/Keycloak/Models/Update/BaseModel.cs +++ b/backend/api/Areas/Keycloak/Models/Role/Update/BaseModel.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Areas.Keycloak.Models.Update +namespace Pims.Api.Areas.Keycloak.Models.Role.Update { /// /// BaseModel class, provides a model that represents the base properties of an update model. diff --git a/backend/api/Areas/Keycloak/Models/Update/RoleModel.cs b/backend/api/Areas/Keycloak/Models/Role/Update/RoleModel.cs similarity index 95% rename from backend/api/Areas/Keycloak/Models/Update/RoleModel.cs rename to backend/api/Areas/Keycloak/Models/Role/Update/RoleModel.cs index 038278bc36..e5efaba0a1 100644 --- a/backend/api/Areas/Keycloak/Models/Update/RoleModel.cs +++ b/backend/api/Areas/Keycloak/Models/Role/Update/RoleModel.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Areas.Keycloak.Models.Update +namespace Pims.Api.Areas.Keycloak.Models.Role.Update { /// /// RoleModel class, provides a model that represents a role. diff --git a/backend/api/Areas/Keycloak/Models/User/AccessRequestModel.cs b/backend/api/Areas/Keycloak/Models/User/AccessRequestModel.cs new file mode 100644 index 0000000000..be1ae41848 --- /dev/null +++ b/backend/api/Areas/Keycloak/Models/User/AccessRequestModel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace Pims.Api.Areas.Keycloak.Models.User +{ + public class AccessRequestModel : Pims.Api.Models.BaseModel + { + #region Properties + public Guid Id { get; set; } + public AccessRequestUserModel User { get; set; } + public IEnumerable Agencies { get; set; } + public bool? IsGranted { get; set; } + public IEnumerable Roles { get; set; } + public bool IsDisabled { get; set; } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Models/User/AccessRequestRoleModel.cs b/backend/api/Areas/Keycloak/Models/User/AccessRequestRoleModel.cs new file mode 100644 index 0000000000..be5d5b4a52 --- /dev/null +++ b/backend/api/Areas/Keycloak/Models/User/AccessRequestRoleModel.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Keycloak.Models.User +{ + /// + /// AccessRequestRoleModel class, provides a model that represents a role attached to an access request. + /// + public class AccessRequestRoleModel : Pims.Api.Models.CodeModel, IEquatable + { + public string Description { get; set; } + public ICollection Users { get; } = new List(); + + public override bool Equals(object obj) + { + return Equals(obj as RoleModel); + } + + public bool Equals([AllowNull] RoleModel other) + { + return other != null && + Id.Equals(other.Id) && + Name == other.Name && + Description == other.Description && + IsDisabled == other.IsDisabled; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Name, Description, IsDisabled, Users); + } + } +} diff --git a/backend/api/Areas/Keycloak/Models/User/AccessRequestUserModel.cs b/backend/api/Areas/Keycloak/Models/User/AccessRequestUserModel.cs new file mode 100644 index 0000000000..3dba44cf56 --- /dev/null +++ b/backend/api/Areas/Keycloak/Models/User/AccessRequestUserModel.cs @@ -0,0 +1,72 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Keycloak.Models.User +{ + /// + /// AccessRequestUserModel class, provides a model that represents a user attached to an access request. + /// + public class AccessRequestUserModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + /// + /// get/set - The user's unique identifier. + /// + /// + public Guid Id { get; set; } + + /// + /// get/set - The user's display name. + /// + /// + public string DisplayName { get; set; } + + /// + /// get/set - The user's given name. + /// + /// + public string FirstName { get; set; } + + /// + /// get/set - The user's middlename. + /// + /// + public string MiddleName { get; set; } + + /// + /// get/set - The user's surname. + /// + /// + public string LastName { get; set; } + + /// + /// get/set - The user's email. + /// + /// + public string Email { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as AccessRequestUserModel); + } + + public bool Equals([AllowNull] AccessRequestUserModel other) + { + return other != null && + Id.Equals(other.Id) && + DisplayName == other.DisplayName && + FirstName == other.FirstName && + MiddleName == other.MiddleName && + LastName == other.LastName && + Email == other.Email; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, DisplayName, FirstName, MiddleName, LastName, Email); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Models/AgencyModel.cs b/backend/api/Areas/Keycloak/Models/User/AgencyModel.cs similarity index 92% rename from backend/api/Areas/Keycloak/Models/AgencyModel.cs rename to backend/api/Areas/Keycloak/Models/User/AgencyModel.cs index 8f31eeac67..e916388cf0 100644 --- a/backend/api/Areas/Keycloak/Models/AgencyModel.cs +++ b/backend/api/Areas/Keycloak/Models/User/AgencyModel.cs @@ -1,13 +1,12 @@ using System; using System.Diagnostics.CodeAnalysis; -using Pims.Api.Models; -namespace Pims.Api.Areas.Keycloak.Models +namespace Pims.Api.Areas.Keycloak.Models.User { /// /// AgencyModel class, provides a model to represent the agency. /// - public class AgencyModel : BaseModel, IEquatable + public class AgencyModel : Pims.Api.Models.BaseModel, IEquatable { #region Properties /// diff --git a/backend/api/Areas/Keycloak/Models/User/RoleModel.cs b/backend/api/Areas/Keycloak/Models/User/RoleModel.cs new file mode 100644 index 0000000000..37eaeb1616 --- /dev/null +++ b/backend/api/Areas/Keycloak/Models/User/RoleModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Keycloak.Models.User +{ + /// + /// RoleModel class, provides a model that represents a role. + /// + public class RoleModel : Pims.Api.Models.CodeModel, IEquatable + { + #region Properties + /// + /// get/set - The role description. + /// + /// + public string Description { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as RoleModel); + } + + public bool Equals([AllowNull] RoleModel other) + { + return other != null && + Id.Equals(other.Id) && + Name == other.Name && + Description == other.Description; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Name, Description); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Models/User/Update/BaseModel.cs b/backend/api/Areas/Keycloak/Models/User/Update/BaseModel.cs new file mode 100644 index 0000000000..f1de5fa7ff --- /dev/null +++ b/backend/api/Areas/Keycloak/Models/User/Update/BaseModel.cs @@ -0,0 +1,37 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Keycloak.Models.User.Update +{ + /// + /// BaseModel class, provides a model that represents the base properties of an update model. + /// + public abstract class BaseModel : IEquatable + { + #region Properties + /// + /// get/set - The rowversion of the item. + /// + /// + public string RowVersion { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as BaseModel); + } + + public bool Equals([AllowNull] BaseModel other) + { + return other != null && + RowVersion == other.RowVersion; + } + + public override int GetHashCode() + { + return HashCode.Combine(RowVersion); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Models/User/Update/RoleModel.cs b/backend/api/Areas/Keycloak/Models/User/Update/RoleModel.cs new file mode 100644 index 0000000000..0c615937ab --- /dev/null +++ b/backend/api/Areas/Keycloak/Models/User/Update/RoleModel.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Keycloak.Models.User.Update +{ + /// + /// RoleModel class, provides a model that represents a role. + /// + public class RoleModel : BaseModel, IEquatable + { + #region Properties + /// + /// get/set - A unique name to identify the role. + /// + /// + public string Name { get; set; } + + /// + /// get/set - The role description. + /// + /// + public string Description { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as RoleModel); + } + + public bool Equals([AllowNull] RoleModel other) + { + return other != null && + Name == other.Name && + Description == other.Description; + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Description); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Models/Update/UserAgencyModel.cs b/backend/api/Areas/Keycloak/Models/User/Update/UserAgencyModel.cs similarity index 95% rename from backend/api/Areas/Keycloak/Models/Update/UserAgencyModel.cs rename to backend/api/Areas/Keycloak/Models/User/Update/UserAgencyModel.cs index 34c652e359..28d5ce8655 100644 --- a/backend/api/Areas/Keycloak/Models/Update/UserAgencyModel.cs +++ b/backend/api/Areas/Keycloak/Models/User/Update/UserAgencyModel.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Areas.Keycloak.Models.Update +namespace Pims.Api.Areas.Keycloak.Models.User.Update { /// /// UserAgencyModel class, provides a model to represent a user agency. diff --git a/backend/api/Areas/Keycloak/Models/Update/UserModel.cs b/backend/api/Areas/Keycloak/Models/User/Update/UserModel.cs similarity index 93% rename from backend/api/Areas/Keycloak/Models/Update/UserModel.cs rename to backend/api/Areas/Keycloak/Models/User/Update/UserModel.cs index e44b81524c..dd60965531 100644 --- a/backend/api/Areas/Keycloak/Models/Update/UserModel.cs +++ b/backend/api/Areas/Keycloak/Models/User/Update/UserModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Areas.Keycloak.Models.Update +namespace Pims.Api.Areas.Keycloak.Models.User.Update { /// /// UserModel class, provides a model that represents a user. @@ -10,6 +10,12 @@ namespace Pims.Api.Areas.Keycloak.Models.Update public class UserModel : BaseModel, IEquatable { #region Properties + /// + /// get/set - An unique identifier for the user. + /// + /// + public Guid Id { get; set; } + /// /// get/set - A unique username to identify the user. /// diff --git a/backend/api/Areas/Keycloak/Models/Update/UserRoleModel.cs b/backend/api/Areas/Keycloak/Models/User/Update/UserRoleModel.cs similarity index 95% rename from backend/api/Areas/Keycloak/Models/Update/UserRoleModel.cs rename to backend/api/Areas/Keycloak/Models/User/Update/UserRoleModel.cs index 93cd15e217..74d153913f 100644 --- a/backend/api/Areas/Keycloak/Models/Update/UserRoleModel.cs +++ b/backend/api/Areas/Keycloak/Models/User/Update/UserRoleModel.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Areas.Keycloak.Models.Update +namespace Pims.Api.Areas.Keycloak.Models.User.Update { /// /// UserRoleModel class, provides a model that represents a user role model. diff --git a/backend/api/Areas/Keycloak/Models/UserModel.cs b/backend/api/Areas/Keycloak/Models/User/UserModel.cs similarity index 96% rename from backend/api/Areas/Keycloak/Models/UserModel.cs rename to backend/api/Areas/Keycloak/Models/User/UserModel.cs index 27a826a3a6..29ac7d4f83 100644 --- a/backend/api/Areas/Keycloak/Models/UserModel.cs +++ b/backend/api/Areas/Keycloak/Models/User/UserModel.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Pims.Api.Models; -namespace Pims.Api.Areas.Keycloak.Models +namespace Pims.Api.Areas.Keycloak.Models.User { /// /// UserModel class, provides a model to represent a user. /// - public class UserModel : BaseModel, IEquatable + public class UserModel : Pims.Api.Models.BaseModel, IEquatable { #region Properties /// diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateAgencyToEntityResolver.cs b/backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateAgencyToEntityResolver.cs deleted file mode 100644 index 986e0b5233..0000000000 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateAgencyToEntityResolver.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AutoMapper; -using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; - -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers -{ - /// - /// UpdateAgencyToEntityResolver class, provides automapper configuration to convert entiy agencies to model agencies. - /// - public class UpdateAgencyToEntityResolver : IValueResolver> - { - public ICollection Resolve(Model.Update.UserModel source, Entity.User destination, ICollection destMember, ResolutionContext context) - { - return source.Agencies.Select(a => new Entity.UserAgency() - { - AgencyId = a.Id, - UserId = destination.Id - }).ToList(); - } - } -} diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateRoleToEntityResolver.cs b/backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateRoleToEntityResolver.cs deleted file mode 100644 index 75b0389a94..0000000000 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/UpdateRoleToEntityResolver.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AutoMapper; -using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; - -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers -{ - /// - /// UpdateRoleToEntityResolver class, provides automapper configuration to convert entiy agencies to model agencies. - /// - public class UpdateRoleToEntityResolver : IValueResolver> - { - public ICollection Resolve(Model.Update.UserModel source, Entity.User destination, ICollection destMember, ResolutionContext context) - { - return source.Roles.Select(r => new Entity.UserRole() - { - RoleId = r.Id, - UserId = destination.Id - }).ToList(); - } - } -} diff --git a/backend/api/Areas/Keycloak/Profiles/Role/BaseProfile.cs b/backend/api/Areas/Keycloak/Profiles/Role/BaseProfile.cs new file mode 100644 index 0000000000..e2f0106979 --- /dev/null +++ b/backend/api/Areas/Keycloak/Profiles/Role/BaseProfile.cs @@ -0,0 +1,35 @@ +using System; +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Keycloak.Models.Role; + +namespace Pims.Api.Areas.Keycloak.Profiles.Role +{ + /// + /// BaseProfile class, provides automapper configuration for base models. + /// + public class BaseProfile : Profile + { + #region Constructors + public BaseProfile() + { + CreateMap() + .ForMember(dest => dest.RowVersion, opt => opt.MapFrom(src => Convert.ToBase64String(src.RowVersion))); + + CreateMap() + .AfterMap((source, dest) => + { + if (!String.IsNullOrWhiteSpace(source.RowVersion)) + { + var rowversion = Convert.FromBase64String(source.RowVersion); + if (dest.RowVersion == null) + { + dest.RowVersion = new byte[rowversion.Length]; + } + Buffer.BlockCopy(rowversion, 0, dest.RowVersion, 0, rowversion.Length); + } + }); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Profiles/Role/RoleProfile.cs b/backend/api/Areas/Keycloak/Profiles/Role/RoleProfile.cs new file mode 100644 index 0000000000..05e2fe7462 --- /dev/null +++ b/backend/api/Areas/Keycloak/Profiles/Role/RoleProfile.cs @@ -0,0 +1,26 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Keycloak.Models.Role; +using KModel = Pims.Keycloak.Models; + +namespace Pims.Api.Areas.Keycloak.Profiles.Role +{ + /// + /// RoleProfile class, provides automapper configuration for groups. + /// + public class RoleProfile : Profile + { + #region Constructors + public RoleProfile() + { + CreateMap(); + + CreateMap() + .IncludeBase(); + + CreateMap(); + CreateMap(); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Profiles/Role/UpdateRoleProfile.cs b/backend/api/Areas/Keycloak/Profiles/Role/UpdateRoleProfile.cs new file mode 100644 index 0000000000..6bd38e73e0 --- /dev/null +++ b/backend/api/Areas/Keycloak/Profiles/Role/UpdateRoleProfile.cs @@ -0,0 +1,26 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Keycloak.Models.Role; +using KModel = Pims.Keycloak.Models; + +namespace Pims.Api.Areas.Keycloak.Profiles.Role +{ + /// + /// RoleProfile class, provides automapper configuration for groups. + /// + public class UpdateRoleProfile : Profile + { + #region Constructors + public UpdateRoleProfile() + { + CreateMap() + .IncludeBase(); + + CreateMap() + .IncludeBase(); + + CreateMap(); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Profiles/User/AccessRequestProfile.cs b/backend/api/Areas/Keycloak/Profiles/User/AccessRequestProfile.cs new file mode 100644 index 0000000000..d6e83b50a5 --- /dev/null +++ b/backend/api/Areas/Keycloak/Profiles/User/AccessRequestProfile.cs @@ -0,0 +1,47 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.User; +using System.Linq; +using System; + +namespace Pims.Api.Areas.Keycloak.Profiles.User +{ + public class AccessRequestProfile : Profile + { + #region Constructors + public AccessRequestProfile() + { + CreateMap() + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.Agencies, opt => opt.MapFrom(src => src.Agencies.Select(a => a.Agency))) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles.Select(r => r.Role))) + .ForMember(dest => dest.User, opt => opt.MapFrom(src => src.User)); + + CreateMap() + .ForMember(dest => dest.User, opt => opt.Ignore()) + .ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.User.Id)); + + CreateMap() + .ForMember(dest => dest.AccessRequest, opt => opt.Ignore()) + .ForMember(dest => dest.AccessRequestId, opt => opt.Ignore()) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => Int32.Parse(src.Id))); + CreateMap() + .ForMember(dest => dest.AccessRequest, opt => opt.Ignore()) + .ForMember(dest => dest.AccessRequestId, opt => opt.Ignore()) + .ForMember(dest => dest.Role, opt => opt.Ignore()) + .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => Guid.Parse(src.Id))); + + CreateMap(); + CreateMap(); + CreateMap() + .IncludeBase(); + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id.ToString())) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Profiles/AgencyProfile.cs b/backend/api/Areas/Keycloak/Profiles/User/AgencyProfile.cs similarity index 76% rename from backend/api/Areas/Keycloak/Profiles/AgencyProfile.cs rename to backend/api/Areas/Keycloak/Profiles/User/AgencyProfile.cs index db14253f61..6dd434e8f5 100644 --- a/backend/api/Areas/Keycloak/Profiles/AgencyProfile.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/AgencyProfile.cs @@ -1,9 +1,8 @@ using AutoMapper; -using Pims.Api.Profiles.Extensions; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.User; -namespace Pims.Api.Areas.Keycloak.Profiles +namespace Pims.Api.Areas.Keycloak.Profiles.User { /// /// AgencyProfile class, provides automapper configuration for agencies. @@ -13,6 +12,9 @@ public class AgencyProfile : Profile #region Constructors public AgencyProfile() { + CreateMap() + .IncludeBase(); + CreateMap() .IncludeBase(); diff --git a/backend/api/Areas/Keycloak/Profiles/BaseProfile.cs b/backend/api/Areas/Keycloak/Profiles/User/BaseProfile.cs similarity index 79% rename from backend/api/Areas/Keycloak/Profiles/BaseProfile.cs rename to backend/api/Areas/Keycloak/Profiles/User/BaseProfile.cs index b1422d10ea..3345a8f69c 100644 --- a/backend/api/Areas/Keycloak/Profiles/BaseProfile.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/BaseProfile.cs @@ -1,9 +1,9 @@ using System; using AutoMapper; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.User; -namespace Pims.Api.Areas.Keycloak.Profiles +namespace Pims.Api.Areas.Keycloak.Profiles.User { /// /// BaseProfile class, provides automapper configuration for base models. @@ -14,14 +14,10 @@ public class BaseProfile : Profile public BaseProfile() { CreateMap() - .IncludeAllDerived() .ForMember(dest => dest.RowVersion, opt => opt.MapFrom(src => Convert.ToBase64String(src.RowVersion))); CreateMap() - .IncludeAllDerived() .ForMember(dest => dest.RowVersion, opt => opt.Ignore()) - .ForMember(dest => dest.CreatedById, opt => opt.Ignore()) - .ForMember(dest => dest.UpdatedById, opt => opt.Ignore()) .AfterMap((source, dest) => { if (!String.IsNullOrWhiteSpace(source.RowVersion)) diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/AgencyToAgencyResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/AgencyToAgencyResolver.cs similarity index 91% rename from backend/api/Areas/Keycloak/Profiles/Resolvers/AgencyToAgencyResolver.cs rename to backend/api/Areas/Keycloak/Profiles/User/Resolvers/AgencyToAgencyResolver.cs index 5ecfad8855..68af2e62e4 100644 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/AgencyToAgencyResolver.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/AgencyToAgencyResolver.cs @@ -3,9 +3,9 @@ using System.Linq; using AutoMapper; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.User; -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers { /// /// AgencyToAgencyResolver class, provides automapper configuration to convert entiy agencies to model agencies. diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/AttributeMapToAgencyResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/AttributeMapToAgencyResolver.cs similarity index 94% rename from backend/api/Areas/Keycloak/Profiles/Resolvers/AttributeMapToAgencyResolver.cs rename to backend/api/Areas/Keycloak/Profiles/User/Resolvers/AttributeMapToAgencyResolver.cs index b430b83b22..f5dcef506b 100644 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/AttributeMapToAgencyResolver.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/AttributeMapToAgencyResolver.cs @@ -4,7 +4,7 @@ using Entity = Pims.Dal.Entities; using KModel = Pims.Keycloak.Models; -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers { /// /// AttributeMapToAgencyResolver class, provides automapper a way to covert the user attribute 'agencies' to a list of agencies. diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/EntityAgencyResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/EntityAgencyResolver.cs similarity index 92% rename from backend/api/Areas/Keycloak/Profiles/Resolvers/EntityAgencyResolver.cs rename to backend/api/Areas/Keycloak/Profiles/User/Resolvers/EntityAgencyResolver.cs index 549f90b325..119e0ca654 100644 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/EntityAgencyResolver.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/EntityAgencyResolver.cs @@ -4,7 +4,7 @@ using Entity = Pims.Dal.Entities; using KModel = Pims.Keycloak.Models; -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers { /// /// EntityAgencyResolver class, provides a way for automapper to convert a role to a group. diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/EntityRoleResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/EntityRoleResolver.cs similarity index 90% rename from backend/api/Areas/Keycloak/Profiles/Resolvers/EntityRoleResolver.cs rename to backend/api/Areas/Keycloak/Profiles/User/Resolvers/EntityRoleResolver.cs index a1ad317568..b99e3880f5 100644 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/EntityRoleResolver.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/EntityRoleResolver.cs @@ -3,7 +3,7 @@ using Entity = Pims.Dal.Entities; using KModel = Pims.Keycloak.Models; -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers { /// /// EntityRoleResolver class, provides a way for automapper to convert a role to a group. diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/KeycloakDisplayNameResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/KeycloakDisplayNameResolver.cs similarity index 92% rename from backend/api/Areas/Keycloak/Profiles/Resolvers/KeycloakDisplayNameResolver.cs rename to backend/api/Areas/Keycloak/Profiles/User/Resolvers/KeycloakDisplayNameResolver.cs index ecf03a321d..690465b2fd 100644 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/KeycloakDisplayNameResolver.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/KeycloakDisplayNameResolver.cs @@ -3,7 +3,7 @@ using Entity = Pims.Dal.Entities; using KModel = Pims.Keycloak.Models; -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers { /// /// KeycloakDisplayNameResolver class, provides a way for automapper to convert a keycloak display name attribute to a display name property. diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/KeycloakGroupResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/KeycloakGroupResolver.cs similarity index 90% rename from backend/api/Areas/Keycloak/Profiles/Resolvers/KeycloakGroupResolver.cs rename to backend/api/Areas/Keycloak/Profiles/User/Resolvers/KeycloakGroupResolver.cs index 2e1755781f..bc1a711478 100644 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/KeycloakGroupResolver.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/KeycloakGroupResolver.cs @@ -2,7 +2,7 @@ using Entity = Pims.Dal.Entities; using KModel = Pims.Keycloak.Models; -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers { /// /// KeycloakGroupResolver class, provides a way for automapper to convert a group to a role. diff --git a/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateAgencyToEntityResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateAgencyToEntityResolver.cs new file mode 100644 index 0000000000..b72bdf6192 --- /dev/null +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateAgencyToEntityResolver.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Keycloak.Models.User; + +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers +{ + /// + /// UpdateAgencyToEntityResolver class, provides automapper configuration to convert entiy agencies to model agencies. + /// + public class UpdateAgencyToEntityResolver : IValueResolver> + { + public ICollection Resolve(Model.Update.UserModel source, Entity.User destination, ICollection destMember, ResolutionContext context) + { + if (destMember == null) destMember = new List(); + + foreach (var agency in source.Agencies) + { + var existing = destMember.FirstOrDefault(a => a.AgencyId == agency.Id); + if (existing == null) + { + destMember.Add(new Entity.UserAgency() + { + UserId = destination.Id, + AgencyId = agency.Id + }); + } + } + + return destMember; + } + } +} diff --git a/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateRoleToEntityResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateRoleToEntityResolver.cs new file mode 100644 index 0000000000..8884ae51e9 --- /dev/null +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UpdateRoleToEntityResolver.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Keycloak.Models.User; + +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers +{ + /// + /// UpdateRoleToEntityResolver class, provides automapper configuration to convert entiy agencies to model agencies. + /// + public class UpdateRoleToEntityResolver : IValueResolver> + { + public ICollection Resolve(Model.Update.UserModel source, Entity.User destination, ICollection destMember, ResolutionContext context) + { + if (destMember == null) destMember = new List(); + + foreach (var role in source.Roles) + { + var existing = destMember.FirstOrDefault(a => a.RoleId == role.Id); + if (existing == null) + { + destMember.Add(new Entity.UserRole() + { + UserId = destination.Id, + RoleId = role.Id + }); + } + } + + return destMember; + } + } +} diff --git a/backend/api/Areas/Keycloak/Profiles/Resolvers/UserRoleToRoleResolver.cs b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UserRoleToRoleResolver.cs similarity index 86% rename from backend/api/Areas/Keycloak/Profiles/Resolvers/UserRoleToRoleResolver.cs rename to backend/api/Areas/Keycloak/Profiles/User/Resolvers/UserRoleToRoleResolver.cs index b1138cbbf9..1dafb8c37e 100644 --- a/backend/api/Areas/Keycloak/Profiles/Resolvers/UserRoleToRoleResolver.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/Resolvers/UserRoleToRoleResolver.cs @@ -3,9 +3,9 @@ using System.Linq; using AutoMapper; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.User; -namespace Pims.Api.Areas.Keycloak.Profiles.Resolvers +namespace Pims.Api.Areas.Keycloak.Profiles.User.Resolvers { /// /// UserRoleToRoleResolver class, provides a way for automapper to convert user roles to groups. @@ -16,7 +16,7 @@ public class UserRoleToRoleResolver : IValueResolver new Model.RoleModel() { - Id = r.RoleId, + Id = r.RoleId.ToString(), Name = r.Role?.Name, Description = r.Role?.Description, CreatedOn = r.Role?.CreatedOn ?? new DateTime(), diff --git a/backend/api/Areas/Keycloak/Profiles/RoleProfile.cs b/backend/api/Areas/Keycloak/Profiles/User/RoleProfile.cs similarity index 80% rename from backend/api/Areas/Keycloak/Profiles/RoleProfile.cs rename to backend/api/Areas/Keycloak/Profiles/User/RoleProfile.cs index 9e286e1c2b..6956290818 100644 --- a/backend/api/Areas/Keycloak/Profiles/RoleProfile.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/RoleProfile.cs @@ -1,10 +1,9 @@ using AutoMapper; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.User; using KModel = Pims.Keycloak.Models; -using Pims.Api.Profiles.Extensions; -namespace Pims.Api.Areas.Keycloak.Profiles +namespace Pims.Api.Areas.Keycloak.Profiles.User { /// /// RoleProfile class, provides automapper configuration for groups. @@ -14,6 +13,9 @@ public class RoleProfile : Profile #region Constructors public RoleProfile() { + CreateMap() + .IncludeBase(); + CreateMap(); CreateMap() diff --git a/backend/api/Areas/Keycloak/Profiles/User/UpdateUserProfile.cs b/backend/api/Areas/Keycloak/Profiles/User/UpdateUserProfile.cs new file mode 100644 index 0000000000..caf51e5239 --- /dev/null +++ b/backend/api/Areas/Keycloak/Profiles/User/UpdateUserProfile.cs @@ -0,0 +1,69 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Keycloak.Models.User; +using KModel = Pims.Keycloak.Models; + +namespace Pims.Api.Areas.Keycloak.Profiles.User +{ + /// + /// UpdateUserProfile class, provides automapper configuration for users. + /// + public class UpdateUserProfile : Profile + { + #region Constructors + public UpdateUserProfile() + { + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) + .ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.DisplayName)) + .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) + .ForMember(dest => dest.EmailVerified, opt => opt.MapFrom(src => src.EmailVerified)) + .ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName)) + .ForMember(dest => dest.MiddleName, opt => opt.MapFrom(src => src.MiddleName)) + .ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName)) + .ForMember(dest => dest.Position, opt => opt.MapFrom(src => src.Position)) + .ForMember(dest => dest.Note, opt => opt.MapFrom(src => src.Note)) + .ForMember(dest => dest.IsDisabled, opt => opt.MapFrom(src => src.IsDisabled)) + .ForMember(dest => dest.Agencies, opt => opt.MapFrom(src => src.Agencies)) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) + .ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.DisplayName)) + .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) + .ForMember(dest => dest.EmailVerified, opt => opt.MapFrom(src => src.EmailVerified)) + .ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName)) + .ForMember(dest => dest.MiddleName, opt => opt.MapFrom(src => src.MiddleName)) + .ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName)) + .ForMember(dest => dest.Position, opt => opt.MapFrom(src => src.Position)) + .ForMember(dest => dest.Note, opt => opt.MapFrom(src => src.Note)) + .ForMember(dest => dest.IsDisabled, opt => opt.MapFrom(src => src.IsDisabled)) + .ForMember(dest => dest.Agencies, opt => opt.MapFrom(src => src.Agencies)) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.Enabled, opt => opt.MapFrom(src => !src.IsDisabled)); + + // UserRole + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.RoleId)) + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Role.Name)); + + CreateMap() + .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => src.Id)); + + // UserAgency + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.AgencyId)) + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Agency.Name)); + + CreateMap() + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => src.Id)); + } + #endregion + } +} diff --git a/backend/api/Areas/Keycloak/Profiles/UserProfile.cs b/backend/api/Areas/Keycloak/Profiles/User/UserProfile.cs similarity index 68% rename from backend/api/Areas/Keycloak/Profiles/UserProfile.cs rename to backend/api/Areas/Keycloak/Profiles/User/UserProfile.cs index fd1815ae8e..1929eee170 100644 --- a/backend/api/Areas/Keycloak/Profiles/UserProfile.cs +++ b/backend/api/Areas/Keycloak/Profiles/User/UserProfile.cs @@ -1,10 +1,9 @@ using AutoMapper; using Entity = Pims.Dal.Entities; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.User; using KModel = Pims.Keycloak.Models; -using Pims.Api.Profiles.Extensions; -namespace Pims.Api.Areas.Keycloak.Profiles +namespace Pims.Api.Areas.Keycloak.Profiles.User { /// /// UserProfile class, provides automapper configuration for users. @@ -14,6 +13,9 @@ public class UserProfile : Profile #region Constructors public UserProfile() { + CreateMap() + .IncludeBase(); + CreateMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) @@ -54,25 +56,6 @@ public UserProfile() .ForMember(dest => dest.Enabled, opt => opt.MapFrom(src => !src.IsDisabled)); CreateMap() .ForMember(dest => dest.IsDisabled, opt => opt.MapFrom(src => !src.Enabled)); - - // Update models - CreateMap() - .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) - .ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.DisplayName)) - .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) - .ForMember(dest => dest.EmailVerified, opt => opt.MapFrom(src => src.EmailVerified)) - .ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName)) - .ForMember(dest => dest.MiddleName, opt => opt.MapFrom(src => src.MiddleName)) - .ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName)) - .ForMember(dest => dest.Position, opt => opt.MapFrom(src => src.Position)) - .ForMember(dest => dest.Note, opt => opt.MapFrom(src => src.Note)) - .ForMember(dest => dest.IsDisabled, opt => opt.MapFrom(src => src.IsDisabled)) - .ForMember(dest => dest.Agencies, opt => opt.MapFrom()) - .ForMember(dest => dest.Roles, opt => opt.MapFrom()) - .IncludeBase(); - - CreateMap() - .ForMember(dest => dest.Enabled, opt => opt.MapFrom(src => !src.IsDisabled)); } #endregion } diff --git a/backend/api/Areas/Tools/Controllers/ImportController.cs b/backend/api/Areas/Tools/Controllers/ImportController.cs index d56e210d12..eb2b75f8f6 100644 --- a/backend/api/Areas/Tools/Controllers/ImportController.cs +++ b/backend/api/Areas/Tools/Controllers/ImportController.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using Pims.Api.Areas.Tools.Helpers; using Pims.Api.Areas.Tools.Models; -using Model = Pims.Api.Models; +using Model = Pims.Api.Areas.Tools.Models.Import; using Pims.Api.Policies; using Pims.Dal.Security; using Pims.Dal.Services.Admin; @@ -56,9 +56,9 @@ public ImportController(ILogger logger, IPimsAdminService pims [HttpPost("properties")] [Produces("application/json")] [ProducesResponseType(typeof(IEnumerable), 200)] - [ProducesResponseType(typeof(Model.ErrorResponseModel), 400)] + [ProducesResponseType(typeof(Pims.Api.Models.ErrorResponseModel), 400)] [SwaggerOperation(Tags = new[] { "tools-import" })] - public IActionResult ImportProperties([FromBody] PropertyModel[] models) + public IActionResult ImportProperties([FromBody] Model.PropertyModel[] models) { if (models.Count() > 100) return BadRequest("Must not submit more than 100 properties in a single request."); diff --git a/backend/api/Areas/Tools/Helpers/ImportPropertiesHelper.cs b/backend/api/Areas/Tools/Helpers/ImportPropertiesHelper.cs index 0795c71bf9..50ef8e2c81 100644 --- a/backend/api/Areas/Tools/Helpers/ImportPropertiesHelper.cs +++ b/backend/api/Areas/Tools/Helpers/ImportPropertiesHelper.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; -using Pims.Api.Areas.Tools.Models; +using Model = Pims.Api.Areas.Tools.Models.Import; using Pims.Core.Extensions; using Pims.Core.Helpers; using Pims.Dal.Helpers.Extensions; @@ -56,7 +56,7 @@ public ImportPropertiesHelper(IPimsAdminService pimsAdminService, ILogger logger /// /// /// - public IEnumerable AddUpdateProperties(IEnumerable properties) + public IEnumerable AddUpdateProperties(IEnumerable properties) { if (properties == null) throw new ArgumentNullException(nameof(properties)); @@ -109,7 +109,7 @@ public ImportPropertiesHelper(IPimsAdminService pimsAdminService, ILogger logger /// /// /// - private Entity.Agency GetOrCreateAgency(PropertyModel property) + private Entity.Agency GetOrCreateAgency(Model.PropertyModel property) { // Find the agency or create a new one. var agency = _agencies.FirstOrDefault(a => a.Code == property.Agency.Trim()); @@ -230,7 +230,7 @@ private Entity.City GetOrCreateCity(string name) /// /// /// - private Entity.Parcel AddUpdateParcel(PropertyModel property, int pid, Entity.Agency agency) + private Entity.Parcel AddUpdateParcel(Model.PropertyModel property, int pid, Entity.Agency agency) { var p_e = ExceptionHelper.HandleKeyNotFoundWithDefault(() => _pimsAdminService.Parcel.GetByPid(pid)); var fiscal = int.Parse(property.FiscalYear); @@ -319,7 +319,7 @@ private Entity.Parcel AddUpdateParcel(PropertyModel property, int pid, Entity.Ag /// /// /// - private Entity.Parcel AddUpdateBuilding(PropertyModel property, int pid, Entity.Agency agency) + private Entity.Parcel AddUpdateBuilding(Model.PropertyModel property, int pid, Entity.Agency agency) { var lid = property.LocalId; var b_e = ExceptionHelper.HandleKeyNotFoundWithDefault(() => _pimsAdminService.Building.GetByPidAndLocalId(pid, lid)); diff --git a/backend/api/Areas/Tools/Models/Import/AddressModel.cs b/backend/api/Areas/Tools/Models/Import/AddressModel.cs new file mode 100644 index 0000000000..00d60c7db8 --- /dev/null +++ b/backend/api/Areas/Tools/Models/Import/AddressModel.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Tools.Models.Import +{ + public class AddressModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public string Line1 { get; set; } + + public string Line2 { get; set; } + + public int CityId { get; set; } + + public string City { get; set; } + + public string ProvinceId { get; set; } + + public string Province { get; set; } + + public string Postal { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as AddressModel); + } + + public bool Equals([AllowNull] AddressModel other) + { + return other != null && + Id == other.Id && + Line1 == other.Line1 && + Line2 == other.Line2 && + CityId == other.CityId && + City == other.City && + ProvinceId == other.ProvinceId && + Province == other.Province && + Postal == other.Postal; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Line1, Line2, CityId, City, ProvinceId, Province, Postal); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Models/Import/BuidlingEvaluationModel.cs b/backend/api/Areas/Tools/Models/Import/BuidlingEvaluationModel.cs new file mode 100644 index 0000000000..e9f6e1071d --- /dev/null +++ b/backend/api/Areas/Tools/Models/Import/BuidlingEvaluationModel.cs @@ -0,0 +1,54 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Tools.Models.Import +{ + public class BuildingEvaluationModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + public int PropertyId { get; set; } + + public int FiscalYear { get; set; } + + public float EstimatedValue { get; set; } + + public float AppraisedValue { get; set; } + + public float AssessedValue { get; set; } + + public float NetBookValue { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as BuildingEvaluationModel); + } + + public bool Equals([AllowNull] BuildingEvaluationModel other) + { + return other != null && + base.Equals(other) && + PropertyId == other.PropertyId && + FiscalYear == other.FiscalYear && + EstimatedValue == other.EstimatedValue && + AppraisedValue == other.AppraisedValue && + AssessedValue == other.AssessedValue && + NetBookValue == other.NetBookValue; + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(PropertyId); + hash.Add(FiscalYear); + hash.Add(EstimatedValue); + hash.Add(AppraisedValue); + hash.Add(AssessedValue); + hash.Add(NetBookValue); + return hash.ToHashCode(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Models/Import/ParcelBuildingModel.cs b/backend/api/Areas/Tools/Models/Import/ParcelBuildingModel.cs new file mode 100644 index 0000000000..5359da4404 --- /dev/null +++ b/backend/api/Areas/Tools/Models/Import/ParcelBuildingModel.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Pims.Api.Areas.Tools.Models.Import +{ + public class ParcelBuildingModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public int ParcelId { get; set; } + + public int AgencyId { get; set; } + + public string LocalId { get; set; } + + public string Description { get; set; } + + public AddressModel Address { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public int BuildingConstructionTypeId { get; set; } + + public string BuildingConstructionType { get; set; } + + public int BuildingFloorCount { get; set; } + + public int BuildingPredominateUseId { get; set; } + + public string BuildingPredominateUse { get; set; } + + public string BuildingTenancy { get; set; } + + public float RentableArea { get; set; } + + public bool IsSensitive { get; set; } + + public IEnumerable Evaluations { get; set; } = new List(); + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as ParcelBuildingModel); + } + + public bool Equals([AllowNull] ParcelBuildingModel other) + { + return other != null && + Id == other.Id && + LocalId == other.LocalId && + ParcelId == other.ParcelId && + AgencyId == other.AgencyId && + Description == other.Description && + EqualityComparer.Default.Equals(Address, other.Address) && + Latitude == other.Latitude && + Longitude == other.Longitude && + BuildingConstructionTypeId == other.BuildingConstructionTypeId && + BuildingConstructionType == other.BuildingConstructionType && + BuildingFloorCount == other.BuildingFloorCount && + BuildingPredominateUseId == other.BuildingPredominateUseId && + BuildingPredominateUse == other.BuildingPredominateUse && + BuildingTenancy == other.BuildingTenancy && + RentableArea == other.RentableArea && + Enumerable.SequenceEqual(Evaluations, other.Evaluations); + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Id); + hash.Add(LocalId); + hash.Add(ParcelId); + hash.Add(AgencyId); + hash.Add(Description); + hash.Add(Address); + hash.Add(Latitude); + hash.Add(Longitude); + hash.Add(BuildingConstructionTypeId); + hash.Add(BuildingConstructionType); + hash.Add(BuildingFloorCount); + hash.Add(BuildingPredominateUseId); + hash.Add(BuildingPredominateUse); + hash.Add(BuildingTenancy); + hash.Add(RentableArea); + hash.Add(Evaluations); + return hash.ToHashCode(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Models/Import/ParcelEvaluationModel.cs b/backend/api/Areas/Tools/Models/Import/ParcelEvaluationModel.cs new file mode 100644 index 0000000000..07ce5c37e2 --- /dev/null +++ b/backend/api/Areas/Tools/Models/Import/ParcelEvaluationModel.cs @@ -0,0 +1,54 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Areas.Tools.Models.Import +{ + public class ParcelEvaluationModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + public int PropertyId { get; set; } + + public int FiscalYear { get; set; } + + public float EstimatedValue { get; set; } + + public float AppraisedValue { get; set; } + + public float AssessedValue { get; set; } + + public float NetBookValue { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as ParcelEvaluationModel); + } + + public bool Equals([AllowNull] ParcelEvaluationModel other) + { + return other != null && + base.Equals(other) && + PropertyId == other.PropertyId && + FiscalYear == other.FiscalYear && + EstimatedValue == other.EstimatedValue && + AppraisedValue == other.AppraisedValue && + AssessedValue == other.AssessedValue && + NetBookValue == other.NetBookValue; + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(PropertyId); + hash.Add(FiscalYear); + hash.Add(EstimatedValue); + hash.Add(AppraisedValue); + hash.Add(AssessedValue); + hash.Add(NetBookValue); + return hash.ToHashCode(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Models/Import/ParcelModel.cs b/backend/api/Areas/Tools/Models/Import/ParcelModel.cs new file mode 100644 index 0000000000..e82701b070 --- /dev/null +++ b/backend/api/Areas/Tools/Models/Import/ParcelModel.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Pims.Api.Areas.Tools.Models.Import +{ + public class ParcelModel : Pims.Api.Models.BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public string PID { get; set; } + + public string PIN { get; set; } + + public int StatusId { get; set; } + + public string Status { get; set; } + + public int ClassificationId { get; set; } + + public string Classification { get; set; } + + public int AgencyId { get; set; } + + public string SubAgency { get; set; } + + public string Agency { get; set; } + + public AddressModel Address { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public float LandArea { get; set; } + + public string Description { get; set; } + + public string LandLegalDescription { get; set; } + + public bool IsSensitive { get; set; } + + public IEnumerable Evaluations { get; set; } = new List(); + public IEnumerable Buildings { get; set; } = new List(); + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as ParcelModel); + } + + public bool Equals([AllowNull] ParcelModel other) + { + return other != null && + base.Equals(other) && + Id == other.Id && + PID == other.PID && + PIN == other.PIN && + StatusId == other.StatusId && + Status == other.Status && + ClassificationId == other.ClassificationId && + Classification == other.Classification && + AgencyId == other.AgencyId && + SubAgency == other.SubAgency && + Agency == other.Agency && + EqualityComparer.Default.Equals(Address, other.Address) && + Latitude == other.Latitude && + Longitude == other.Longitude && + LandArea == other.LandArea && + Description == other.Description && + LandLegalDescription == other.LandLegalDescription && + Enumerable.SequenceEqual(Buildings, other.Buildings) && + Enumerable.SequenceEqual(Evaluations, other.Evaluations); + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(Id); + hash.Add(PID); + hash.Add(PIN); + hash.Add(StatusId); + hash.Add(Status); + hash.Add(ClassificationId); + hash.Add(Classification); + hash.Add(AgencyId); + hash.Add(SubAgency); + hash.Add(Agency); + hash.Add(Address); + hash.Add(Latitude); + hash.Add(Longitude); + hash.Add(LandArea); + hash.Add(Description); + hash.Add(LandLegalDescription); + hash.Add(Buildings); + hash.Add(Evaluations); + return hash.ToHashCode(); + } + + #endregion + } +} diff --git a/backend/api/Areas/Tools/Models/PropertyModel.cs b/backend/api/Areas/Tools/Models/Import/PropertyModel.cs similarity index 96% rename from backend/api/Areas/Tools/Models/PropertyModel.cs rename to backend/api/Areas/Tools/Models/Import/PropertyModel.cs index 5207a7460d..0e7477af2e 100644 --- a/backend/api/Areas/Tools/Models/PropertyModel.cs +++ b/backend/api/Areas/Tools/Models/Import/PropertyModel.cs @@ -1,4 +1,4 @@ -namespace Pims.Api.Areas.Tools.Models +namespace Pims.Api.Areas.Tools.Models.Import { public class PropertyModel { diff --git a/backend/api/Areas/Tools/Profiles/Import/AddressProfile.cs b/backend/api/Areas/Tools/Profiles/Import/AddressProfile.cs new file mode 100644 index 0000000000..b7304593fc --- /dev/null +++ b/backend/api/Areas/Tools/Profiles/Import/AddressProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Model = Pims.Api.Areas.Tools.Models.Import; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Areas.Tools.Profiles.Import +{ + public class AddressProfile : Profile + { + #region Constructors + public AddressProfile() + { + CreateMap() + .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City.Name)) + .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province.Name)) + .ForMember(dest => dest.Line1, opt => opt.MapFrom(src => src.Address1)) + .ForMember(dest => dest.Line2, opt => opt.MapFrom(src => src.Address2)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.City, opt => opt.Ignore()) + .ForMember(dest => dest.Province, opt => opt.Ignore()) + .ForMember(dest => dest.Address1, opt => opt.MapFrom(src => src.Line1)) + .ForMember(dest => dest.Address2, opt => opt.MapFrom(src => src.Line2)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Profiles/Import/BuildingEvaluationProfile.cs b/backend/api/Areas/Tools/Profiles/Import/BuildingEvaluationProfile.cs new file mode 100644 index 0000000000..c23cb17557 --- /dev/null +++ b/backend/api/Areas/Tools/Profiles/Import/BuildingEvaluationProfile.cs @@ -0,0 +1,27 @@ +using AutoMapper; +using Model = Pims.Api.Areas.Tools.Models.Import; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Areas.Tools.Profiles.Import +{ + public class BuildingEvaluationProfile : Profile + { + #region Constructors + public BuildingEvaluationProfile() + { + CreateMap() + .ForMember(dest => dest.PropertyId, opt => opt.MapFrom(src => src.BuildingId)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .ForMember(dest => dest.BuildingId, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Profiles/Import/ParcelBuildingProfile.cs b/backend/api/Areas/Tools/Profiles/Import/ParcelBuildingProfile.cs new file mode 100644 index 0000000000..4495e238c4 --- /dev/null +++ b/backend/api/Areas/Tools/Profiles/Import/ParcelBuildingProfile.cs @@ -0,0 +1,31 @@ +using AutoMapper; +using Model = Pims.Api.Areas.Tools.Models.Import; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Areas.Tools.Profiles.Import +{ + public class ParcelBuildingProfile : Profile + { + #region Constructors + public ParcelBuildingProfile() + { + CreateMap() + .ForMember(dest => dest.BuildingConstructionType, opt => opt.MapFrom(src => src.BuildingConstructionType.Name)) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.MapFrom(src => src.BuildingPredominateUse.Name)); + + CreateMap() + .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) + .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)) + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.ParcelId, opt => opt.MapFrom(src => src.ParcelId)) + .ForMember(dest => dest.Parcel, opt => opt.Ignore()) + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => src.AgencyId)) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Evaluations, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Profiles/Import/ParcelEvaluationProfile.cs b/backend/api/Areas/Tools/Profiles/Import/ParcelEvaluationProfile.cs new file mode 100644 index 0000000000..46c2c1d748 --- /dev/null +++ b/backend/api/Areas/Tools/Profiles/Import/ParcelEvaluationProfile.cs @@ -0,0 +1,27 @@ +using AutoMapper; +using Model = Pims.Api.Areas.Tools.Models.Import; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Areas.Tools.Profiles.Import +{ + public class ParcelEvaluationProfile : Profile + { + #region Constructors + public ParcelEvaluationProfile() + { + CreateMap() + .ForMember(dest => dest.PropertyId, opt => opt.MapFrom(src => src.ParcelId)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .ForMember(dest => dest.ParcelId, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Profiles/Import/ParcelProfile.cs b/backend/api/Areas/Tools/Profiles/Import/ParcelProfile.cs new file mode 100644 index 0000000000..e4347ae804 --- /dev/null +++ b/backend/api/Areas/Tools/Profiles/Import/ParcelProfile.cs @@ -0,0 +1,37 @@ +using AutoMapper; +using Model = Pims.Api.Areas.Tools.Models.Import; +using Entity = Pims.Dal.Entities; +using Pims.Api.Profiles.Converters; +using Pims.Api.Areas.Tools.Profiles.Import.Resolvers; + +namespace Pims.Api.Areas.Tools.Profiles.Import +{ + public class ParcelProfile : Profile + { + #region Constructors + public ParcelProfile() + { + CreateMap() + .ForMember(dest => dest.PID, opt => opt.MapFrom(src => src.ParcelIdentity)) + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.Name)) + .ForMember(dest => dest.Classification, opt => opt.MapFrom(src => src.Classification.Name)) + .ForMember(dest => dest.Agency, opt => opt.ConvertUsing(new ParcelAgencyConverter())) + .ForMember(dest => dest.SubAgency, opt => opt.MapFrom()) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.PID, opt => opt.ConvertUsing(new ParcelIdConverter(), src => src.PID)) + .ForMember(dest => dest.ParcelIdentity, opt => opt.Ignore()) + .ForMember(dest => dest.Status, opt => opt.Ignore()) + .ForMember(dest => dest.Classification, opt => opt.Ignore()) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)) + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Buildings, opt => opt.Ignore()) + .ForMember(dest => dest.Evaluations, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Areas/Tools/Profiles/Import/Resolvers/ParcelSubAgencyResolver.cs b/backend/api/Areas/Tools/Profiles/Import/Resolvers/ParcelSubAgencyResolver.cs new file mode 100644 index 0000000000..9983f94718 --- /dev/null +++ b/backend/api/Areas/Tools/Profiles/Import/Resolvers/ParcelSubAgencyResolver.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Areas.Tools.Models.Import; + +namespace Pims.Api.Areas.Tools.Profiles.Import.Resolvers +{ + public class ParcelSubAgencyResolver : IValueResolver + { + public string Resolve(Entity.Parcel source, Model.ParcelModel destination, string destMember, ResolutionContext context) + { + return source?.Agency?.ParentId == null ? null : source.Agency.Code; + } + } +} diff --git a/backend/api/Controllers/AuthController.cs b/backend/api/Controllers/AuthController.cs index 37600b1641..955e136afd 100644 --- a/backend/api/Controllers/AuthController.cs +++ b/backend/api/Controllers/AuthController.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Model = Pims.Api.Models.Auth; namespace Pims.Api.Controllers { @@ -57,7 +58,7 @@ public AuthController(ILogger logger, IOptionsMonitor), 200)] + [ProducesResponseType(typeof(IEnumerable), 200)] [SwaggerOperation(Tags = new[] { "auth" })] public IActionResult Claims() { - return new JsonResult(User.Claims.Select(c => new Models.Auth.ClaimModel(c.Type, c.Value))); + return new JsonResult(User.Claims.Select(c => new Model.ClaimModel(c.Type, c.Value))); } #endregion } diff --git a/backend/api/Controllers/HealthController.cs b/backend/api/Controllers/HealthController.cs index ffcead81e3..08a578e992 100644 --- a/backend/api/Controllers/HealthController.cs +++ b/backend/api/Controllers/HealthController.cs @@ -1,8 +1,8 @@ -using System.Reflection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Swashbuckle.AspNetCore.Annotations; +using Model = Pims.Api.Models.Health; namespace Pims.Api.Controllers { @@ -40,11 +40,11 @@ public HealthController(IWebHostEnvironment environment, ILogger [HttpGet("env")] [Produces("application/json")] - [ProducesResponseType(typeof(Models.Health.EnvModel), 200)] + [ProducesResponseType(typeof(Model.EnvModel), 200)] [SwaggerOperation(Tags = new[] { "health" })] public IActionResult Environment() { - return new JsonResult(new Models.Health.EnvModel(_environment)); + return new JsonResult(new Model.EnvModel(_environment)); } #endregion } diff --git a/backend/api/Controllers/LookupController.cs b/backend/api/Controllers/LookupController.cs index 44834a03e5..49e1eb0d76 100644 --- a/backend/api/Controllers/LookupController.cs +++ b/backend/api/Controllers/LookupController.cs @@ -52,7 +52,7 @@ public LookupController(ILogger logger, IPimsService pimsServi [SwaggerOperation(Tags = new[] { "lookup" })] public IActionResult GetAgencies() { - var agencyCodes = _mapper.Map(_pimsService.Lookup.GetAgenciesNoTracking()); + var agencyCodes = _mapper.Map(_pimsService.Lookup.GetAgencies()); return new JsonResult(agencyCodes.ToArray()); } @@ -66,7 +66,7 @@ public IActionResult GetAgencies() [SwaggerOperation(Tags = new[] { "lookup" })] public IActionResult GetRoles() { - var roleCodes = _mapper.Map(_pimsService.Lookup.GetRolesNoTracking()); + var roleCodes = _mapper.Map(_pimsService.Lookup.GetRoles()); return new JsonResult(roleCodes.ToArray()); } @@ -80,7 +80,7 @@ public IActionResult GetRoles() [SwaggerOperation(Tags = new[] { "lookup" })] public IActionResult GetPropertyClassifications() { - var propertyClassificationCodes = _mapper.Map(_pimsService.Lookup.GetPropertyClassificationsNoTracking()); + var propertyClassificationCodes = _mapper.Map(_pimsService.Lookup.GetPropertyClassifications()); return new JsonResult(propertyClassificationCodes.ToArray()); } @@ -94,9 +94,9 @@ public IActionResult GetPropertyClassifications() [SwaggerOperation(Tags = new[] { "lookup" })] public IActionResult GetAll() { - var agencyCodes = _mapper.Map(_pimsService.Lookup.GetAgenciesNoTracking()); - var propertyClassificationCodes = _mapper.Map(_pimsService.Lookup.GetPropertyClassificationsNoTracking()); - var roleCodes = _mapper.Map(_pimsService.Lookup.GetRolesNoTracking()); + var agencyCodes = _mapper.Map(_pimsService.Lookup.GetAgencies()); + var propertyClassificationCodes = _mapper.Map(_pimsService.Lookup.GetPropertyClassifications()); + var roleCodes = _mapper.Map(_pimsService.Lookup.GetRoles()); return new JsonResult(agencyCodes.Concat(propertyClassificationCodes).Concat(roleCodes).ToArray()); } diff --git a/backend/api/Controllers/ParcelController.cs b/backend/api/Controllers/ParcelController.cs index c0073a4752..70e2b1b17c 100644 --- a/backend/api/Controllers/ParcelController.cs +++ b/backend/api/Controllers/ParcelController.cs @@ -4,12 +4,11 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Model = Pims.Api.Models; +using Model = Pims.Api.Models.Parcel; using Pims.Api.Helpers.Extensions; using Pims.Api.Policies; using Pims.Dal; using Pims.Dal.Entities.Models; -using Pims.Dal.Helpers.Extensions; using Pims.Dal.Security; using System; using System.Collections.Generic; @@ -57,7 +56,7 @@ public ParcelController(ILogger logger, IPimsService pimsServi [HttpGet] [HasPermission(Permissions.PropertyView)] [Produces("application/json")] - [ProducesResponseType(typeof(IEnumerable), 200)] + [ProducesResponseType(typeof(IEnumerable), 200)] [SwaggerOperation(Tags = new[] { "parcel" })] public IActionResult GetParcels() { @@ -74,15 +73,16 @@ public IActionResult GetParcels() [HttpPost("filter")] [HasPermission(Permissions.PropertyView)] [Produces("application/json")] - [ProducesResponseType(typeof(IEnumerable), 200)] + [ProducesResponseType(typeof(IEnumerable), 200)] [SwaggerOperation(Tags = new[] { "parcel" })] public IActionResult GetParcels([FromBody]ParcelFilter filter) { filter.ThrowBadRequestIfNull($"The request must include a filter."); if (!filter.ValidFilter()) throw new BadRequestException("Property filter must contain valid values."); - var parcels = _pimsService.Parcel.GetNoTracking(filter); - return new JsonResult(_mapper.Map(parcels)); + var parcels = _pimsService.Parcel.Get(filter); + var result = _mapper.Map(parcels); + return new JsonResult(result); } /// @@ -97,7 +97,7 @@ public IActionResult GetParcels([FromBody]ParcelFilter filter) [SwaggerOperation(Tags = new[] { "parcel" })] public IActionResult GetParcel(int id) { - var entity = _pimsService.Parcel.GetNoTracking(id); + var entity = _pimsService.Parcel.Get(id); var parcel = _mapper.Map(entity); return new JsonResult(parcel); @@ -116,16 +116,17 @@ public IActionResult GetParcel(int id) public IActionResult AddParcel([FromBody] Model.ParcelModel model) { var entity = _mapper.Map(model); - var userId = this.User.GetUserId(); _pimsService.Parcel.Add(entity); var parcel = _mapper.Map(entity); - return new CreatedAtActionResult(nameof(GetParcel), nameof(ParcelController), new { id = parcel.Id }, parcel); + + return CreatedAtAction(nameof(GetParcel), new { id = parcel.Id }, parcel); } /// /// Update the specified parcel in the datasource if the user is allowed. /// + /// /// /// [HttpPut("{id}")] @@ -133,7 +134,7 @@ public IActionResult AddParcel([FromBody] Model.ParcelModel model) [Produces("application/json")] [ProducesResponseType(typeof(Model.ParcelModel), 200)] [SwaggerOperation(Tags = new[] { "parcel" })] - public IActionResult UpdateParcel([FromBody] Model.ParcelModel model) + public IActionResult UpdateParcel(int id, [FromBody] Model.ParcelModel model) { var entity = _mapper.Map(model); @@ -154,7 +155,7 @@ public IActionResult UpdateParcel([FromBody] Model.ParcelModel model) [Produces("application/json")] [ProducesResponseType(typeof(Model.ParcelModel), 200)] [SwaggerOperation(Tags = new[] { "parcel" })] - public IActionResult DeleteParcel(Guid id, [FromBody] Model.ParcelModel model) + public IActionResult DeleteParcel(int id, [FromBody] Model.ParcelModel model) { var entity = _mapper.Map(model); diff --git a/backend/api/Controllers/PropertyController.cs b/backend/api/Controllers/PropertyController.cs index f1e7a86db1..29b10e56c2 100644 --- a/backend/api/Controllers/PropertyController.cs +++ b/backend/api/Controllers/PropertyController.cs @@ -81,9 +81,9 @@ public IActionResult GetProperties([FromBody]PropertyFilterModel filter) var properties = new List(); if (filter.IncludeParcels) - properties.AddRange(_mapper.Map(_pimsService.Parcel.GetNoTracking((ParcelFilter)filter))); + properties.AddRange(_mapper.Map(_pimsService.Parcel.Get((ParcelFilter)filter))); if (filter.IncludeBuildings) - properties.AddRange(_mapper.Map(_pimsService.Building.GetNoTracking((BuildingFilter)filter))); + properties.AddRange(_mapper.Map(_pimsService.Building.Get((BuildingFilter)filter))); return new JsonResult(properties.ToArray()); } #endregion diff --git a/backend/api/Controllers/UserController.cs b/backend/api/Controllers/UserController.cs index beebdde897..863131e6ca 100644 --- a/backend/api/Controllers/UserController.cs +++ b/backend/api/Controllers/UserController.cs @@ -1,12 +1,12 @@ using AutoMapper; using Entity = Pims.Dal.Entities; using KModel = Pims.Keycloak.Models; +using Model = Pims.Api.Models.User; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Pims.Api.Helpers.Extensions; -using Pims.Api.Models; using Pims.Dal.Services; using Pims.Keycloak; using System.Linq; @@ -84,9 +84,9 @@ public async Task UserInfo() /// [HttpPost("access/request")] [Produces("application/json")] - [ProducesResponseType(typeof(AccessRequestModel), 200)] + [ProducesResponseType(typeof(Model.AccessRequestModel), 200)] [SwaggerOperation(Tags = new[] { "user" })] - public IActionResult AddAccessRequest([FromBody] AccessRequestModel accessRequestModel) + public IActionResult AddAccessRequest([FromBody] Model.AccessRequestModel accessRequestModel) { if (accessRequestModel == null || accessRequestModel.Agencies == null || accessRequestModel.Roles == null) { @@ -102,7 +102,7 @@ public IActionResult AddAccessRequest([FromBody] AccessRequestModel accessReques } var entity = _mapper.Map(accessRequestModel); _userService.AddAccessRequest(entity); - return new JsonResult(_mapper.Map(entity)); // TODO: Should return 201. + return new JsonResult(_mapper.Map(entity)); // TODO: Should return 201. } #endregion } diff --git a/backend/api/Profiles/Extensions/MappingExpressionExtensions.cs b/backend/api/Helpers/Extensions/MappingExpressionExtensions.cs similarity index 95% rename from backend/api/Profiles/Extensions/MappingExpressionExtensions.cs rename to backend/api/Helpers/Extensions/MappingExpressionExtensions.cs index a3ffa8ba2a..01b2f80983 100644 --- a/backend/api/Profiles/Extensions/MappingExpressionExtensions.cs +++ b/backend/api/Helpers/Extensions/MappingExpressionExtensions.cs @@ -1,6 +1,6 @@ using AutoMapper; -namespace Pims.Api.Profiles.Extensions +namespace Pims.Api.Helpers.Extensions { /// /// MappingExpressionExtensions static class, provides extension methods for automapper IMappingExpression. diff --git a/backend/api/Models/Building/AddressModel.cs b/backend/api/Models/Building/AddressModel.cs new file mode 100644 index 0000000000..8e4d8bf2ae --- /dev/null +++ b/backend/api/Models/Building/AddressModel.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Models.Building +{ + public class AddressModel : BaseModel, IEquatable + { + #region Properties + public int Id { get; set; } + + public string Line1 { get; set; } + + public string Line2 { get; set; } + + public int CityId { get; set; } + + public string City { get; set; } + + public string ProvinceId { get; set; } + + public string Province { get; set; } + + public string Postal { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as AddressModel); + } + + public bool Equals([AllowNull] AddressModel other) + { + return other != null && + Id == other.Id && + Line1 == other.Line1 && + Line2 == other.Line2 && + CityId == other.CityId && + City == other.City && + ProvinceId == other.ProvinceId && + Province == other.Province && + Postal == other.Postal; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Line1, Line2, CityId, City, ProvinceId, Province, Postal); + } + #endregion + } +} diff --git a/backend/api/Models/Building/BuildingEvaluationModel.cs b/backend/api/Models/Building/BuildingEvaluationModel.cs new file mode 100644 index 0000000000..33552fa6d4 --- /dev/null +++ b/backend/api/Models/Building/BuildingEvaluationModel.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Models.Building +{ + public class BuildingEvaluationModel : BaseModel, IEquatable + { + #region Properties + public int PropertyId { get; set; } + + public int FiscalYear { get; set; } + + public float EstimatedValue { get; set; } + + public float AppraisedValue { get; set; } + + public float AssessedValue { get; set; } + + public float NetBookValue { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as BuildingEvaluationModel); + } + + public bool Equals([AllowNull] BuildingEvaluationModel other) + { + return other != null && + base.Equals(other) && + PropertyId == other.PropertyId && + FiscalYear == other.FiscalYear && + EstimatedValue == other.EstimatedValue && + AppraisedValue == other.AppraisedValue && + AssessedValue == other.AssessedValue && + NetBookValue == other.NetBookValue; + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(PropertyId); + hash.Add(FiscalYear); + hash.Add(EstimatedValue); + hash.Add(AppraisedValue); + hash.Add(AssessedValue); + hash.Add(NetBookValue); + return hash.ToHashCode(); + } + + #endregion + } +} diff --git a/backend/api/Models/BuildingModel.cs b/backend/api/Models/Building/BuildingModel.cs similarity index 94% rename from backend/api/Models/BuildingModel.cs rename to backend/api/Models/Building/BuildingModel.cs index 954cfeaa8d..6d8a817b15 100644 --- a/backend/api/Models/BuildingModel.cs +++ b/backend/api/Models/Building/BuildingModel.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace Pims.Api.Models +namespace Pims.Api.Models.Building { public class BuildingModel : BaseModel, IEquatable { @@ -36,7 +36,7 @@ public class BuildingModel : BaseModel, IEquatable public float RentableArea { get; set; } - public IEnumerable Evaluations { get; set; } = new List(); + public IEnumerable Evaluations { get; set; } = new List(); public override bool Equals(object obj) { diff --git a/backend/api/Models/Parts/BuildingModel.cs b/backend/api/Models/Building/PartialBuildingModel.cs similarity index 77% rename from backend/api/Models/Parts/BuildingModel.cs rename to backend/api/Models/Building/PartialBuildingModel.cs index d96f3346ab..80e0af33ac 100644 --- a/backend/api/Models/Parts/BuildingModel.cs +++ b/backend/api/Models/Building/PartialBuildingModel.cs @@ -3,7 +3,7 @@ namespace Pims.Api.Models.Parts { - public class BuildingModel : BaseModel, IEquatable + public class PartialBuildingModel : BaseModel, IEquatable { #region Properties public int Id { get; set; } @@ -15,13 +15,15 @@ public class BuildingModel : BaseModel, IEquatable public double Latitude { get; set; } public double Longitude { get; set; } + #endregion + #region Methods public override bool Equals(object obj) { - return Equals(obj as BuildingModel); + return Equals(obj as PartialBuildingModel); } - public bool Equals([AllowNull] BuildingModel other) + public bool Equals([AllowNull] PartialBuildingModel other) { return other != null && Id == other.Id && diff --git a/backend/api/Models/AddressModel.cs b/backend/api/Models/Parcel/AddressModel.cs similarity index 94% rename from backend/api/Models/AddressModel.cs rename to backend/api/Models/Parcel/AddressModel.cs index 8f36964b29..5fcb6e18d6 100644 --- a/backend/api/Models/AddressModel.cs +++ b/backend/api/Models/Parcel/AddressModel.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models +namespace Pims.Api.Models.Parcel { public class AddressModel : BaseModel, IEquatable { @@ -21,7 +21,9 @@ public class AddressModel : BaseModel, IEquatable public string Province { get; set; } public string Postal { get; set; } + #endregion + #region Methods public override bool Equals(object obj) { return Equals(obj as AddressModel); diff --git a/backend/api/Models/Parcel/BuildingEvaluationModel.cs b/backend/api/Models/Parcel/BuildingEvaluationModel.cs new file mode 100644 index 0000000000..87b7617ca5 --- /dev/null +++ b/backend/api/Models/Parcel/BuildingEvaluationModel.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Api.Models.Parcel +{ + public class BuildingEvaluationModel : BaseModel, IEquatable + { + #region Properties + public int PropertyId { get; set; } + + public int FiscalYear { get; set; } + + public float EstimatedValue { get; set; } + + public float AppraisedValue { get; set; } + + public float AssessedValue { get; set; } + + public float NetBookValue { get; set; } + #endregion + + #region Methods + public override bool Equals(object obj) + { + return Equals(obj as BuildingEvaluationModel); + } + + public bool Equals([AllowNull] BuildingEvaluationModel other) + { + return other != null && + base.Equals(other) && + PropertyId == other.PropertyId && + FiscalYear == other.FiscalYear && + EstimatedValue == other.EstimatedValue && + AppraisedValue == other.AppraisedValue && + AssessedValue == other.AssessedValue && + NetBookValue == other.NetBookValue; + } + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + hash.Add(PropertyId); + hash.Add(FiscalYear); + hash.Add(EstimatedValue); + hash.Add(AppraisedValue); + hash.Add(AssessedValue); + hash.Add(NetBookValue); + return hash.ToHashCode(); + } + + #endregion + } +} diff --git a/backend/api/Models/Parts/ParcelBuildingModel.cs b/backend/api/Models/Parcel/ParcelBuildingModel.cs similarity index 92% rename from backend/api/Models/Parts/ParcelBuildingModel.cs rename to backend/api/Models/Parcel/ParcelBuildingModel.cs index daac319f1e..e41ad5382b 100644 --- a/backend/api/Models/Parts/ParcelBuildingModel.cs +++ b/backend/api/Models/Parcel/ParcelBuildingModel.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace Pims.Api.Models.Parts +namespace Pims.Api.Models.Parcel { public class ParcelBuildingModel : BaseModel, IEquatable { @@ -38,8 +38,12 @@ public class ParcelBuildingModel : BaseModel, IEquatable public float RentableArea { get; set; } - public IEnumerable Evaluations { get; set; } = new List(); + public bool IsSensitive { get; set; } + public IEnumerable Evaluations { get; set; } = new List(); + #endregion + + #region Methods public override bool Equals(object obj) { return Equals(obj as ParcelBuildingModel); diff --git a/backend/api/Models/EvaluationModel.cs b/backend/api/Models/Parcel/ParcelEvaluationModel.cs similarity index 72% rename from backend/api/Models/EvaluationModel.cs rename to backend/api/Models/Parcel/ParcelEvaluationModel.cs index da294038bf..59f751736b 100644 --- a/backend/api/Models/EvaluationModel.cs +++ b/backend/api/Models/Parcel/ParcelEvaluationModel.cs @@ -1,9 +1,9 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models +namespace Pims.Api.Models.Parcel { - public class EvaluationModel : BaseModel, IEquatable + public class ParcelEvaluationModel : BaseModel, IEquatable { #region Properties public int PropertyId { get; set; } @@ -12,22 +12,27 @@ public class EvaluationModel : BaseModel, IEquatable public float EstimatedValue { get; set; } + public float AppraisedValue { get; set; } + public float AssessedValue { get; set; } public float NetBookValue { get; set; } + #endregion + #region Methods public override bool Equals(object obj) { - return Equals(obj as EvaluationModel); + return Equals(obj as ParcelEvaluationModel); } - public bool Equals([AllowNull] EvaluationModel other) + public bool Equals([AllowNull] ParcelEvaluationModel other) { return other != null && base.Equals(other) && PropertyId == other.PropertyId && FiscalYear == other.FiscalYear && EstimatedValue == other.EstimatedValue && + AppraisedValue == other.AppraisedValue && AssessedValue == other.AssessedValue && NetBookValue == other.NetBookValue; } @@ -39,6 +44,7 @@ public override int GetHashCode() hash.Add(PropertyId); hash.Add(FiscalYear); hash.Add(EstimatedValue); + hash.Add(AppraisedValue); hash.Add(AssessedValue); hash.Add(NetBookValue); return hash.ToHashCode(); diff --git a/backend/api/Models/ParcelModel.cs b/backend/api/Models/Parcel/ParcelModel.cs similarity index 90% rename from backend/api/Models/ParcelModel.cs rename to backend/api/Models/Parcel/ParcelModel.cs index da050262c2..90c71fcf60 100644 --- a/backend/api/Models/ParcelModel.cs +++ b/backend/api/Models/Parcel/ParcelModel.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace Pims.Api.Models +namespace Pims.Api.Models.Parcel { public class ParcelModel : BaseModel, IEquatable { @@ -40,8 +40,10 @@ public class ParcelModel : BaseModel, IEquatable public string LandLegalDescription { get; set; } - public IEnumerable Evaluations { get; set; } = new List(); - public IEnumerable Buildings { get; set; } = new List(); + public bool IsSensitive { get; set; } + + public IEnumerable Evaluations { get; set; } = new List(); + public IEnumerable Buildings { get; set; } = new List(); public override bool Equals(object obj) { diff --git a/backend/api/Models/Parts/ParcelModel.cs b/backend/api/Models/Parcel/PartialParcelModel.cs similarity index 80% rename from backend/api/Models/Parts/ParcelModel.cs rename to backend/api/Models/Parcel/PartialParcelModel.cs index 356ca0b61e..1eb42831de 100644 --- a/backend/api/Models/Parts/ParcelModel.cs +++ b/backend/api/Models/Parcel/PartialParcelModel.cs @@ -1,9 +1,9 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models.Parts +namespace Pims.Api.Models.Parcel { - public class ParcelModel : BaseModel, IEquatable + public class PartialParcelModel : BaseModel, IEquatable { #region Properties public int Id { get; set; } @@ -21,13 +21,15 @@ public class ParcelModel : BaseModel, IEquatable public double Longitude { get; set; } public string Description { get; set; } + #endregion + #region Methods public override bool Equals(object obj) { - return Equals(obj as ParcelModel); + return Equals(obj as PartialParcelModel); } - public bool Equals([AllowNull] ParcelModel other) + public bool Equals([AllowNull] PartialParcelModel other) { return other != null && Id == other.Id && diff --git a/backend/api/Models/Property/PropertyModel.cs b/backend/api/Models/Property/PropertyModel.cs index 82f3ebae03..ee9d448c16 100644 --- a/backend/api/Models/Property/PropertyModel.cs +++ b/backend/api/Models/Property/PropertyModel.cs @@ -35,7 +35,7 @@ public class PropertyModel : IEquatable #region Methods public override bool Equals(object obj) { - return Equals(obj as ParcelModel); + return Equals(obj as PropertyModel); } public bool Equals([AllowNull] PropertyModel other) diff --git a/backend/api/Models/AccessRequestModel.cs b/backend/api/Models/User/AccessRequestModel.cs similarity index 73% rename from backend/api/Models/AccessRequestModel.cs rename to backend/api/Models/User/AccessRequestModel.cs index eeecf7e776..1160e44ecf 100644 --- a/backend/api/Models/AccessRequestModel.cs +++ b/backend/api/Models/User/AccessRequestModel.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Pims.Api.Areas.Admin.Models; -using Pims.Api.Models.Parts; -namespace Pims.Api.Models +namespace Pims.Api.Models.User { public class AccessRequestModel : BaseModel { @@ -15,9 +12,6 @@ public class AccessRequestModel : BaseModel public bool? IsGranted { get; set; } public IEnumerable Roles { get; set; } public bool IsDisabled { get; set; } - - public AccessRequestModel() { } - #endregion } } diff --git a/backend/api/Models/Parts/AccessRequestRoleModel.cs b/backend/api/Models/User/AccessRequestRoleModel.cs similarity index 97% rename from backend/api/Models/Parts/AccessRequestRoleModel.cs rename to backend/api/Models/User/AccessRequestRoleModel.cs index ce129be29e..e9e26280a7 100644 --- a/backend/api/Models/Parts/AccessRequestRoleModel.cs +++ b/backend/api/Models/User/AccessRequestRoleModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models.Parts +namespace Pims.Api.Models.User { /// /// AccessRequestRoleModel class, provides a model that represents a role attached to an access request. diff --git a/backend/api/Models/Parts/AccessRequestUserModel.cs b/backend/api/Models/User/AccessRequestUserModel.cs similarity index 96% rename from backend/api/Models/Parts/AccessRequestUserModel.cs rename to backend/api/Models/User/AccessRequestUserModel.cs index 0497a5c306..182ee486bc 100644 --- a/backend/api/Models/Parts/AccessRequestUserModel.cs +++ b/backend/api/Models/User/AccessRequestUserModel.cs @@ -1,8 +1,7 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models.Parts +namespace Pims.Api.Models.User { /// /// AccessRequestUserModel class, provides a model that represents a user attached to an access request. diff --git a/backend/api/Models/AgencyModel.cs b/backend/api/Models/User/AgencyModel.cs similarity index 86% rename from backend/api/Models/AgencyModel.cs rename to backend/api/Models/User/AgencyModel.cs index 76625f3701..bff0f79373 100644 --- a/backend/api/Models/AgencyModel.cs +++ b/backend/api/Models/User/AgencyModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models +namespace Pims.Api.Models.User { public class AgencyModel : CodeModel, IEquatable { @@ -10,9 +10,10 @@ public class AgencyModel : CodeModel, IEquatable public string Description { get; set; } public AgencyModel Parent { get; set; } public ICollection Children { get; } = new List(); - public ICollection Parcels { get; } = new List(); public ICollection Users { get; } = new List(); + #endregion + #region Methods public override bool Equals(object obj) { return Equals(obj as AgencyModel); @@ -27,7 +28,7 @@ public bool Equals([AllowNull] AgencyModel other) public override int GetHashCode() { - return HashCode.Combine(base.GetHashCode(), Description, Parent, Children, Parcels, Users); + return HashCode.Combine(base.GetHashCode(), Description, Parent, Children, Users); } #endregion } diff --git a/backend/api/Models/RoleModel.cs b/backend/api/Models/User/RoleModel.cs similarity index 96% rename from backend/api/Models/RoleModel.cs rename to backend/api/Models/User/RoleModel.cs index 095fbb5cad..905607a80a 100644 --- a/backend/api/Models/RoleModel.cs +++ b/backend/api/Models/User/RoleModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models +namespace Pims.Api.Models.User { public class RoleModel : CodeModel, IEquatable { diff --git a/backend/api/Models/UserModel.cs b/backend/api/Models/User/UserModel.cs similarity index 94% rename from backend/api/Models/UserModel.cs rename to backend/api/Models/User/UserModel.cs index 9c09af7c38..5310904f0d 100644 --- a/backend/api/Models/UserModel.cs +++ b/backend/api/Models/User/UserModel.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace Pims.Api.Models +namespace Pims.Api.Models.User { - public class UserModel : BaseModel, IEquatable + public class UserModel : Pims.Api.Models.BaseModel, IEquatable { #region Properties public Guid Id { get; set; } diff --git a/backend/api/Pims.Api.csproj b/backend/api/Pims.Api.csproj index c236c98963..2abf08531e 100644 --- a/backend/api/Pims.Api.csproj +++ b/backend/api/Pims.Api.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -9,6 +9,12 @@ true $(NoWarn);1591 + + + + + + @@ -44,4 +50,7 @@ + + + diff --git a/backend/api/Profiles/AgencyProfile.cs b/backend/api/Profiles/AgencyProfile.cs deleted file mode 100644 index 369fb7c420..0000000000 --- a/backend/api/Profiles/AgencyProfile.cs +++ /dev/null @@ -1,20 +0,0 @@ -using AutoMapper; -using Pims.Api.Models; -using Entity = Pims.Dal.Entities; - -namespace Pims.Api.Helpers.Profiles -{ - public class AgencyProfile : Profile - { - #region Constructors - public AgencyProfile() - { - CreateMap() - .IncludeBase(); - - CreateMap() - .IncludeBase(); - } - #endregion - } -} diff --git a/backend/api/Profiles/BaseProfile.cs b/backend/api/Profiles/BaseProfile.cs index c1fb4fdb86..6bab4e6dc1 100644 --- a/backend/api/Profiles/BaseProfile.cs +++ b/backend/api/Profiles/BaseProfile.cs @@ -3,13 +3,15 @@ using Pims.Api.Models; using Entity = Pims.Dal.Entities; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles { public class BaseProfile : Profile { #region Constructors public BaseProfile() { + CreateMap(); + CreateMap() .IncludeAllDerived() .ForMember(dest => dest.RowVersion, opt => opt.MapFrom(src => Convert.ToBase64String(src.RowVersion))); diff --git a/backend/api/Profiles/Building/AddressProfile.cs b/backend/api/Profiles/Building/AddressProfile.cs new file mode 100644 index 0000000000..51013f855f --- /dev/null +++ b/backend/api/Profiles/Building/AddressProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Building; + +namespace Pims.Api.Profiles.Building +{ + public class AddressProfile : Profile + { + #region Constructors + public AddressProfile() + { + CreateMap() + .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City.Name)) + .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province.Name)) + .ForMember(dest => dest.Line1, opt => opt.MapFrom(src => src.Address1)) + .ForMember(dest => dest.Line2, opt => opt.MapFrom(src => src.Address2)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.City, opt => opt.Ignore()) + .ForMember(dest => dest.Province, opt => opt.Ignore()) + .ForMember(dest => dest.Address1, opt => opt.MapFrom(src => src.Line1)) + .ForMember(dest => dest.Address2, opt => opt.MapFrom(src => src.Line2)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/Building/BuildingEvaluationProfile.cs b/backend/api/Profiles/Building/BuildingEvaluationProfile.cs new file mode 100644 index 0000000000..f7ef8c4b61 --- /dev/null +++ b/backend/api/Profiles/Building/BuildingEvaluationProfile.cs @@ -0,0 +1,27 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Building; + +namespace Pims.Api.Profiles.Building +{ + public class BuildingEvaluationProfile : Profile + { + #region Constructors + public BuildingEvaluationProfile() + { + CreateMap() + .ForMember(dest => dest.PropertyId, opt => opt.MapFrom(src => src.BuildingId)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .ForMember(dest => dest.BuildingId, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/Building/BuildingProfile.cs b/backend/api/Profiles/Building/BuildingProfile.cs new file mode 100644 index 0000000000..a945630345 --- /dev/null +++ b/backend/api/Profiles/Building/BuildingProfile.cs @@ -0,0 +1,27 @@ +using AutoMapper; +using Model = Pims.Api.Models.Building; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Profiles.Building +{ + public class BuildingProfile : Profile + { + #region Constructors + public BuildingProfile() + { + CreateMap() + .ForMember(dest => dest.BuildingConstructionType, opt => opt.MapFrom(src => src.BuildingConstructionType.Name)) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.MapFrom(src => src.BuildingPredominateUse.Name)); + + CreateMap() // TODO: Map evaluation + .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) + .ForMember(dest => dest.Parcel, opt => opt.Ignore()) + .ForMember(dest => dest.AgencyId, opt => opt.Ignore()) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.IsSensitive, opt => opt.Ignore()) + .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)); + } + #endregion + } +} diff --git a/backend/api/Profiles/BuildingProfile.cs b/backend/api/Profiles/BuildingProfile.cs deleted file mode 100644 index 9ff77ac1a1..0000000000 --- a/backend/api/Profiles/BuildingProfile.cs +++ /dev/null @@ -1,60 +0,0 @@ -using AutoMapper; -using Pims.Api.Models; -using Entity = Pims.Dal.Entities; - -namespace Pims.Api.Helpers.Profiles -{ - public class BuildingProfile : Profile - { - #region Constructors - public BuildingProfile() - { - CreateMap(); - - CreateMap() - .ForMember(dest => dest.ParcelId, opt => opt.Ignore()) - .ForMember(dest => dest.Parcel, opt => opt.Ignore()) - .ForMember(dest => dest.AddressId, opt => opt.Ignore()) - .ForMember(dest => dest.Address, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingConstructionTypeId, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingFloorCount, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingPredominateUseId, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingTenancy, opt => opt.Ignore()) - .ForMember(dest => dest.RentableArea, opt => opt.Ignore()) - .ForMember(dest => dest.AgencyId, opt => opt.Ignore()) - .ForMember(dest => dest.Agency, opt => opt.Ignore()) - .ForMember(dest => dest.IsSensitive, opt => opt.Ignore()) - .IncludeBase(); - - CreateMap() - .ForMember(dest => dest.BuildingConstructionType, opt => opt.MapFrom(src => src.BuildingConstructionType.Name)) - .ForMember(dest => dest.BuildingPredominateUse, opt => opt.MapFrom(src => src.BuildingPredominateUse.Name)); - - CreateMap() - .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) - .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)) - .ForMember(dest => dest.Parcel, opt => opt.Ignore()) - .ForMember(dest => dest.Agency, opt => opt.Ignore()) - .ForMember(dest => dest.IsSensitive, opt => opt.Ignore()) - .ForMember(dest => dest.Evaluations, opt => opt.MapFrom(src => src.Evaluations)) - .IncludeBase(); - - CreateMap() - .ForMember(dest => dest.BuildingConstructionType, opt => opt.MapFrom(src => src.BuildingConstructionType.Name)) - .ForMember(dest => dest.BuildingPredominateUse, opt => opt.MapFrom(src => src.BuildingPredominateUse.Name)); - - CreateMap() // TODO: Map evaluation - .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) - .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) - .ForMember(dest => dest.Parcel, opt => opt.Ignore()) - .ForMember(dest => dest.AgencyId, opt => opt.Ignore()) - .ForMember(dest => dest.Agency, opt => opt.Ignore()) - .ForMember(dest => dest.IsSensitive, opt => opt.Ignore()) - .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)); - } - #endregion - } -} diff --git a/backend/api/Profiles/CodeProfile.cs b/backend/api/Profiles/CodeProfile.cs index cb7a1d7dba..7bcc95646c 100644 --- a/backend/api/Profiles/CodeProfile.cs +++ b/backend/api/Profiles/CodeProfile.cs @@ -1,21 +1,21 @@ using AutoMapper; -using Pims.Api.Models; +using Model = Pims.Api.Models; using Entity = Pims.Dal.Entities; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles { public class CodeProfile : Profile { #region Constructors public CodeProfile() { - CreateMap() + CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.GetType().Name)) - .IncludeBase() + .IncludeBase() .IncludeAllDerived(); - CreateMap() - .IncludeBase() + CreateMap() + .IncludeBase() .IncludeAllDerived(); } #endregion diff --git a/backend/api/Profiles/Converters/ParcelAgencyConverter.cs b/backend/api/Profiles/Converters/ParcelAgencyConverter.cs index c4be60ad62..360d484905 100644 --- a/backend/api/Profiles/Converters/ParcelAgencyConverter.cs +++ b/backend/api/Profiles/Converters/ParcelAgencyConverter.cs @@ -1,7 +1,7 @@ using AutoMapper; using Entity = Pims.Dal.Entities; -namespace Pims.Api.Helpers.Profiles.Converters +namespace Pims.Api.Profiles.Converters { public class ParcelAgencyConverter : IValueConverter { diff --git a/backend/api/Profiles/Converters/ParcelIdConverter.cs b/backend/api/Profiles/Converters/ParcelIdConverter.cs index d0eaaf9282..d884049a22 100644 --- a/backend/api/Profiles/Converters/ParcelIdConverter.cs +++ b/backend/api/Profiles/Converters/ParcelIdConverter.cs @@ -1,6 +1,6 @@ using AutoMapper; -namespace Pims.Api.Helpers.Profiles.Converters +namespace Pims.Api.Profiles.Converters { public class ParcelIdConverter : IValueConverter { diff --git a/backend/api/Profiles/Converters/ParcelSubAgencyConverter.cs b/backend/api/Profiles/Converters/ParcelSubAgencyConverter.cs index 5d486faf8e..7f649839a2 100644 --- a/backend/api/Profiles/Converters/ParcelSubAgencyConverter.cs +++ b/backend/api/Profiles/Converters/ParcelSubAgencyConverter.cs @@ -1,7 +1,7 @@ using AutoMapper; using Entity = Pims.Dal.Entities; -namespace Pims.Api.Helpers.Profiles.Converters +namespace Pims.Api.Profiles.Converters { public class ParcelSubAgencyConverter : IValueConverter { diff --git a/backend/api/Profiles/EvaluationProfile.cs b/backend/api/Profiles/EvaluationProfile.cs deleted file mode 100644 index 99199d6f28..0000000000 --- a/backend/api/Profiles/EvaluationProfile.cs +++ /dev/null @@ -1,30 +0,0 @@ -using AutoMapper; -using Pims.Api.Models; -using Entity = Pims.Dal.Entities; - -namespace Pims.Api.Helpers.Profiles -{ - public class EvaluationProfile : Profile - { - #region Constructors - public EvaluationProfile() - { - CreateMap() - .ForMember(dest => dest.PropertyId, opt => opt.MapFrom(src => src.ParcelId)) - .IncludeBase(); - - CreateMap() - .ForMember(dest => dest.ParcelId, opt => opt.MapFrom(src => src.PropertyId)) - .IncludeBase(); - - CreateMap() - .ForMember(dest => dest.PropertyId, opt => opt.MapFrom(src => src.BuildingId)) - .IncludeBase(); - - CreateMap() - .ForMember(dest => dest.BuildingId, opt => opt.MapFrom(src => src.PropertyId)) - .IncludeBase(); - } - #endregion - } -} diff --git a/backend/api/Profiles/LookupProfile.cs b/backend/api/Profiles/Lookup/LookupProfile.cs similarity index 61% rename from backend/api/Profiles/LookupProfile.cs rename to backend/api/Profiles/Lookup/LookupProfile.cs index cb836a534c..26f3703018 100644 --- a/backend/api/Profiles/LookupProfile.cs +++ b/backend/api/Profiles/Lookup/LookupProfile.cs @@ -1,23 +1,23 @@ using AutoMapper; -using Pims.Api.Models; +using Model = Pims.Api.Models; using Entity = Pims.Dal.Entities; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles.Lookup { public class LookupProfile : Profile { #region Constructors public LookupProfile() { - CreateMap() - .IncludeBase() + CreateMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id.ToString())) .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.GetType().Name)) .ForMember(dest => dest.Code, opt => opt.MapFrom(src => src.Name)) + .IncludeBase() .IncludeAllDerived(); - CreateMap() - .IncludeBase() + CreateMap() + .IncludeBase() .IncludeAllDerived(); } #endregion diff --git a/backend/api/Profiles/AddressProfile.cs b/backend/api/Profiles/Parcel/AddressProfile.cs similarity index 69% rename from backend/api/Profiles/AddressProfile.cs rename to backend/api/Profiles/Parcel/AddressProfile.cs index 160ada1211..e546357fee 100644 --- a/backend/api/Profiles/AddressProfile.cs +++ b/backend/api/Profiles/Parcel/AddressProfile.cs @@ -1,26 +1,27 @@ using AutoMapper; -using Pims.Api.Models; using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Parcel; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles.Parcel { public class AddressProfile : Profile { #region Constructors public AddressProfile() { - CreateMap() + CreateMap() .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City.Name)) .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province.Name)) .ForMember(dest => dest.Line1, opt => opt.MapFrom(src => src.Address1)) - .ForMember(dest => dest.Line2, opt => opt.MapFrom(src => src.Address2)); + .ForMember(dest => dest.Line2, opt => opt.MapFrom(src => src.Address2)) + .IncludeBase(); - CreateMap() + CreateMap() .ForMember(dest => dest.City, opt => opt.Ignore()) .ForMember(dest => dest.Province, opt => opt.Ignore()) .ForMember(dest => dest.Address1, opt => opt.MapFrom(src => src.Line1)) .ForMember(dest => dest.Address2, opt => opt.MapFrom(src => src.Line2)) - .ForMember(dest => dest.RowVersion, opt => opt.Ignore()); + .IncludeBase(); } #endregion } diff --git a/backend/api/Profiles/Parcel/BuildingEvaluationProfile.cs b/backend/api/Profiles/Parcel/BuildingEvaluationProfile.cs new file mode 100644 index 0000000000..8be96dd591 --- /dev/null +++ b/backend/api/Profiles/Parcel/BuildingEvaluationProfile.cs @@ -0,0 +1,32 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Parcel; + +namespace Pims.Api.Profiles.Parcel +{ + public class BuildingEvaluationProfile : Profile + { + #region Constructors + public BuildingEvaluationProfile() + { + CreateMap() + .ForMember(dest => dest.PropertyId, opt => opt.MapFrom(src => src.BuildingId)) + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .ForMember(dest => dest.BuildingId, opt => opt.MapFrom(src => src.PropertyId)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/Parcel/ParcelBuildingProfile.cs b/backend/api/Profiles/Parcel/ParcelBuildingProfile.cs new file mode 100644 index 0000000000..4ad42c18e7 --- /dev/null +++ b/backend/api/Profiles/Parcel/ParcelBuildingProfile.cs @@ -0,0 +1,54 @@ +using AutoMapper; +using Model = Pims.Api.Models.Parcel; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Profiles.Parcel +{ + public class ParcelBuildingProfile : Profile + { + #region Constructors + public ParcelBuildingProfile() + { + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.LocalId, opt => opt.MapFrom(src => src.LocalId)) + .ForMember(dest => dest.ParcelId, opt => opt.MapFrom(src => src.ParcelId)) + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => src.AgencyId)) + .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.Latitude, opt => opt.MapFrom(src => src.Latitude)) + .ForMember(dest => dest.Longitude, opt => opt.MapFrom(src => src.Longitude)) + .ForMember(dest => dest.BuildingConstructionTypeId, opt => opt.MapFrom(src => src.BuildingConstructionTypeId)) + .ForMember(dest => dest.BuildingConstructionType, opt => opt.MapFrom(src => src.BuildingConstructionType.Name)) + .ForMember(dest => dest.BuildingFloorCount, opt => opt.MapFrom(src => src.BuildingFloorCount)) + .ForMember(dest => dest.BuildingPredominateUseId, opt => opt.MapFrom(src => src.BuildingPredominateUseId)) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.MapFrom(src => src.BuildingPredominateUse.Name)) + .ForMember(dest => dest.BuildingTenancy, opt => opt.MapFrom(src => src.BuildingTenancy)) + .ForMember(dest => dest.RentableArea, opt => opt.MapFrom(src => src.RentableArea)) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Evaluations, opt => opt.MapFrom(src => src.Evaluations)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.LocalId, opt => opt.MapFrom(src => src.LocalId)) + .ForMember(dest => dest.ParcelId, opt => opt.MapFrom(src => src.ParcelId)) + .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => src.AgencyId)) + .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.Latitude, opt => opt.MapFrom(src => src.Latitude)) + .ForMember(dest => dest.Longitude, opt => opt.MapFrom(src => src.Longitude)) + .ForMember(dest => dest.BuildingConstructionTypeId, opt => opt.MapFrom(src => src.BuildingConstructionTypeId)) + .ForMember(dest => dest.BuildingConstructionType, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingFloorCount, opt => opt.MapFrom(src => src.BuildingFloorCount)) + .ForMember(dest => dest.BuildingPredominateUseId, opt => opt.MapFrom(src => src.BuildingPredominateUseId)) + .ForMember(dest => dest.BuildingPredominateUse, opt => opt.Ignore()) + .ForMember(dest => dest.BuildingTenancy, opt => opt.MapFrom(src => src.BuildingTenancy)) + .ForMember(dest => dest.RentableArea, opt => opt.MapFrom(src => src.RentableArea)) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Evaluations, opt => opt.MapFrom(src => src.Evaluations)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/Parcel/ParcelEvaluationProfile.cs b/backend/api/Profiles/Parcel/ParcelEvaluationProfile.cs new file mode 100644 index 0000000000..1bec962c7c --- /dev/null +++ b/backend/api/Profiles/Parcel/ParcelEvaluationProfile.cs @@ -0,0 +1,32 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Parcel; + +namespace Pims.Api.Profiles.Parcel +{ + public class ParcelEvaluationProfile : Profile + { + #region Constructors + public ParcelEvaluationProfile() + { + CreateMap() + .ForMember(dest => dest.PropertyId, opt => opt.MapFrom(src => src.ParcelId)) + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.ParcelId, opt => opt.Ignore()) + .ForMember(dest => dest.FiscalYear, opt => opt.MapFrom(src => src.FiscalYear)) + .ForMember(dest => dest.EstimatedValue, opt => opt.MapFrom(src => src.EstimatedValue)) + .ForMember(dest => dest.AssessedValue, opt => opt.MapFrom(src => src.AssessedValue)) + .ForMember(dest => dest.AppraisedValue, opt => opt.MapFrom(src => src.AppraisedValue)) + .ForMember(dest => dest.NetBookValue, opt => opt.MapFrom(src => src.NetBookValue)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/Parcel/ParcelProfile.cs b/backend/api/Profiles/Parcel/ParcelProfile.cs new file mode 100644 index 0000000000..33be6e0c88 --- /dev/null +++ b/backend/api/Profiles/Parcel/ParcelProfile.cs @@ -0,0 +1,39 @@ +using AutoMapper; +using Model = Pims.Api.Models.Parcel; +using Entity = Pims.Dal.Entities; +using Pims.Api.Profiles.Converters; +using Pims.Api.Profiles.Parcel.Resolvers; + +namespace Pims.Api.Profiles.Parcel +{ + public class ParcelProfile : Profile + { + #region Constructors + public ParcelProfile() + { + CreateMap() + .ForMember(dest => dest.PID, opt => opt.MapFrom(src => src.ParcelIdentity)) + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.Name)) + .ForMember(dest => dest.Classification, opt => opt.MapFrom(src => src.Classification.Name)) + .ForMember(dest => dest.Agency, opt => opt.ConvertUsing(new ParcelAgencyConverter())) + .ForMember(dest => dest.SubAgency, opt => opt.MapFrom()) + .ForMember(dest => dest.Buildings, opt => opt.MapFrom(src => src.Buildings)) + .ForMember(dest => dest.Evaluations, opt => opt.MapFrom(src => src.Evaluations)) + .IncludeBase(); + + CreateMap() + .ForMember(dest => dest.PID, opt => opt.ConvertUsing(new ParcelIdConverter(), src => src.PID)) + .ForMember(dest => dest.ParcelIdentity, opt => opt.Ignore()) + .ForMember(dest => dest.Status, opt => opt.Ignore()) + .ForMember(dest => dest.Classification, opt => opt.Ignore()) + .ForMember(dest => dest.Agency, opt => opt.Ignore()) + .ForMember(dest => dest.AddressId, opt => opt.MapFrom(src => src.Address.Id)) + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address)) + .ForMember(dest => dest.IsSensitive, opt => opt.MapFrom(src => src.IsSensitive)) + .ForMember(dest => dest.Buildings, opt => opt.Ignore()) + .ForMember(dest => dest.Evaluations, opt => opt.Ignore()) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/Parcel/PartialParcelProfile.cs b/backend/api/Profiles/Parcel/PartialParcelProfile.cs new file mode 100644 index 0000000000..5a9f86b0ac --- /dev/null +++ b/backend/api/Profiles/Parcel/PartialParcelProfile.cs @@ -0,0 +1,31 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Parcel; +using Pims.Api.Profiles.Converters; +using Pims.Api.Helpers.Extensions; + +namespace Pims.Api.Profiles.Parcel +{ + public class PartialParcelProfile : Profile + { + #region Constructors + public PartialParcelProfile() + { + CreateMap() + .ForMember(dest => dest.PID, opt => opt.MapFrom(src => src.ParcelIdentity)); + + CreateMap() + .IgnoreAllUnmapped() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.ParcelIdentity, opt => opt.Ignore()) + .ForMember(dest => dest.StatusId, opt => opt.MapFrom(src => src.StatusId)) + .ForMember(dest => dest.ClassificationId, opt => opt.MapFrom(src => src.ClassificationId)) + .ForMember(dest => dest.Latitude, opt => opt.MapFrom(src => src.Latitude)) + .ForMember(dest => dest.Longitude, opt => opt.MapFrom(src => src.Longitude)) + .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) + .ForMember(dest => dest.PID, opt => opt.ConvertUsing(new ParcelIdConverter(), src => src.PID)) + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/Parcel/Resolvers/ParceIdResolver.cs b/backend/api/Profiles/Parcel/Resolvers/ParceIdResolver.cs new file mode 100644 index 0000000000..2a990e79f4 --- /dev/null +++ b/backend/api/Profiles/Parcel/Resolvers/ParceIdResolver.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using Model = Pims.Api.Models.Parcel; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Profiles.Parcel.Resolvers +{ + public class ParcelIdResolver : IValueResolver + { + public int Resolve(Model.ParcelModel source, Entity.Parcel destination, int destMember, ResolutionContext context) + { + return string.IsNullOrWhiteSpace(source.PID) ? 0 : int.TryParse(source.PID.Replace("-", ""), out int pid) ? pid : 0; + } + } +} diff --git a/backend/api/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs b/backend/api/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs new file mode 100644 index 0000000000..1106fe6667 --- /dev/null +++ b/backend/api/Profiles/Parcel/Resolvers/ParcelSubAgencyResolver.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Parcel; + +namespace Pims.Api.Profiles.Parcel.Resolvers +{ + public class ParcelSubAgencyResolver : IValueResolver + { + public string Resolve(Entity.Parcel source, Model.ParcelModel destination, string destMember, ResolutionContext context) + { + return source?.Agency?.ParentId == null ? null : source.Agency.Code; + } + } +} diff --git a/backend/api/Profiles/PropertyProfile.cs b/backend/api/Profiles/Property/PropertyProfile.cs similarity index 74% rename from backend/api/Profiles/PropertyProfile.cs rename to backend/api/Profiles/Property/PropertyProfile.cs index 81d4324054..03468c78a4 100644 --- a/backend/api/Profiles/PropertyProfile.cs +++ b/backend/api/Profiles/Property/PropertyProfile.cs @@ -1,18 +1,19 @@ using AutoMapper; using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Property; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles.Property { public class PropertyProfile : Profile { #region Constructors public PropertyProfile() { - CreateMap() + CreateMap() .ForMember(dest => dest.PropertyTypeId, opt => opt.MapFrom(src => 0)) .ForMember(dest => dest.PID, opt => opt.MapFrom(src => src.ParcelIdentity)); - CreateMap() + CreateMap() .ForMember(dest => dest.PropertyTypeId, opt => opt.MapFrom(src => 1)) .ForMember(dest => dest.PID, opt => opt.MapFrom(src => src.Parcel.ParcelIdentity)); } diff --git a/backend/api/Profiles/Resolvers/ParceIdResolver.cs b/backend/api/Profiles/Resolvers/ParceIdResolver.cs deleted file mode 100644 index ff6d711fdf..0000000000 --- a/backend/api/Profiles/Resolvers/ParceIdResolver.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AutoMapper; -using Pims.Api.Models; -using Entity = Pims.Dal.Entities; - -namespace Pims.Api.Helpers.Profiles.Converters -{ - public class ParcelIdResolver : IValueResolver - { - public int Resolve(ParcelModel source, Entity.Parcel destination, int destMember, ResolutionContext context) - { - return string.IsNullOrWhiteSpace(source.PID) ? 0 : int.TryParse(source.PID.Replace("-", ""), out int pid) ? pid : 0; - } - } -} diff --git a/backend/api/Profiles/Resolvers/ParcelSubAgencyResolver.cs b/backend/api/Profiles/Resolvers/ParcelSubAgencyResolver.cs deleted file mode 100644 index 78e4d11055..0000000000 --- a/backend/api/Profiles/Resolvers/ParcelSubAgencyResolver.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AutoMapper; -using Pims.Api.Models; -using Pims.Dal.Entities; -using Entity = Pims.Dal.Entities; - -namespace Pims.Api.Helpers.Profiles.Converters -{ - public class ParcelSubAgencyResolver : IValueResolver - { - public string Resolve(Parcel source, ParcelModel destination, string destMember, ResolutionContext context) - { - return source?.Agency?.ParentId == null ? null : source.Agency.Code; - } - } -} diff --git a/backend/api/Profiles/AccessRequestProfile.cs b/backend/api/Profiles/User/AccessRequestProfile.cs similarity index 64% rename from backend/api/Profiles/AccessRequestProfile.cs rename to backend/api/Profiles/User/AccessRequestProfile.cs index e279c75417..f8ebcff4f4 100644 --- a/backend/api/Profiles/AccessRequestProfile.cs +++ b/backend/api/Profiles/User/AccessRequestProfile.cs @@ -1,44 +1,43 @@ using AutoMapper; -using Pims.Api.Models; using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.User; using System.Linq; using System; -using Pims.Api.Models.Parts; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles.User { public class AccessRequestProfile : Profile { #region Constructors public AccessRequestProfile() { - CreateMap() + CreateMap() .ForMember(dest => dest.Agencies, opt => opt.MapFrom(src => src.Agencies.Select(a => a.Agency))) .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles.Select(r => r.Role))) .ForMember(dest => dest.User, opt => opt.MapFrom(src => src.User)); - CreateMap() + CreateMap() .ForMember(dest => dest.User, opt => opt.Ignore()) .ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.User.Id)); - CreateMap() + CreateMap() .ForMember(dest => dest.AccessRequest, opt => opt.Ignore()) .ForMember(dest => dest.AccessRequestId, opt => opt.Ignore()) .ForMember(dest => dest.Agency, opt => opt.Ignore()) .ForMember(dest => dest.AgencyId, opt => opt.MapFrom(src => Int32.Parse(src.Id))); - CreateMap() + CreateMap() .ForMember(dest => dest.AccessRequest, opt => opt.Ignore()) .ForMember(dest => dest.AccessRequestId, opt => opt.Ignore()) .ForMember(dest => dest.Role, opt => opt.Ignore()) .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => Guid.Parse(src.Id))); - CreateMap(); - CreateMap(); - CreateMap() - .IncludeBase(); - CreateMap() + CreateMap(); + CreateMap(); + CreateMap() + .IncludeBase(); + CreateMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id.ToString())) - .IncludeBase(); + .IncludeBase(); } #endregion } diff --git a/backend/api/Profiles/User/AgencyProfile.cs b/backend/api/Profiles/User/AgencyProfile.cs new file mode 100644 index 0000000000..14504db846 --- /dev/null +++ b/backend/api/Profiles/User/AgencyProfile.cs @@ -0,0 +1,20 @@ +using AutoMapper; +using Model = Pims.Api.Models.User; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Profiles.User +{ + public class AgencyProfile : Profile + { + #region Constructors + public AgencyProfile() + { + CreateMap() + .IncludeBase(); + + CreateMap() + .IncludeBase(); + } + #endregion + } +} diff --git a/backend/api/Profiles/RoleProfile.cs b/backend/api/Profiles/User/RoleProfile.cs similarity index 62% rename from backend/api/Profiles/RoleProfile.cs rename to backend/api/Profiles/User/RoleProfile.cs index 9bceee0e8c..f6ccb8872d 100644 --- a/backend/api/Profiles/RoleProfile.cs +++ b/backend/api/Profiles/User/RoleProfile.cs @@ -1,39 +1,39 @@ using AutoMapper; -using Pims.Api.Models; -using Pims.Api.Profiles.Extensions; +using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.User; +using Pims.Api.Helpers.Extensions; using System; using System.Linq; -using Entity = Pims.Dal.Entities; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles.User { public class RoleProfile : Profile { #region Constructors public RoleProfile() { - CreateMap() - .IncludeBase() + CreateMap() + .IncludeBase() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id.ToString())) .ForMember(dest => dest.Users, opt => opt.MapFrom(src => src.Users.Select(u => u.User))); - CreateMap() - .IncludeBase() + CreateMap() + .IncludeBase() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id.ToString())) .ForMember(dest => dest.Code, opt => opt.MapFrom(src => src.Name)); - CreateMap() + CreateMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Guid.Parse(src.Id))) - .IncludeBase(); + .IncludeBase(); - CreateMap() + CreateMap() .IgnoreAllUnmapped() - .IncludeBase() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.RoleId)) .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Role.Name)) .ForMember(dest => dest.Code, opt => opt.Ignore()) .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Role.Description)) - .ForMember(dest => dest.IsDisabled, opt => opt.MapFrom(src => src.Role.IsDisabled)); + .ForMember(dest => dest.IsDisabled, opt => opt.MapFrom(src => src.Role.IsDisabled)) + .IncludeBase(); } #endregion } diff --git a/backend/api/Profiles/UserProfile.cs b/backend/api/Profiles/User/UserProfile.cs similarity index 74% rename from backend/api/Profiles/UserProfile.cs rename to backend/api/Profiles/User/UserProfile.cs index 3624e43482..e9c90f23c7 100644 --- a/backend/api/Profiles/UserProfile.cs +++ b/backend/api/Profiles/User/UserProfile.cs @@ -1,19 +1,19 @@ using AutoMapper; -using Pims.Api.Models; +using Model = Pims.Api.Models.User; using Entity = Pims.Dal.Entities; -namespace Pims.Api.Helpers.Profiles +namespace Pims.Api.Profiles.User { public class UserProfile : Profile { #region Constructors public UserProfile() { - CreateMap() + CreateMap() .ForMember(dest => dest.Agencies, opt => opt.MapFrom(src => src.Agencies)) .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles)); - CreateMap() + CreateMap() .ForMember(dest => dest.Agencies, opt => opt.Ignore()) .ForMember(dest => dest.Roles, opt => opt.Ignore()); } diff --git a/backend/api/Properties/launchSettings.json b/backend/api/Properties/launchSettings.json index 8f3c1cffe1..fa5f208261 100644 --- a/backend/api/Properties/launchSettings.json +++ b/backend/api/Properties/launchSettings.json @@ -3,25 +3,25 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:53591/", + "applicationUrl": "http://localhost:5000/", "sslPort": 44342 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Local" } }, - "Pims.api": { + "Pims.Api": { "commandName": "Project", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Local" }, "applicationUrl": "https://localhost:5000" } } -} \ No newline at end of file +} diff --git a/backend/core/Extensions/EnumerableExtensions.cs b/backend/core/Extensions/EnumerableExtensions.cs index 4f51c0b701..f41e50a720 100644 --- a/backend/core/Extensions/EnumerableExtensions.cs +++ b/backend/core/Extensions/EnumerableExtensions.cs @@ -31,5 +31,33 @@ public static IEnumerable Flatten(this IEnumerable e, Func f(c).Flatten(f)).Concat(e); } + + /// + /// Quick way to skip and take one. + /// + /// + /// + /// + /// + public static T Next(this IEnumerable items, int skip) + { + if (skip < 0) throw new ArgumentException("Argument must be greater than or equal to zero.", nameof(skip)); + return items.Skip(skip).First(); + } + + /// + /// Quick way to skip and take the specified quantity. + /// + /// + /// + /// + /// + /// + public static IEnumerable Next(this IEnumerable items, int skip, int take) + { + if (skip < 0) throw new ArgumentException("Argument must be greater than or equal to zero.", nameof(skip)); + if (take < 1) throw new ArgumentException("Argument must be greater than or equal to 1.", nameof(take)); + return items.Skip(skip).Take(take); + } } } diff --git a/backend/core/Properties/launchSettings.json b/backend/core/Properties/launchSettings.json new file mode 100644 index 0000000000..3d7b366afe --- /dev/null +++ b/backend/core/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:16003/", + "sslPort": 44309 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Pims.Core": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/backend/dal.keycloak/Partials/PimsKeycloakRoleService.cs b/backend/dal.keycloak/Partials/PimsKeycloakRoleService.cs index fe070be589..45355f0eea 100644 --- a/backend/dal.keycloak/Partials/PimsKeycloakRoleService.cs +++ b/backend/dal.keycloak/Partials/PimsKeycloakRoleService.cs @@ -1,4 +1,5 @@ -using System; +using Pims.Core.Helpers; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -68,7 +69,7 @@ public partial class PimsKeycloakService : IPimsKeycloakService var kgroups = await _keycloakService.GetGroupsAsync((page - 1) * quantity, quantity, search); // TODO: Need better performing solution. - var eroles = kgroups.Select(g => _pimsAdminService.Role.Find(g.Id) ?? _mapper.Map(g)); + var eroles = kgroups.Select(g => ExceptionHelper.HandleKeyNotFound(() => _pimsAdminService.Role.Get(g.Id)) ?? _mapper.Map(g)); return eroles; } @@ -82,7 +83,7 @@ public partial class PimsKeycloakService : IPimsKeycloakService public async Task GetRoleAsync(Guid id) { var kgroup = await _keycloakService.GetGroupAsync(id) ?? throw new KeyNotFoundException(); - return _pimsAdminService.Role.GetNoTracking(kgroup.Id); + return _pimsAdminService.Role.Get(kgroup.Id); } /// diff --git a/backend/dal.keycloak/Partials/PimsKeycloakUserService.cs b/backend/dal.keycloak/Partials/PimsKeycloakUserService.cs index fd8a3cf43d..07f1fb9987 100644 --- a/backend/dal.keycloak/Partials/PimsKeycloakUserService.cs +++ b/backend/dal.keycloak/Partials/PimsKeycloakUserService.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Pims.Core.Extensions; using Pims.Core.Helpers; +using Pims.Dal.Entities.Comparers; using Pims.Dal.Helpers.Extensions; using Pims.Dal.Security; using Entity = Pims.Dal.Entities; @@ -90,7 +91,7 @@ public partial class PimsKeycloakService : IPimsKeycloakService var kusers = await _keycloakService.GetUsersAsync((page - 1) * quantity, quantity, search); // TODO: Need better performing solution. - var eusers = kusers.Select(u => ExceptionHelper.HandleKeyNotFound(() => _pimsAdminService.User.GetNoTracking(u.Id)) ?? _mapper.Map(u)); + var eusers = kusers.Select(u => ExceptionHelper.HandleKeyNotFound(() => _pimsAdminService.User.Get(u.Id)) ?? _mapper.Map(u)); return eusers; } @@ -120,14 +121,13 @@ public partial class PimsKeycloakService : IPimsKeycloakService if (user.Username != kuser.Username) throw new InvalidOperationException($"Cannot change the username from '{kuser.Username}' to '{user.Username}'."); - var addRoles = user.Roles.Except(euser.Roles).ToArray(); - var removeRoles = euser.Roles.Except(user.Roles).ToArray(); - var addAgencies = user.Agencies.Except(euser.Agencies).ToArray(); - var removeAgencies = euser.Agencies.Except(user.Agencies).ToArray(); + var addRoles = user.Roles.Except(euser.Roles, new UserRoleRoleIdComparer()).ToArray(); + var removeRoles = euser.Roles.Except(user.Roles, new UserRoleRoleIdComparer()).ToArray(); + var addAgencies = user.Agencies.Except(euser.Agencies, new UserAgencyAgencyIdComparer()).ToArray(); + var removeAgencies = euser.Agencies.Except(user.Agencies, new UserAgencyAgencyIdComparer()).ToArray(); // Update PIMS - _pimsAdminService.User.SetCurrentValues(user, euser); - _pimsAdminService.User.UpdateOne(euser); + _mapper.Map(user, euser); // Update Roles. addRoles.ForEach(async r => @@ -153,7 +153,7 @@ public partial class PimsKeycloakService : IPimsKeycloakService euser.Agencies.Remove(a); }); - _pimsAdminService.CommitTransaction(); + _pimsAdminService.User.Update(euser); // Now update keycloak var kmodel = _mapper.Map(user); @@ -179,7 +179,7 @@ public partial class PimsKeycloakService : IPimsKeycloakService entity.ThrowIfNull(nameof(entity.UserId)); this._user.ThrowIfNotAuthorized(Permissions.AdminUsers, Permissions.AgencyAdmin); - var accessRequest = _pimsAdminService.User.GetAccessRequestNoTracking(entity.Id); + var accessRequest = _pimsAdminService.User.GetAccessRequest(entity.Id); if (!accessRequest.IsGranted != true && entity.IsGranted == true) { Entity.User user = _pimsAdminService.User.Get(accessRequest.UserId.Value); diff --git a/backend/dal/Helpers/Extensions/DbContextExtensions.cs b/backend/dal/Helpers/Extensions/DbContextExtensions.cs new file mode 100644 index 0000000000..08498b1fb7 --- /dev/null +++ b/backend/dal/Helpers/Extensions/DbContextExtensions.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using Pims.Dal.Entities; + +namespace Pims.Dal.Helpers.Extensions +{ + /// + /// DbContextExtensions static class, provides extension methods for DbContext objects. + /// + public static class DbContextExtensions + { + /// + /// When manipulating entities it is necessary to reset the original value for 'RowVersion' so that concurrency checking can occur. + /// + /// + /// + /// + public static void SetOriginalRowVersion(this DbContext context, T source) where T : BaseEntity + { + context.Entry(source).OriginalValues[nameof(BaseEntity.RowVersion)] = source.RowVersion; + } + + /// + /// Detach the entity from the context. + /// + /// + /// + /// + public static void Detach(this DbContext context, T entity) where T : BaseEntity + { + context.Entry(entity).State = EntityState.Detached; + } + } +} diff --git a/backend/dal/Helpers/Extensions/EntityExtensions.cs b/backend/dal/Helpers/Extensions/EntityExtensions.cs index 38b3aaf2ef..706f4d0930 100644 --- a/backend/dal/Helpers/Extensions/EntityExtensions.cs +++ b/backend/dal/Helpers/Extensions/EntityExtensions.cs @@ -1,7 +1,7 @@ using System.Security.Claims; +using Microsoft.EntityFrameworkCore; using Pims.Core.Extensions; using Pims.Dal.Entities; -using Pims.Dal.Exceptions; using Pims.Dal.Security; namespace Pims.Dal.Helpers.Extensions @@ -54,5 +54,15 @@ public static T ThrowIfNotAllowedToEdit(this T entity, string paramName, Clai return entity; } + + /// + /// When manipulating entities it is necessary to reset the original value for 'RowVersion' so that concurrency checking can occur. + /// + /// + /// + public static void SetOriginalRowVersion(this T source, DbContext context) where T : BaseEntity + { + context.SetOriginalRowVersion(source); + } } } diff --git a/backend/dal/PIMSContext.cs b/backend/dal/PIMSContext.cs index ecad00b05c..762728888e 100644 --- a/backend/dal/PIMSContext.cs +++ b/backend/dal/PIMSContext.cs @@ -110,6 +110,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) base.OnModelCreating(modelBuilder); } + /// + /// Save the entities with who created them or updated them. + /// + /// public override int SaveChanges() { // get entries that are being Added or Updated @@ -128,7 +132,7 @@ public override int SaveChanges() entity.CreatedOn = DateTime.UtcNow; } else if (entry.State != EntityState.Deleted) - { + { entity.UpdatedById = userId; entity.UpdatedOn = DateTime.UtcNow; } @@ -145,7 +149,7 @@ public override int SaveChanges() public int CommitTransaction() { var result = 0; - using(var transaction = this.Database.BeginTransaction()) + using (var transaction = this.Database.BeginTransaction()) { try { diff --git a/backend/dal/Properties/launchSettings.json b/backend/dal/Properties/launchSettings.json new file mode 100644 index 0000000000..ec06f90e44 --- /dev/null +++ b/backend/dal/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:16002/", + "sslPort": 44303 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Pims.Dal": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/backend/dal/Services/Admin/Concrete/AddressService.cs b/backend/dal/Services/Admin/Concrete/AddressService.cs index 9d0c533bfa..e1814e23a3 100644 --- a/backend/dal/Services/Admin/Concrete/AddressService.cs +++ b/backend/dal/Services/Admin/Concrete/AddressService.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using Pims.Core.Extensions; using Pims.Dal.Entities; -using Pims.Dal.Helpers.Extensions; namespace Pims.Dal.Services.Admin { diff --git a/backend/dal/Services/Admin/Concrete/AgencyService.cs b/backend/dal/Services/Admin/Concrete/AgencyService.cs index b7eed6941b..7781e64a30 100644 --- a/backend/dal/Services/Admin/Concrete/AgencyService.cs +++ b/backend/dal/Services/Admin/Concrete/AgencyService.cs @@ -34,19 +34,9 @@ public AgencyService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public IEnumerable GetAllNoTracking() - { - var query = this.Context.Agencies.AsNoTracking(); - return query.OrderBy(p => p.Name).ToArray(); - } - - /// - /// Get all agencies from the datasource. - /// - /// public IEnumerable GetAll() { - return this.Context.Agencies.OrderBy(p => p.Name).ToArray(); + return this.Context.Agencies.AsNoTracking().OrderBy(p => p.Name).ToArray(); } /// diff --git a/backend/dal/Services/Admin/Concrete/BaseService`.cs b/backend/dal/Services/Admin/Concrete/BaseService`.cs index 754a8f1f23..35dbc88f87 100644 --- a/backend/dal/Services/Admin/Concrete/BaseService`.cs +++ b/backend/dal/Services/Admin/Concrete/BaseService`.cs @@ -1,6 +1,5 @@ -using System; -using System.Collections.Generic; using System.Security.Claims; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Pims.Core.Extensions; using Pims.Dal.Entities; @@ -26,17 +25,6 @@ public BaseService(PimsContext dbContext, ClaimsPrincipal user, ILogger - /// Copies the values from the 'source' into the 'destination' so that EF can update appropriately. - /// - /// - /// - public void SetCurrentValues(ET source, ET destination) - { - this.Context.Entry(destination).CurrentValues.SetValues(source); - this.Context.Entry(destination).OriginalValues[nameof(destination.RowVersion)] = destination.RowVersion; // Need to do this to resolve the concurrency bug/feature in EF. - } - /// /// Find the entity within the datasource with the specified key values. /// @@ -46,7 +34,9 @@ public virtual ET Find(params object[] keyValues) { this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); - return this.Context.Set().Find(keyValues); + var result = this.Context.Set().Find(keyValues); + this.Context.Entry(result).State = Microsoft.EntityFrameworkCore.EntityState.Detached; // Force detach so that outside the DAL the DB cannot be manipulated. + return result; } /// @@ -73,9 +63,7 @@ public virtual ET AddOne(ET entity) { entity.ThrowIfNull(nameof(entity)); this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); - - entity.CreatedById = this.User.GetUserId(); - this.Context.Set().Add(entity); + this.Context.Entry(entity).State = EntityState.Added; return entity; } @@ -107,10 +95,7 @@ public virtual ET UpdateOne(ET entity) entity.ThrowIfNull(nameof(entity)); entity.ThrowIfRowVersionNull(nameof(entity)); this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); - - entity.UpdatedById = this.User.GetUserId(); - entity.UpdatedOn = DateTime.UtcNow; - this.Context.Set().Update(entity); + this.Context.Entry(entity).State = EntityState.Modified; return entity; } @@ -138,8 +123,7 @@ public virtual void RemoveOne(ET entity) entity.ThrowIfNull(nameof(entity)); entity.ThrowIfRowVersionNull(nameof(entity)); this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); - - this.Context.Set().Remove(entity); + this.Context.Entry(entity).State = EntityState.Deleted; } #endregion } diff --git a/backend/dal/Services/Admin/Concrete/BuildingConstructionTypeService.cs b/backend/dal/Services/Admin/Concrete/BuildingConstructionTypeService.cs index 4ab5233cf0..7736ea4d52 100644 --- a/backend/dal/Services/Admin/Concrete/BuildingConstructionTypeService.cs +++ b/backend/dal/Services/Admin/Concrete/BuildingConstructionTypeService.cs @@ -36,19 +36,9 @@ public BuildingConstructionTypeService(PimsContext dbContext, ClaimsPrincipal us /// /// /// - public IEnumerable GetAllNoTracking() - { - var query = this.Context.BuildingConstructionTypes.AsNoTracking(); - return query.OrderBy(p => p.Name).ToArray(); - } - - /// - /// Get all building construction types from the datasource. - /// - /// public IEnumerable GetAll() { - return this.Context.BuildingConstructionTypes.OrderBy(p => p.Name).ToArray(); + return this.Context.BuildingConstructionTypes.AsNoTracking().OrderBy(p => p.Name).ToArray(); } /// diff --git a/backend/dal/Services/Admin/Concrete/BuildingPredominateUseService.cs b/backend/dal/Services/Admin/Concrete/BuildingPredominateUseService.cs index 5799a7c515..ac04842000 100644 --- a/backend/dal/Services/Admin/Concrete/BuildingPredominateUseService.cs +++ b/backend/dal/Services/Admin/Concrete/BuildingPredominateUseService.cs @@ -36,19 +36,9 @@ public BuildingPredominateUseService(PimsContext dbContext, ClaimsPrincipal user /// /// /// - public IEnumerable GetAllNoTracking() - { - var query = this.Context.BuildingPredominateUses.AsNoTracking(); - return query.OrderBy(p => p.Name).ToArray(); - } - - /// - /// Get all building predominate uses from the datasource. - /// - /// public IEnumerable GetAll() { - return this.Context.BuildingPredominateUses.OrderBy(p => p.Name).ToArray(); + return this.Context.BuildingPredominateUses.AsNoTracking().OrderBy(p => p.Name).ToArray(); } /// diff --git a/backend/dal/Services/Admin/Concrete/BuildingService.cs b/backend/dal/Services/Admin/Concrete/BuildingService.cs index 124f578930..32dc889a33 100644 --- a/backend/dal/Services/Admin/Concrete/BuildingService.cs +++ b/backend/dal/Services/Admin/Concrete/BuildingService.cs @@ -35,7 +35,7 @@ public BuildingService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public Paged GetNoTracking(int page, int quantity, string sort) + public Paged Get(int page, int quantity, string sort) { // TODO: Check for system-administrator role. if (this.User == null) throw new NotAuthorizedException(); @@ -51,7 +51,7 @@ public Paged GetNoTracking(int page, int quantity, string sort) /// /// /// - public Building GetNoTracking(int id) + public Building Get(int id) { // TODO: Check for system-administrator role. if (this.User == null) throw new NotAuthorizedException(); @@ -67,27 +67,6 @@ public Building GetNoTracking(int id) .AsNoTracking().SingleOrDefault(u => u.Id == id) ?? throw new KeyNotFoundException(); } - /// - /// Get the building for the specified 'localId'. - /// - /// - /// - public Building GetByLocalIdNoTracking(string localId) - { - // TODO: Check for system-administrator role. - if (this.User == null) throw new NotAuthorizedException(); - - return this.Context.Buildings - .Include(p => p.BuildingConstructionType) - .Include(p => p.BuildingPredominateUse) - .Include(p => p.Address) - .Include(p => p.Address.City) - .Include(p => p.Address.Province) - .Include(p => p.Agency) - .Include(p => p.Agency.Parent) - .AsNoTracking().SingleOrDefault(u => u.LocalId == localId) ?? throw new KeyNotFoundException(); - } - /// /// Get the building for the specified 'localId'. /// @@ -106,7 +85,7 @@ public Building GetByLocalId(string localId) .Include(p => p.Address.Province) .Include(p => p.Agency) .Include(p => p.Agency.Parent) - .SingleOrDefault(u => u.LocalId == localId) ?? throw new KeyNotFoundException(); + .AsNoTracking().SingleOrDefault(u => u.LocalId == localId) ?? throw new KeyNotFoundException(); } /// @@ -128,23 +107,10 @@ public Building GetByPidAndLocalId(int pid, string localId) .Include(p => p.Address.Province) .Include(p => p.Agency) .Include(p => p.Agency.Parent) + .AsNoTracking() .SingleOrDefault(u => u.Parcel.PID == pid && u.LocalId == localId) ?? throw new KeyNotFoundException(); } - /// - /// Get the building for the specified 'id'. - /// - /// - /// - public Building Get(int id) - { - var entity = this.Context.Buildings - .Include(p => p.Address) - .SingleOrDefault(p => p.Id == id) ?? throw new KeyNotFoundException(); - - return entity; - } - /// /// Add the collection of buildings to the datasource. /// diff --git a/backend/dal/Services/Admin/Concrete/CityService.cs b/backend/dal/Services/Admin/Concrete/CityService.cs index 4a1ad7d13e..7daed0313e 100644 --- a/backend/dal/Services/Admin/Concrete/CityService.cs +++ b/backend/dal/Services/Admin/Concrete/CityService.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.Logging; using Pims.Core.Extensions; using Pims.Dal.Entities; -using Pims.Dal.Helpers.Extensions; namespace Pims.Dal.Services.Admin { @@ -36,7 +35,7 @@ public CityService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public IEnumerable GetNoTracking(string name) + public IEnumerable Get(string name) { var query = this.Context.Cities.AsNoTracking(); diff --git a/backend/dal/Services/Admin/Concrete/ParcelService.cs b/backend/dal/Services/Admin/Concrete/ParcelService.cs index cccdc9807e..a7b51764c2 100644 --- a/backend/dal/Services/Admin/Concrete/ParcelService.cs +++ b/backend/dal/Services/Admin/Concrete/ParcelService.cs @@ -7,7 +7,6 @@ using Pims.Core.Extensions; using Pims.Dal.Entities; using Pims.Dal.Entities.Models; -using Pims.Dal.Exceptions; using Pims.Dal.Helpers.Extensions; using Pims.Dal.Security; @@ -36,7 +35,7 @@ public ParcelService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public Paged GetNoTracking(int page, int quantity, string sort) + public Paged Get(int page, int quantity, string sort) { this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); @@ -51,7 +50,7 @@ public Paged GetNoTracking(int page, int quantity, string sort) /// /// Entity does not exist in the datasource. /// - public Parcel GetNoTracking(int id) + public Parcel Get(int id) { this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); @@ -73,34 +72,6 @@ public Parcel GetNoTracking(int id) .AsNoTracking().SingleOrDefault(u => u.Id == id) ?? throw new KeyNotFoundException(); } - /// - /// Get the parcel for the specified 'pid'. - /// - /// - /// Entity does not exist in the datasource. - /// - public Parcel GetByPidNoTracking(int pid) - { - this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); - - return this.Context.Parcels - .Include(p => p.Status) - .Include(p => p.Classification) - .Include(p => p.Address) - .Include(p => p.Address.City) - .Include(p => p.Address.Province) - .Include(p => p.Agency) - .Include(p => p.Agency.Parent) - .Include(p => p.Evaluations) - .Include(p => p.Buildings) - .Include(p => p.Buildings).ThenInclude(b => b.Address) - .Include(p => p.Buildings).ThenInclude(b => b.Address.City) - .Include(p => p.Buildings).ThenInclude(b => b.Address.Province) - .Include(p => p.Buildings).ThenInclude(b => b.BuildingConstructionType) - .Include(p => p.Buildings).ThenInclude(b => b.BuildingPredominateUse) - .AsNoTracking().SingleOrDefault(u => u.PID == pid) ?? throw new KeyNotFoundException(); - } - /// /// Get the parcel for the specified 'pid'. /// @@ -126,31 +97,7 @@ public Parcel GetByPid(int pid) .Include(p => p.Buildings).ThenInclude(b => b.Address.Province) .Include(p => p.Buildings).ThenInclude(b => b.BuildingConstructionType) .Include(p => p.Buildings).ThenInclude(b => b.BuildingPredominateUse) - .SingleOrDefault(u => u.PID == pid) ?? throw new KeyNotFoundException(); - } - - /// - /// Get the parcel for the specified 'id'. - /// - /// - /// Entity does not exist in the datasource. - /// - public Parcel Get(int id) - { - this.User.ThrowIfNotAuthorized(Permissions.SystemAdmin, Permissions.AgencyAdmin); - - var entity = this.Context.Parcels - .Include(p => p.Address) - .Include(p => p.Evaluations) - .Include(p => p.Buildings) - .Include(p => p.Buildings).ThenInclude(b => b.Address) - .Include(p => p.Buildings).ThenInclude(b => b.Address.City) - .Include(p => p.Buildings).ThenInclude(b => b.Address.Province) - .Include(p => p.Buildings).ThenInclude(b => b.BuildingConstructionType) - .Include(p => p.Buildings).ThenInclude(b => b.BuildingPredominateUse) - .SingleOrDefault(p => p.Id == id) ?? throw new KeyNotFoundException(); - - return entity; + .AsNoTracking().SingleOrDefault(u => u.PID == pid) ?? throw new KeyNotFoundException(); } /// diff --git a/backend/dal/Services/Admin/Concrete/PropertyClassificationService.cs b/backend/dal/Services/Admin/Concrete/PropertyClassificationService.cs index 741d05c449..d17d008fba 100644 --- a/backend/dal/Services/Admin/Concrete/PropertyClassificationService.cs +++ b/backend/dal/Services/Admin/Concrete/PropertyClassificationService.cs @@ -36,19 +36,9 @@ public PropertyClassificationService(PimsContext dbContext, ClaimsPrincipal user /// /// /// - public IEnumerable GetAllNoTracking() - { - var query = this.Context.PropertyClassifications.AsNoTracking(); - return query.OrderBy(p => p.Name).ToArray(); - } - - /// - /// Get all property classifications from the datasource. - /// - /// public IEnumerable GetAll() { - return this.Context.PropertyClassifications.OrderBy(p => p.Name).ToArray(); + return this.Context.PropertyClassifications.AsNoTracking().OrderBy(p => p.Name).ToArray(); } /// diff --git a/backend/dal/Services/Admin/Concrete/PropertyStatusService.cs b/backend/dal/Services/Admin/Concrete/PropertyStatusService.cs index 67e906b5ad..ce197cea50 100644 --- a/backend/dal/Services/Admin/Concrete/PropertyStatusService.cs +++ b/backend/dal/Services/Admin/Concrete/PropertyStatusService.cs @@ -36,19 +36,9 @@ public PropertyStatusService(PimsContext dbContext, ClaimsPrincipal user, ILogge /// /// /// - public IEnumerable GetAllNoTracking() - { - var query = this.Context.PropertyStatus.AsNoTracking(); - return query.OrderBy(p => p.Name).ToArray(); - } - - /// - /// Get all property status from the datasource. - /// - /// public IEnumerable GetAll() { - return this.Context.PropertyStatus.OrderBy(p => p.Name).ToArray(); + return this.Context.PropertyStatus.AsNoTracking().OrderBy(p => p.Name).ToArray(); } /// diff --git a/backend/dal/Services/Admin/Concrete/PropertyTypeService.cs b/backend/dal/Services/Admin/Concrete/PropertyTypeService.cs index 3664991b65..a90f2ebd52 100644 --- a/backend/dal/Services/Admin/Concrete/PropertyTypeService.cs +++ b/backend/dal/Services/Admin/Concrete/PropertyTypeService.cs @@ -36,21 +36,12 @@ public PropertyTypeService(PimsContext dbContext, ClaimsPrincipal user, ILogger< /// /// /// - public IEnumerable GetAllNoTracking() + public IEnumerable GetAll() { var query = this.Context.PropertyTypes.AsNoTracking(); return query.OrderBy(p => p.Name).ToArray(); } - /// - /// Get all property types from the datasource. - /// - /// - public IEnumerable GetAll() - { - return this.Context.PropertyTypes.OrderBy(p => p.Name).ToArray(); - } - /// /// Updates the specified property type in the datasource. /// diff --git a/backend/dal/Services/Admin/Concrete/ProvinceService.cs b/backend/dal/Services/Admin/Concrete/ProvinceService.cs index 58c77d3c85..a58b416bb2 100644 --- a/backend/dal/Services/Admin/Concrete/ProvinceService.cs +++ b/backend/dal/Services/Admin/Concrete/ProvinceService.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Pims.Core.Extensions; using Pims.Dal.Entities; -using Pims.Dal.Helpers.Extensions; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; namespace Pims.Dal.Services.Admin { @@ -36,7 +34,7 @@ public ProvinceService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public IEnumerable GetNoTracking() + public IEnumerable Get() { var query = this.Context.Provinces.AsNoTracking(); return query.OrderBy(p => p.Name).ToArray(); diff --git a/backend/dal/Services/Admin/Concrete/RoleService.cs b/backend/dal/Services/Admin/Concrete/RoleService.cs index b96b5364a4..c226476503 100644 --- a/backend/dal/Services/Admin/Concrete/RoleService.cs +++ b/backend/dal/Services/Admin/Concrete/RoleService.cs @@ -37,7 +37,7 @@ public RoleService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public Paged GetNoTracking(int page = 1, int quantity = 10) + public Paged Get(int page = 1, int quantity = 10) { this.User.ThrowIfNotAuthorized(Permissions.AdminRoles); @@ -52,7 +52,7 @@ public Paged GetNoTracking(int page = 1, int quantity = 10) /// /// Entity does not exist in the datasource. /// - public Role GetNoTracking(Guid id) + public Role Get(Guid id) { this.User.ThrowIfNotAuthorized(Permissions.AdminRoles); @@ -67,7 +67,7 @@ public Role GetNoTracking(Guid id) /// public Role GetByName(string name) { - return this.Context.Roles.SingleOrDefault(r => r.Name == name) ?? throw new KeyNotFoundException(); + return this.Context.Roles.AsNoTracking().FirstOrDefault(r => r.Name == name) ?? throw new KeyNotFoundException(); } /// @@ -84,7 +84,9 @@ public override Role Update(Role entity) var role = this.Context.Roles.Find(entity.Id) ?? throw new KeyNotFoundException(); this.Context.Entry(role).CurrentValues.SetValues(entity); - return base.Update(role); + base.Update(role); + this.Context.Detach(role); + return role; } /// diff --git a/backend/dal/Services/Admin/Concrete/UserService.cs b/backend/dal/Services/Admin/Concrete/UserService.cs index 1a1ce1176c..093fdbd553 100644 --- a/backend/dal/Services/Admin/Concrete/UserService.cs +++ b/backend/dal/Services/Admin/Concrete/UserService.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; using Pims.Core.Extensions; using Pims.Dal.Entities; +using Pims.Dal.Entities.Comparers; using Pims.Dal.Entities.Models; using Pims.Dal.Helpers.Extensions; using Pims.Dal.Security; @@ -38,9 +39,9 @@ public UserService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public Paged GetNoTracking(int page = 1, int quantity = 10) + public Paged Get(int page = 1, int quantity = 10) { - return GetNoTracking(new UserFilter(page, quantity)); + return Get(new UserFilter(page, quantity)); } /// @@ -49,7 +50,7 @@ public Paged GetNoTracking(int page = 1, int quantity = 10) /// /// /// - public Paged GetNoTracking(UserFilter filter = null) + public Paged Get(UserFilter filter = null) { this.User.ThrowIfNotAuthorized(Permissions.AdminUsers); var query = this.Context.Users @@ -90,25 +91,6 @@ public Paged GetNoTracking(UserFilter filter = null) /// /// Entity does not exist in the datasource. /// - public User GetNoTracking(Guid id) - { - this.User.ThrowIfNotAuthorized(Permissions.AdminUsers); - - return this.Context.Users - .Include(u => u.Roles) - .ThenInclude(r => r.Role) - .Include(u => u.Agencies) - .ThenInclude(a => a.Agency) - .AsNoTracking() - .SingleOrDefault(u => u.Id == id) ?? throw new KeyNotFoundException(); - } - - /// - /// Get the user for the specified 'id'. - /// - /// - /// Entity does not exist in the datasource. - /// public User Get(Guid id) { this.User.ThrowIfNotAuthorized(Permissions.AdminUsers); @@ -118,6 +100,7 @@ public User Get(Guid id) .ThenInclude(r => r.Role) .Include(u => u.Agencies) .ThenInclude(a => a.Agency) + .AsNoTracking() .SingleOrDefault(u => u.Id == id) ?? throw new KeyNotFoundException(); } @@ -131,11 +114,27 @@ public override User Update(User entity) { entity.ThrowIfNull(nameof(entity)); - var user = this.Context.Users.Find(entity.Id) ?? throw new KeyNotFoundException(); + var user = this.Context.Users + .Include(u => u.Agencies) + .Include(u => u.Roles) + .AsNoTracking() + .FirstOrDefault(u => u.Id == entity.Id) ?? throw new KeyNotFoundException(); + + this.Context.SetOriginalRowVersion(user); + + var addRoles = entity.Roles.Except(user.Roles, new UserRoleRoleIdComparer()); + addRoles.ForEach(r => this.Context.Entry(r).State = EntityState.Added); + var removeRoles = user.Roles.Except(entity.Roles, new UserRoleRoleIdComparer()); + removeRoles.ForEach(r => this.Context.Entry(r).State = EntityState.Deleted); - this.Context.Entry(user).CurrentValues.SetValues(entity); // TODO: Do not user CurrentValue.SetValues(...) unless you reset the Original RowVersion. - this.Context.Entry(user).OriginalValues[nameof(entity.RowVersion)] = user.RowVersion; - return base.Update(user); + var addAgencies = entity.Agencies.Except(user.Agencies, new UserAgencyAgencyIdComparer()); + addAgencies.ForEach(a => this.Context.Entry(a).State = EntityState.Added); + var removeAgencies = user.Agencies.Except(entity.Agencies, new UserAgencyAgencyIdComparer()); + removeAgencies.ForEach(a => this.Context.Entry(a).State = EntityState.Deleted); + + base.Update(entity); + this.Context.Detach(entity); + return entity; } /// @@ -147,10 +146,17 @@ public override void Remove(User entity) { entity.ThrowIfNull(nameof(entity)); - var user = this.Context.Users.Find(entity.Id) ?? throw new KeyNotFoundException(); + var user = this.Context.Users + .Include(u => u.Agencies) + .Include(u => u.Roles) + .AsNoTracking() + .FirstOrDefault(u => u.Id == entity.Id) ?? throw new KeyNotFoundException(); + + this.Context.SetOriginalRowVersion(user); + + user.Roles.Clear(); + user.Agencies.Clear(); - this.Context.Entry(user).CurrentValues.SetValues(entity); - this.Context.Entry(user).OriginalValues[nameof(entity.RowVersion)] = user.RowVersion; base.Remove(user); } @@ -160,7 +166,7 @@ public override void Remove(User entity) /// public AccessRequest UpdateAccessRequest(AccessRequest entity) { - var accessRequest = GetAccessRequestNoTracking(entity.Id); + var accessRequest = GetAccessRequest(entity.Id); entity.UpdatedById = this.User.GetUserId(); // TODO: No longer needed. entity.UpdatedOn = DateTime.UtcNow; this.Context.Entry(accessRequest).CurrentValues.SetValues(entity); @@ -172,7 +178,7 @@ public AccessRequest UpdateAccessRequest(AccessRequest entity) /// Get the access request with matching id /// /// - public AccessRequest GetAccessRequestNoTracking(Guid id) + public AccessRequest GetAccessRequest(Guid id) { this.User.ThrowIfNotAuthorized(Permissions.AdminUsers); @@ -194,7 +200,7 @@ public AccessRequest GetAccessRequestNoTracking(Guid id) /// /// /// - public Paged GetAccessRequestsNoTracking(int page = 1, int quantity = 10, string sort = null, bool? isGranted = null) + public Paged GetAccessRequests(int page = 1, int quantity = 10, string sort = null, bool? isGranted = null) { this.User.ThrowIfNotAuthorized(Permissions.AdminUsers); diff --git a/backend/dal/Services/Admin/IAddressService.cs b/backend/dal/Services/Admin/IAddressService.cs index 780157280e..6419c91f9a 100644 --- a/backend/dal/Services/Admin/IAddressService.cs +++ b/backend/dal/Services/Admin/IAddressService.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using Pims.Dal.Entities; namespace Pims.Dal.Services.Admin diff --git a/backend/dal/Services/Admin/IAgencyService.cs b/backend/dal/Services/Admin/IAgencyService.cs index 0699c15b35..fa6fbef1ea 100644 --- a/backend/dal/Services/Admin/IAgencyService.cs +++ b/backend/dal/Services/Admin/IAgencyService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface IAgencyService : IBaseService { - IEnumerable GetAllNoTracking(); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IBaseService`.cs b/backend/dal/Services/Admin/IBaseService`.cs index 359fc2f327..b492efa993 100644 --- a/backend/dal/Services/Admin/IBaseService`.cs +++ b/backend/dal/Services/Admin/IBaseService`.cs @@ -9,13 +9,6 @@ namespace Pims.Dal.Services.Admin public interface IBaseService where ET : BaseEntity { #region Methods - /// - /// Copies the values from the 'source' into the 'destination' so that EF can update appropriately. - /// - /// - /// - void SetCurrentValues(ET source, ET destination); - /// /// Find the entity with the specified key in the datasource. /// diff --git a/backend/dal/Services/Admin/IBuildingConstructionTypeService.cs b/backend/dal/Services/Admin/IBuildingConstructionTypeService.cs index c67c88991d..01226fabfc 100644 --- a/backend/dal/Services/Admin/IBuildingConstructionTypeService.cs +++ b/backend/dal/Services/Admin/IBuildingConstructionTypeService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface IBuildingConstructionTypeService : IBaseService { - IEnumerable GetAllNoTracking(); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IBuildingPredominateUseService.cs b/backend/dal/Services/Admin/IBuildingPredominateUseService.cs index 320d3751ae..2c0ecc8b58 100644 --- a/backend/dal/Services/Admin/IBuildingPredominateUseService.cs +++ b/backend/dal/Services/Admin/IBuildingPredominateUseService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface IBuildingPredominateUseService : IBaseService { - IEnumerable GetAllNoTracking(); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IBuildingService.cs b/backend/dal/Services/Admin/IBuildingService.cs index 9370d5ad2d..0806ea126a 100644 --- a/backend/dal/Services/Admin/IBuildingService.cs +++ b/backend/dal/Services/Admin/IBuildingService.cs @@ -9,9 +9,7 @@ namespace Pims.Dal.Services.Admin /// public interface IBuildingService : IBaseService { - Paged GetNoTracking(int page, int quantity, string sort); - Building GetNoTracking(int id); - Building GetByLocalIdNoTracking(string localId); + Paged Get(int page, int quantity, string sort); Building Get(int id); Building GetByLocalId(string localId); Building GetByPidAndLocalId(int pid, string localId); diff --git a/backend/dal/Services/Admin/ICityService.cs b/backend/dal/Services/Admin/ICityService.cs index 71e1a85e87..924e4ea072 100644 --- a/backend/dal/Services/Admin/ICityService.cs +++ b/backend/dal/Services/Admin/ICityService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface ICityService : IBaseService { - IEnumerable GetNoTracking(string name); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IParcelService.cs b/backend/dal/Services/Admin/IParcelService.cs index f9bbcb7552..680dfb13ab 100644 --- a/backend/dal/Services/Admin/IParcelService.cs +++ b/backend/dal/Services/Admin/IParcelService.cs @@ -9,9 +9,7 @@ namespace Pims.Dal.Services.Admin /// public interface IParcelService : IBaseService { - Paged GetNoTracking(int page, int quantity, string sort); - Parcel GetNoTracking(int id); - Parcel GetByPidNoTracking(int pid); + Paged Get(int page, int quantity, string sort); Parcel Get(int id); Parcel GetByPid(int pid); IEnumerable Add(IEnumerable parcels); diff --git a/backend/dal/Services/Admin/IPropertyClassificationService.cs b/backend/dal/Services/Admin/IPropertyClassificationService.cs index faf1867072..bb0ca4d010 100644 --- a/backend/dal/Services/Admin/IPropertyClassificationService.cs +++ b/backend/dal/Services/Admin/IPropertyClassificationService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface IPropertyClassificationService : IBaseService { - IEnumerable GetAllNoTracking(); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IPropertyStatusService.cs b/backend/dal/Services/Admin/IPropertyStatusService.cs index a232369ced..c3aff27f82 100644 --- a/backend/dal/Services/Admin/IPropertyStatusService.cs +++ b/backend/dal/Services/Admin/IPropertyStatusService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface IPropertyStatusService : IBaseService { - IEnumerable GetAllNoTracking(); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IPropertyTypeService.cs b/backend/dal/Services/Admin/IPropertyTypeService.cs index 26ab71e12d..32680c5820 100644 --- a/backend/dal/Services/Admin/IPropertyTypeService.cs +++ b/backend/dal/Services/Admin/IPropertyTypeService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface IPropertyTypeService : IBaseService { - IEnumerable GetAllNoTracking(); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IProvinceService.cs b/backend/dal/Services/Admin/IProvinceService.cs index 35c908ba7f..cbef37643d 100644 --- a/backend/dal/Services/Admin/IProvinceService.cs +++ b/backend/dal/Services/Admin/IProvinceService.cs @@ -8,7 +8,6 @@ namespace Pims.Dal.Services.Admin /// public interface IProvinceService : IBaseService { - IEnumerable GetNoTracking(); IEnumerable GetAll(); } } diff --git a/backend/dal/Services/Admin/IRoleService.cs b/backend/dal/Services/Admin/IRoleService.cs index 066b890fc2..5053fead61 100644 --- a/backend/dal/Services/Admin/IRoleService.cs +++ b/backend/dal/Services/Admin/IRoleService.cs @@ -9,8 +9,8 @@ namespace Pims.Dal.Services.Admin /// public interface IRoleService : IBaseService { - Paged GetNoTracking(int page, int quantity); - Role GetNoTracking(Guid id); + Paged Get(int page, int quantity); + Role Get(Guid id); Role GetByName(string name); int RemoveAll(Guid[] exclude); } diff --git a/backend/dal/Services/Admin/IUserService.cs b/backend/dal/Services/Admin/IUserService.cs index 736439cdb5..6e2e02e896 100644 --- a/backend/dal/Services/Admin/IUserService.cs +++ b/backend/dal/Services/Admin/IUserService.cs @@ -9,13 +9,12 @@ namespace Pims.Dal.Services.Admin /// public interface IUserService : IBaseService { - Paged GetNoTracking(int page = 1, int quantity = 10); - Paged GetNoTracking(UserFilter filter); - User GetNoTracking(Guid id); + Paged Get(int page = 1, int quantity = 10); + Paged Get(UserFilter filter); User Get(Guid id); AccessRequest UpdateAccessRequest(AccessRequest entity); - AccessRequest GetAccessRequestNoTracking(Guid id); - Paged GetAccessRequestsNoTracking(int page = 1, int quantity = 10, string sort = null, bool? isGranted = null); + AccessRequest GetAccessRequest(Guid id); + Paged GetAccessRequests(int page = 1, int quantity = 10, string sort = null, bool? isGranted = null); } } diff --git a/backend/dal/Services/BaseService.cs b/backend/dal/Services/BaseService.cs index 63917bcda4..0fad284db8 100644 --- a/backend/dal/Services/BaseService.cs +++ b/backend/dal/Services/BaseService.cs @@ -1,6 +1,5 @@ using System.Security.Claims; using Microsoft.Extensions.Logging; -using Pims.Dal.Entities; namespace Pims.Dal.Services { diff --git a/backend/dal/Services/Concrete/BuildingService.cs b/backend/dal/Services/Concrete/BuildingService.cs index 531c4c6932..ced5a4bc25 100644 --- a/backend/dal/Services/Concrete/BuildingService.cs +++ b/backend/dal/Services/Concrete/BuildingService.cs @@ -38,7 +38,7 @@ public BuildingService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public IEnumerable GetNoTracking(double neLat, double neLong, double swLat, double swLong) + public IEnumerable Get(double neLat, double neLong, double swLat, double swLong) { // Check if user has the ability to view sensitive properties. var userAgencies = this.User.GetAgencies(); @@ -66,7 +66,7 @@ public IEnumerable GetNoTracking(double neLat, double neLong, double s /// /// /// - public IEnumerable GetNoTracking(BuildingFilter filter) + public IEnumerable Get(BuildingFilter filter) { filter.ThrowIfNull(nameof(filter)); @@ -147,7 +147,7 @@ public IEnumerable GetNoTracking(BuildingFilter filter) /// /// Entity does not exist in the datasource. /// - public Building GetNoTracking(int id) + public Building Get(int id) { // Check if user has the ability to view sensitive properties. var userAgencies = this.User.GetAgencies(); @@ -216,7 +216,7 @@ public Building Update(Building building) entity.UpdatedById = this.User.GetUserId(); entity.UpdatedOn = DateTime.UtcNow; - this.Context.Buildings.Update(entity); + this.Context.Buildings.Update(entity); // TODO: Must detach entity before returning it. this.Context.CommitTransaction(); return entity; } diff --git a/backend/dal/Services/Concrete/LookupService.cs b/backend/dal/Services/Concrete/LookupService.cs index 5661ee755c..e9453bc688 100644 --- a/backend/dal/Services/Concrete/LookupService.cs +++ b/backend/dal/Services/Concrete/LookupService.cs @@ -26,7 +26,7 @@ public LookupService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// Get all agencies sorted by SortOrder and Name /// - public IEnumerable GetAgenciesNoTracking() + public IEnumerable GetAgencies() { return this.Context.Agencies.AsNoTracking().OrderBy(a => a.SortOrder).ThenBy(a => a.Name).ToArray(); } @@ -34,7 +34,7 @@ public IEnumerable GetAgenciesNoTracking() /// /// Get all building construction types sorted by SortOrder and Name /// - public IEnumerable GetBuildingConstructionTypesNoTracking() + public IEnumerable GetBuildingConstructionTypes() { return this.Context.BuildingConstructionTypes.AsNoTracking().OrderBy(a => a.SortOrder).ThenBy(a => a.Name).ToArray(); } @@ -42,7 +42,7 @@ public IEnumerable GetBuildingConstructionTypesNoTrack /// /// Get all predominate uses sorted by SortOrder and Name /// - public IEnumerable GetBuildingPredominateUsesNoTracking() + public IEnumerable GetBuildingPredominateUses() { return this.Context.BuildingPredominateUses.AsNoTracking().OrderBy(a => a.SortOrder).ThenBy(a => a.Name).ToArray(); } @@ -50,7 +50,7 @@ public IEnumerable GetBuildingPredominateUsesNoTracking( /// /// Get all cities sorted by SortOrder and Name /// - public IEnumerable GetCitiesNoTracking() + public IEnumerable GetCities() { return this.Context.Cities.AsNoTracking().OrderBy(a => a.SortOrder).ThenBy(a => a.Name).ToArray(); } @@ -58,7 +58,7 @@ public IEnumerable GetCitiesNoTracking() /// /// Get all property classifications sorted by SortOrder and Name /// - public IEnumerable GetPropertyClassificationsNoTracking() + public IEnumerable GetPropertyClassifications() { return this.Context.PropertyClassifications.AsNoTracking().OrderBy(a => a.SortOrder).ThenBy(a => a.Name).ToArray(); } @@ -66,7 +66,7 @@ public IEnumerable GetPropertyClassificationsNoTracking( /// /// Get all property status sorted by SortOrder and Name /// - public IEnumerable GetPropertyStatusNoTracking() + public IEnumerable GetPropertyStatus() { return this.Context.PropertyStatus.AsNoTracking().OrderBy(a => a.SortOrder).ThenBy(a => a.Name).ToArray(); } @@ -74,7 +74,7 @@ public IEnumerable GetPropertyStatusNoTracking() /// /// Get all property types sorted by SortOrder and Name /// - public IEnumerable GetPropertyTypesNoTracking() + public IEnumerable GetPropertyTypes() { return this.Context.PropertyTypes.AsNoTracking().OrderBy(a => a.SortOrder).ThenBy(a => a.Name).ToArray(); } @@ -82,7 +82,7 @@ public IEnumerable GetPropertyTypesNoTracking() /// /// Get all provinces sorted by SortOrder and Name /// - public IEnumerable GetProvincesNoTracking() + public IEnumerable GetProvinces() { return this.Context.Provinces.AsNoTracking().OrderBy(a => a.Name).ToArray(); } @@ -90,7 +90,7 @@ public IEnumerable GetProvincesNoTracking() /// /// Get all roles sorted by SortOrder and Name /// - public IEnumerable GetRolesNoTracking() + public IEnumerable GetRoles() { return this.Context.Roles.AsNoTracking().OrderBy(a => a.Name).ToArray(); } diff --git a/backend/dal/Services/Concrete/ParcelService.cs b/backend/dal/Services/Concrete/ParcelService.cs index a9917607c2..2dc8642103 100644 --- a/backend/dal/Services/Concrete/ParcelService.cs +++ b/backend/dal/Services/Concrete/ParcelService.cs @@ -38,7 +38,7 @@ public ParcelService(PimsContext dbContext, ClaimsPrincipal user, ILogger /// /// - public IEnumerable GetNoTracking(double neLat, double neLong, double swLat, double swLong) + public IEnumerable Get(double neLat, double neLong, double swLat, double swLong) { // Check if user has the ability to view sensitive properties. var userAgencies = this.User.GetAgencies(); @@ -62,7 +62,7 @@ public IEnumerable GetNoTracking(double neLat, double neLong, double swL /// /// /// - public IEnumerable GetNoTracking(ParcelFilter filter) + public IEnumerable Get(ParcelFilter filter) { filter.ThrowIfNull(nameof(filter)); @@ -140,7 +140,7 @@ public IEnumerable GetNoTracking(ParcelFilter filter) /// /// Entity does not exist in the datasource. /// - public Parcel GetNoTracking(int id) + public Parcel Get(int id) { // Check if user has the ability to view sensitive properties. var userAgencies = this.User.GetAgencies(); @@ -186,7 +186,6 @@ public Parcel Add(Parcel parcel) this.Context.Parcels.ThrowIfNotUnique(parcel); - parcel.CreatedById = this.User.GetUserId(); parcel.AgencyId = agency_id; this.Context.Parcels.Add(parcel); this.Context.CommitTransaction(); @@ -219,6 +218,7 @@ public Parcel Update(Parcel parcel) if (existingParcel.AgencyId != parcel.AgencyId) throw new NotAuthorizedException("Parcel cannot be transferred to the specified agency."); this.Context.Parcels.ThrowIfNotUnique(parcel); + this.Context.Update(parcel); this.Context.CommitTransaction(); return parcel; diff --git a/backend/dal/Services/Concrete/UserService.cs b/backend/dal/Services/Concrete/UserService.cs index 0f8f487fd8..4ecd4f921a 100644 --- a/backend/dal/Services/Concrete/UserService.cs +++ b/backend/dal/Services/Concrete/UserService.cs @@ -117,7 +117,7 @@ public IEnumerable GetAgencies(Guid userId) { var user = this.Context.Users.Include(u => u.Agencies).ThenInclude(a => a.Agency).ThenInclude(a => a.Children).Single(u => u.Id == userId) ?? throw new KeyNotFoundException(); var agencies = user.Agencies.Select(a => a.AgencyId).ToList(); - agencies.AddRange(user.Agencies.SelectMany(a => a.Agency.Children).Select(a => a.Id)); + agencies.AddRange(user.Agencies.SelectMany(a => a.Agency?.Children).Select(a => a.Id)); return agencies.ToArray(); } diff --git a/backend/dal/Services/IBuildingService.cs b/backend/dal/Services/IBuildingService.cs index d36847e8a0..9e29ee04aa 100644 --- a/backend/dal/Services/IBuildingService.cs +++ b/backend/dal/Services/IBuildingService.cs @@ -9,9 +9,9 @@ namespace Pims.Dal.Services /// public interface IBuildingService { - IEnumerable GetNoTracking(double neLat, double neLong, double swLat, double swLong); - IEnumerable GetNoTracking(BuildingFilter filter); - Building GetNoTracking(int id); + IEnumerable Get(double neLat, double neLong, double swLat, double swLong); + IEnumerable Get(BuildingFilter filter); + Building Get(int id); Building Add(Building parcel); Building Update(Building parcel); void Remove(Building parcel); diff --git a/backend/dal/Services/ILookupService.cs b/backend/dal/Services/ILookupService.cs index 69a0b703ec..2fa2f0bd30 100644 --- a/backend/dal/Services/ILookupService.cs +++ b/backend/dal/Services/ILookupService.cs @@ -8,14 +8,14 @@ namespace Pims.Dal.Services /// public interface ILookupService { - IEnumerable GetAgenciesNoTracking(); - IEnumerable GetCitiesNoTracking(); - IEnumerable GetProvincesNoTracking(); - IEnumerable GetPropertyStatusNoTracking(); - IEnumerable GetPropertyClassificationsNoTracking(); - IEnumerable GetPropertyTypesNoTracking(); - IEnumerable GetBuildingConstructionTypesNoTracking(); - IEnumerable GetBuildingPredominateUsesNoTracking(); - IEnumerable GetRolesNoTracking(); + IEnumerable GetAgencies(); + IEnumerable GetCities(); + IEnumerable GetProvinces(); + IEnumerable GetPropertyStatus(); + IEnumerable GetPropertyClassifications(); + IEnumerable GetPropertyTypes(); + IEnumerable GetBuildingConstructionTypes(); + IEnumerable GetBuildingPredominateUses(); + IEnumerable GetRoles(); } } diff --git a/backend/dal/Services/IParcelService.cs b/backend/dal/Services/IParcelService.cs index 8d6c0deda0..10c9a2870e 100644 --- a/backend/dal/Services/IParcelService.cs +++ b/backend/dal/Services/IParcelService.cs @@ -9,9 +9,9 @@ namespace Pims.Dal.Services /// public interface IParcelService { - IEnumerable GetNoTracking(double neLat, double neLong, double swLat, double swLong); - IEnumerable GetNoTracking(ParcelFilter filter); - Parcel GetNoTracking(int id); + IEnumerable Get(double neLat, double neLong, double swLat, double swLong); + IEnumerable Get(ParcelFilter filter); + Parcel Get(int id); Parcel Add(Parcel parcel); Parcel Update(Parcel parcel); void Remove(Parcel parcel); diff --git a/backend/entities/Comparers/AgencyIdComparer.cs b/backend/entities/Comparers/AgencyIdComparer.cs new file mode 100644 index 0000000000..56bec7fac7 --- /dev/null +++ b/backend/entities/Comparers/AgencyIdComparer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Dal.Entities.Comparers +{ + public class AgencyIdComparer : IEqualityComparer + { + public bool Equals([AllowNull] Agency x, [AllowNull] Agency y) + { + return (x == null || y == null) ? false : GetHashCode(x) == GetHashCode(y); + } + + public int GetHashCode([DisallowNull] Agency obj) + { + var hash = new HashCode(); + hash.Add(obj.Id); + return hash.ToHashCode(); + } + } +} diff --git a/backend/entities/Comparers/RoleIdComparer.cs b/backend/entities/Comparers/RoleIdComparer.cs new file mode 100644 index 0000000000..f931a6098d --- /dev/null +++ b/backend/entities/Comparers/RoleIdComparer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Dal.Entities.Comparers +{ + public class RoleIdComparer : IEqualityComparer + { + public bool Equals([AllowNull] Role x, [AllowNull] Role y) + { + return (x == null || y == null) ? false : GetHashCode(x) == GetHashCode(y); + } + + public int GetHashCode([DisallowNull] Role obj) + { + var hash = new HashCode(); + hash.Add(obj.Id); + return hash.ToHashCode(); + } + } +} diff --git a/backend/entities/Comparers/UserAgencyAgencyIdComparer.cs b/backend/entities/Comparers/UserAgencyAgencyIdComparer.cs new file mode 100644 index 0000000000..7b63a480a7 --- /dev/null +++ b/backend/entities/Comparers/UserAgencyAgencyIdComparer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Dal.Entities.Comparers +{ + public class UserAgencyAgencyIdComparer : IEqualityComparer + { + public bool Equals([AllowNull] UserAgency x, [AllowNull] UserAgency y) + { + return (x == null || y == null) ? false : GetHashCode(x) == GetHashCode(y); + } + + public int GetHashCode([DisallowNull] UserAgency obj) + { + var hash = new HashCode(); + hash.Add(obj.AgencyId); + return hash.ToHashCode(); + } + } +} diff --git a/backend/entities/Comparers/UserRoleRoleIdComparer.cs b/backend/entities/Comparers/UserRoleRoleIdComparer.cs new file mode 100644 index 0000000000..c248da7ab4 --- /dev/null +++ b/backend/entities/Comparers/UserRoleRoleIdComparer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Pims.Dal.Entities.Comparers +{ + public class UserRoleRoleIdComparer : IEqualityComparer + { + public bool Equals([AllowNull] UserRole x, [AllowNull] UserRole y) + { + return (x == null || y == null) ? false : GetHashCode(x) == GetHashCode(y); + } + + public int GetHashCode([DisallowNull] UserRole obj) + { + var hash = new HashCode(); + hash.Add(obj.RoleId); + return hash.ToHashCode(); + } + } +} diff --git a/backend/entities/ParcelEvaluation.cs b/backend/entities/ParcelEvaluation.cs index 104624b053..ed2792266d 100644 --- a/backend/entities/ParcelEvaluation.cs +++ b/backend/entities/ParcelEvaluation.cs @@ -68,7 +68,6 @@ public ParcelEvaluation(int fiscalYear, Parcel parcel) this.ParcelId = parcel?.Id ?? throw new ArgumentNullException(nameof(parcel)); this.Parcel = parcel; - parcel.Evaluations.Add(this); } /// diff --git a/backend/entities/Pims.Dal.Entities.csproj b/backend/entities/Pims.Dal.Entities.csproj index a9a5b22561..b179bcedf2 100644 --- a/backend/entities/Pims.Dal.Entities.csproj +++ b/backend/entities/Pims.Dal.Entities.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 diff --git a/backend/test/Controllers/Admin/ParcelControllerTest.cs b/backend/test/Controllers/Admin/ParcelControllerTest.cs index 06f02d9e82..c5f787e5b9 100644 --- a/backend/test/Controllers/Admin/ParcelControllerTest.cs +++ b/backend/test/Controllers/Admin/ParcelControllerTest.cs @@ -1,16 +1,21 @@ using AutoMapper; using Entity = Pims.Dal.Entities; using Microsoft.AspNetCore.Mvc; +using Model = Pims.Api.Areas.Admin.Models.Parcel; using Moq; using Pims.Api.Areas.Admin.Controllers; using Pims.Api.Test.Helpers; using Pims.Dal.Security; using Pims.Dal.Services.Admin; -using System; +using System.Linq; using Xunit; namespace Pims.Api.Test.Controllers.Admin { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("area", "admine")] + [Trait("group", "parcel")] public class ParcelControllerTest { #region Constructors @@ -32,18 +37,285 @@ public void DeleteParcel_Success() var mapper = helper.GetService(); var parcel = EntityHelper.CreateParcel(1, 1, 1, 1); service.Setup(m => m.Parcel.Remove(It.IsAny())); - var modelToDelete = mapper.Map(parcel); + var modelToDelete = mapper.Map(parcel); // Act - var result = controller.DeleteParcel(Guid.NewGuid(), modelToDelete); + var result = controller.DeleteParcel(parcel.Id, modelToDelete); // Assert var actionResult = Assert.IsType(result); - var actualParcel = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(parcel), actualParcel); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(mapper.Map(parcel), actualParcel); service.Verify(m => m.Parcel.Remove(It.IsAny()), Times.Once()); } #endregion + + #region AddParcel + [Fact] + public void AddParcel_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var parcel = EntityHelper.CreateParcel(1); + service.Setup(m => m.Parcel.Add(It.IsAny())).Returns(parcel); + var model = mapper.Map(parcel); + + // Act + var result = controller.AddParcel(model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Equal(201, actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + service.Verify(m => m.Parcel.Add(It.IsAny()), Times.Once()); + } + #endregion + + #region UpdateParcel + [Fact] + public void UpdateParcel_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var parcel = EntityHelper.CreateParcel(1, 1, 1, 1); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcel); + service.Setup(m => m.Parcel.Update(It.IsAny())).Returns(parcel); + var model = mapper.Map(parcel); + + // Act + var result = controller.UpdateParcel(parcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(model, actualParcel); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); + } + + [Fact] + public void UpdateParcel_AddEvaluation_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var existingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + existingParcel.Evaluations.Add(new Entity.ParcelEvaluation(2020, existingParcel) { EstimatedValue = 12345.45f }); + + var updatingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + updatingParcel.Evaluations.Add(new Entity.ParcelEvaluation(2020, updatingParcel) { EstimatedValue = 12345.45f }); + updatingParcel.Evaluations.Add(new Entity.ParcelEvaluation(2019, updatingParcel) { EstimatedValue = 99999.33f }); + + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(existingParcel); + service.Setup(m => m.Parcel.Update(It.IsAny())).Returns(existingParcel); + var model = mapper.Map(updatingParcel); + + // Act + var result = controller.UpdateParcel(updatingParcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(model.Evaluations.Count(), actualParcel.Evaluations.Count()); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); + } + + [Fact] + public void UpdateParcel_UpdateEvaluation_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var existingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + existingParcel.Evaluations.Add(new Entity.ParcelEvaluation(2020, existingParcel) { EstimatedValue = 12345.45f }); + + var updatingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + updatingParcel.Evaluations.Add(new Entity.ParcelEvaluation(2020, updatingParcel) { EstimatedValue = 10000.45f }); + + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(existingParcel); + service.Setup(m => m.Parcel.Update(It.IsAny())).Returns(existingParcel); + var model = mapper.Map(updatingParcel); + + // Act + var result = controller.UpdateParcel(updatingParcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(model.Evaluations.Count(), actualParcel.Evaluations.Count()); + Assert.Equal(model.Evaluations.First().EstimatedValue, actualParcel.Evaluations.First().EstimatedValue); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); + } + + [Fact] + public void UpdateParcel_AddBuilding_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var existingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var existingBuilding = EntityHelper.CreateBuilding(existingParcel, 1, "0001"); + existingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2020, existingBuilding) { EstimatedValue = 1000.33f }); + existingParcel.Buildings.Add(existingBuilding); + + var updatingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var updatingBuilding1 = EntityHelper.CreateBuilding(updatingParcel, 1, "0001"); + updatingBuilding1.Evaluations.Add(new Entity.BuildingEvaluation(2020, updatingBuilding1) { EstimatedValue = 9999.33f }); + updatingParcel.Buildings.Add(updatingBuilding1); + + var updatingBuilding2 = EntityHelper.CreateBuilding(updatingParcel, 2, "0002"); + updatingBuilding2.Evaluations.Add(new Entity.BuildingEvaluation(2020, updatingBuilding2) { EstimatedValue = 9999.33f }); + updatingParcel.Buildings.Add(updatingBuilding2); + + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(existingParcel); + service.Setup(m => m.Parcel.Update(It.IsAny())).Returns(existingParcel); + var model = mapper.Map(updatingParcel); + + // Act + var result = controller.UpdateParcel(updatingParcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(model.Buildings.Count(), actualParcel.Buildings.Count()); + Assert.Equal(model.Buildings.First().LocalId, actualParcel.Buildings.First().LocalId); + Assert.Equal(model.Buildings.First().Evaluations.Count(), actualParcel.Buildings.First().Evaluations.Count()); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); + } + + [Fact] + public void UpdateParcel_UpdateBuilding_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var existingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var existingBuilding = EntityHelper.CreateBuilding(existingParcel, 1, "0001"); + existingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2020, existingBuilding) { EstimatedValue = 1000.33f }); + existingParcel.Buildings.Add(existingBuilding); + + var updatingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var updatingBuilding = EntityHelper.CreateBuilding(updatingParcel, 1, "0002"); + updatingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2020, updatingBuilding) { EstimatedValue = 9999.33f }); + updatingParcel.Buildings.Add(updatingBuilding); + + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(existingParcel); + service.Setup(m => m.Parcel.Update(It.IsAny())).Returns(existingParcel); + var model = mapper.Map(updatingParcel); + + // Act + var result = controller.UpdateParcel(updatingParcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(model.Buildings.Count(), actualParcel.Buildings.Count()); + Assert.Equal(model.Buildings.First().LocalId, actualParcel.Buildings.First().LocalId); + Assert.Equal(model.Buildings.First().Evaluations.Count(), actualParcel.Buildings.First().Evaluations.Count()); + Assert.Equal(model.Buildings.First().Evaluations.First().EstimatedValue, actualParcel.Buildings.First().Evaluations.First().EstimatedValue); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); + } + + [Fact] + public void UpdateParcel_AddBuildingEvaluation_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var existingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var existingBuilding = EntityHelper.CreateBuilding(existingParcel, 1, "0001"); + existingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2020, existingBuilding) { EstimatedValue = 1000.33f }); + existingParcel.Buildings.Add(existingBuilding); + + var updatingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var updatingBuilding = EntityHelper.CreateBuilding(updatingParcel, 1, "0002"); + updatingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2020, updatingBuilding) { EstimatedValue = 1000.33f }); + updatingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2021, updatingBuilding) { EstimatedValue = 2342.33f }); + updatingParcel.Buildings.Add(updatingBuilding); + + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(existingParcel); + service.Setup(m => m.Parcel.Update(It.IsAny())).Returns(existingParcel); + var model = mapper.Map(updatingParcel); + + // Act + var result = controller.UpdateParcel(updatingParcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(model.Buildings.Count(), actualParcel.Buildings.Count()); + Assert.Equal(model.Buildings.First().LocalId, actualParcel.Buildings.First().LocalId); + Assert.Equal(model.Buildings.First().Evaluations.Count(), actualParcel.Buildings.First().Evaluations.Count()); + Assert.Equal(model.Buildings.First().Evaluations.First().EstimatedValue, actualParcel.Buildings.First().Evaluations.First().EstimatedValue); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); + } + + [Fact] + public void UpdateParcel_UpdateBuildingEvaluation_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var existingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var existingBuilding = EntityHelper.CreateBuilding(existingParcel, 1, "0001"); + existingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2020, existingBuilding) { EstimatedValue = 1000.33f }); + existingParcel.Buildings.Add(existingBuilding); + + var updatingParcel = EntityHelper.CreateParcel(1, 1, 1, 1); + var updatingBuilding = EntityHelper.CreateBuilding(updatingParcel, 1, "0002"); + updatingBuilding.Evaluations.Add(new Entity.BuildingEvaluation(2020, updatingBuilding) { EstimatedValue = 9999.33f }); + updatingParcel.Buildings.Add(updatingBuilding); + + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(existingParcel); + service.Setup(m => m.Parcel.Update(It.IsAny())).Returns(existingParcel); + var model = mapper.Map(updatingParcel); + + // Act + var result = controller.UpdateParcel(updatingParcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(model.Buildings.Count(), actualParcel.Buildings.Count()); + Assert.Equal(model.Buildings.First().LocalId, actualParcel.Buildings.First().LocalId); + Assert.Equal(model.Buildings.First().Evaluations.Count(), actualParcel.Buildings.First().Evaluations.Count()); + Assert.Equal(model.Buildings.First().Evaluations.First().EstimatedValue, actualParcel.Buildings.First().Evaluations.First().EstimatedValue); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); + } + #endregion #endregion } } diff --git a/backend/test/Controllers/Admin/UserControllerTest.cs b/backend/test/Controllers/Admin/UserControllerTest.cs index 61ba8d6b4d..2e8e4520c3 100644 --- a/backend/test/Controllers/Admin/UserControllerTest.cs +++ b/backend/test/Controllers/Admin/UserControllerTest.cs @@ -1,18 +1,21 @@ -using Xunit; -using System; -using Pims.Dal.Services.Admin; -using Pims.Api.Test.Helpers; -using Pims.Api.Areas.Admin.Controllers; -using Moq; -using Model = Pims.Api.Models; -using Microsoft.AspNetCore.Mvc; -using Entity = Pims.Dal.Entities; using AutoMapper; -using AdminModel = Pims.Api.Areas.Admin.Models; +using Entity = Pims.Dal.Entities; +using Microsoft.AspNetCore.Mvc; +using Model = Pims.Api.Areas.Admin.Models.User; +using Moq; +using Pims.Api.Areas.Admin.Controllers; +using Pims.Api.Test.Helpers; using Pims.Dal.Security; +using Pims.Dal.Services.Admin; +using System; +using Xunit; namespace PimsApi.Test.Admin.Controllers { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("area", "admin")] + [Trait("group", "user")] public class UserControllerTest { #region Variables @@ -84,15 +87,14 @@ public UserControllerTest() public void GetAccessRequests_Success() { // Arrange - var user = PrincipalHelper.CreateForRole("admin-users"); var helper = new TestHelper(); - var controller = helper.CreateController(user); + var controller = helper.CreateController(Permissions.AdminUsers); var mapper = helper.GetService(); var service = helper.GetService>(); var expectedAccessRequests = new Entity.AccessRequest[] { _expectedAccessRequest }; var expectedPagedAccessRequests = new Entity.Models.Paged(expectedAccessRequests); - service.Setup(m => m.User.GetAccessRequestsNoTracking(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(expectedPagedAccessRequests); + service.Setup(m => m.User.GetAccessRequests(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(expectedPagedAccessRequests); // Act var result = controller.GetAccessRequests(1, 10); @@ -101,22 +103,21 @@ public void GetAccessRequests_Success() var actionResult = Assert.IsType(result); var actualAccessRequest = Assert.IsType>(actionResult.Value); Assert.Equal(mapper.Map(expectedAccessRequests), actualAccessRequest.Items); - service.Verify(m => m.User.GetAccessRequestsNoTracking(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + service.Verify(m => m.User.GetAccessRequests(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void GetAccessRequests_PageMin_Success() { // Arrange - var user = PrincipalHelper.CreateForRole("admin-users"); var helper = new TestHelper(); - var controller = helper.CreateController(user); + var controller = helper.CreateController(Permissions.AdminUsers); var mapper = helper.GetService(); var service = helper.GetService>(); var expectedAccessRequests = new Entity.AccessRequest[] { _expectedAccessRequest }; var expectedPagedAccessRequests = new Entity.Models.Paged(expectedAccessRequests); - service.Setup(m => m.User.GetAccessRequestsNoTracking(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(expectedPagedAccessRequests); + service.Setup(m => m.User.GetAccessRequests(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(expectedPagedAccessRequests); // Act var result = controller.GetAccessRequests(-1, -10); @@ -125,22 +126,21 @@ public void GetAccessRequests_PageMin_Success() var actionResult = Assert.IsType(result); var actualAccessRequest = Assert.IsType>(actionResult.Value); Assert.Equal(mapper.Map(expectedAccessRequests), actualAccessRequest.Items); - service.Verify(m => m.User.GetAccessRequestsNoTracking(1, 1, null, null), Times.Once()); + service.Verify(m => m.User.GetAccessRequests(1, 1, null, null), Times.Once()); } [Fact] public void GetAccessRequests_PageMax_Success() { // Arrange - var user = PrincipalHelper.CreateForRole("admin-users"); var helper = new TestHelper(); - var controller = helper.CreateController(user); + var controller = helper.CreateController(Permissions.AdminUsers); var mapper = helper.GetService(); var service = helper.GetService>(); var expectedAccessRequests = new Entity.AccessRequest[] { _expectedAccessRequest }; var expectedPagedAccessRequests = new Entity.Models.Paged(expectedAccessRequests); - service.Setup(m => m.User.GetAccessRequestsNoTracking(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(expectedPagedAccessRequests); + service.Setup(m => m.User.GetAccessRequests(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(expectedPagedAccessRequests); // Act var result = controller.GetAccessRequests(2, 100); @@ -149,7 +149,7 @@ public void GetAccessRequests_PageMax_Success() var actionResult = Assert.IsType(result); var actualAccessRequest = Assert.IsType>(actionResult.Value); Assert.Equal(mapper.Map(expectedAccessRequests), actualAccessRequest.Items); - service.Verify(m => m.User.GetAccessRequestsNoTracking(2, 20, null, null), Times.Once()); + service.Verify(m => m.User.GetAccessRequests(2, 20, null, null), Times.Once()); } #endregion @@ -166,16 +166,16 @@ public void GetUsers() var service = helper.GetService>(); var expectedUsers = new Entity.User[] { _expectedUsers }; var expectedPagedUsers = new Entity.Models.Paged(expectedUsers); - service.Setup(m => m.User.GetNoTracking(It.IsAny())).Returns(expectedPagedUsers); + service.Setup(m => m.User.Get(It.IsAny())).Returns(expectedPagedUsers); // Act var result = controller.GetUsers(); // Assert var actionResult = Assert.IsType(result); - var actualAccessRequest = Assert.IsType>(actionResult.Value); - Assert.Equal(mapper.Map(expectedUsers), actualAccessRequest.Items); - service.Verify(m => m.User.GetNoTracking(It.IsAny()), Times.Once()); + var actualAccessRequest = Assert.IsType>(actionResult.Value); + Assert.Equal(mapper.Map(expectedUsers), actualAccessRequest.Items); + service.Verify(m => m.User.Get(It.IsAny()), Times.Once()); } #endregion diff --git a/backend/test/Controllers/Keycloak/RoleControllerTest.cs b/backend/test/Controllers/Keycloak/RoleControllerTest.cs index 2e06507039..684239f692 100644 --- a/backend/test/Controllers/Keycloak/RoleControllerTest.cs +++ b/backend/test/Controllers/Keycloak/RoleControllerTest.cs @@ -3,7 +3,7 @@ using Pims.Api.Test.Helpers; using Pims.Api.Areas.Keycloak.Controllers; using Moq; -using Model = Pims.Api.Areas.Keycloak.Models; +using Model = Pims.Api.Areas.Keycloak.Models.Role; using Microsoft.AspNetCore.Mvc; using Entity = Pims.Dal.Entities; using AutoMapper; @@ -13,6 +13,10 @@ namespace PimsApi.Test.Keycloak.Controllers { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("area", "keycloak")] + [Trait("group", "role")] public class RoleControllerTest { #region Variables diff --git a/backend/test/Controllers/Keycloak/UserControllerTest.cs b/backend/test/Controllers/Keycloak/UserControllerTest.cs index f66fa8e912..37ad292845 100644 --- a/backend/test/Controllers/Keycloak/UserControllerTest.cs +++ b/backend/test/Controllers/Keycloak/UserControllerTest.cs @@ -1,18 +1,23 @@ -using Xunit; -using System; -using Pims.Api.Test.Helpers; -using Pims.Api.Areas.Keycloak.Controllers; -using Moq; -using Model = Pims.Api.Areas.Keycloak.Models; -using Microsoft.AspNetCore.Mvc; -using Entity = Pims.Dal.Entities; using AutoMapper; +using Entity = Pims.Dal.Entities; +using Microsoft.AspNetCore.Mvc; +using Model = Pims.Api.Areas.Keycloak.Models.User; +using Moq; +using Pims.Api.Areas.Keycloak.Controllers; +using Pims.Api.Test.Helpers; using Pims.Dal.Keycloak; -using System.Threading.Tasks; +using Pims.Dal.Security; +using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; namespace PimsApi.Test.Keycloak.Controllers { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("area", "keycloak")] + [Trait("group", "user")] public class UserControllerTest { #region Variables @@ -30,22 +35,22 @@ public UserControllerTest() public async void SyncUserAsync_Success() { // Arrange - var user = PrincipalHelper.CreateForRole("admin-users"); var helper = new TestHelper(); - var controller = helper.CreateController(user); + var controller = helper.CreateController(Permissions.AdminUsers); var mapper = helper.GetService(); var service = helper.GetService>(); - var euser = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); - service.Setup(m => m.SyncUserAsync(It.IsAny())).Returns(Task.FromResult(euser)); + var user = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); + service.Setup(m => m.SyncUserAsync(It.IsAny())).Returns(Task.FromResult(user)); // Act - var result = await controller.SyncUserAsync(euser.Id); + var result = await controller.SyncUserAsync(user.Id); // Assert var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); var data = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(euser), data); + Assert.Equal(mapper.Map(user), data); service.Verify(m => m.SyncUserAsync(It.IsAny()), Times.Once()); } #endregion @@ -55,23 +60,23 @@ public async void SyncUserAsync_Success() public async void GetUsersAsync_Success() { // Arrange - var user = PrincipalHelper.CreateForRole("admin-users"); var helper = new TestHelper(); - var controller = helper.CreateController(user); + var controller = helper.CreateController(Permissions.AdminUsers); var mapper = helper.GetService(); var service = helper.GetService>(); - var euser = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); - var eusers = new[] { euser }; - service.Setup(m => m.GetUsersAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult((IEnumerable)eusers)); + var user = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); + var users = new[] { user }; + service.Setup(m => m.GetUsersAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult((IEnumerable)users)); // Act var result = await controller.GetUsersAsync(1, 10); // Assert var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); var data = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(eusers), data); + Assert.Equal(mapper.Map(users), data); service.Verify(m => m.GetUsersAsync(1, 10, It.IsAny()), Times.Once()); } #endregion @@ -81,22 +86,22 @@ public async void GetUsersAsync_Success() public async void GetUserAsync_Success() { // Arrange - var user = PrincipalHelper.CreateForRole("admin-users"); var helper = new TestHelper(); - var controller = helper.CreateController(user); + var controller = helper.CreateController(Permissions.AdminUsers); var mapper = helper.GetService(); var service = helper.GetService>(); - var euser = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); - service.Setup(m => m.GetUserAsync(It.IsAny())).Returns(Task.FromResult(euser)); + var user = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); + service.Setup(m => m.GetUserAsync(It.IsAny())).Returns(Task.FromResult(user)); // Act - var result = await controller.GetUserAsync(euser.Id); + var result = await controller.GetUserAsync(user.Id); // Assert var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); var data = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(euser), data); + Assert.Equal(mapper.Map(user), data); service.Verify(m => m.GetUserAsync(It.IsAny()), Times.Once()); } #endregion @@ -106,30 +111,23 @@ public async void GetUserAsync_Success() public async void UpdateUserAsync_Success() { // Arrange - var user = PrincipalHelper.CreateForRole("admin-users"); var helper = new TestHelper(); - var controller = helper.CreateController(user); + var controller = helper.CreateController(Permissions.AdminUsers); var mapper = helper.GetService(); var service = helper.GetService>(); - var euser = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); - service.Setup(m => m.UpdateUserAsync(It.IsAny())).Returns(Task.FromResult(euser)); - var model = new Model.Update.UserModel() - { - DisplayName = euser.DisplayName, - FirstName = euser.FirstName, - LastName = euser.LastName, - Email = euser.Email, - IsDisabled = euser.IsDisabled - }; + var user = new Entity.User(Guid.NewGuid(), "test", "test@test.com"); + service.Setup(m => m.UpdateUserAsync(It.IsAny())).Returns(Task.FromResult(user)); + var model = mapper.Map(user); // Act - var result = await controller.UpdateUserAsync(euser.Id, model); + var result = await controller.UpdateUserAsync(user.Id, model); // Assert var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); var data = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(euser), data); + Assert.Equal(mapper.Map(user), data); service.Verify(m => m.UpdateUserAsync(It.IsAny()), Times.Once()); } #endregion diff --git a/backend/test/Controllers/CodeLookupControllerTest.cs b/backend/test/Controllers/LookupControllerTest.cs similarity index 85% rename from backend/test/Controllers/CodeLookupControllerTest.cs rename to backend/test/Controllers/LookupControllerTest.cs index 5ed7db32ce..c14ae3ab69 100644 --- a/backend/test/Controllers/CodeLookupControllerTest.cs +++ b/backend/test/Controllers/LookupControllerTest.cs @@ -12,13 +12,16 @@ namespace Pims.Api.Test.Controllers { - public class CodeLookupControllerTest + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("group", "lookup")] + public class LookupControllerTest { #region Variables #endregion #region Constructors - public CodeLookupControllerTest() + public LookupControllerTest() { } #endregion @@ -40,7 +43,7 @@ public void GetAgencyCodes() Name = "Ministry of Health", Description = "The Ministry of Health" }; - pimsService.Setup(m => m.Lookup.GetAgenciesNoTracking()).Returns(new[] { agency }); + pimsService.Setup(m => m.Lookup.GetAgencies()).Returns(new[] { agency }); // Act var result = controller.GetAgencies(); @@ -49,7 +52,7 @@ public void GetAgencyCodes() JsonResult actionResult = Assert.IsType(result); Model.CodeModel[] resultValue = Assert.IsType(actionResult.Value); Assert.Equal(new[] { mapper.Map(agency) }, resultValue); - pimsService.Verify(m => m.Lookup.GetAgenciesNoTracking(), Times.Once()); + pimsService.Verify(m => m.Lookup.GetAgencies(), Times.Once()); } [Fact] @@ -66,7 +69,7 @@ public void GetPropertyClassificationCodes() { Name = "Surplus Active", }; - pimsService.Setup(m => m.Lookup.GetPropertyClassificationsNoTracking()).Returns(new[] { propertyClassification }); + pimsService.Setup(m => m.Lookup.GetPropertyClassifications()).Returns(new[] { propertyClassification }); // Act var result = controller.GetPropertyClassifications(); @@ -75,7 +78,7 @@ public void GetPropertyClassificationCodes() JsonResult actionResult = Assert.IsType(result); Model.CodeModel[] resultValue = Assert.IsType(actionResult.Value); Assert.Equal(new[] { mapper.Map(propertyClassification) }, resultValue); - pimsService.Verify(m => m.Lookup.GetPropertyClassificationsNoTracking(), Times.Once()); + pimsService.Verify(m => m.Lookup.GetPropertyClassifications(), Times.Once()); } [Fact] @@ -94,7 +97,7 @@ public void GetRoleCodes() Name = "Ministry of Health", Description = "The Ministry of Health" }; - pimsService.Setup(m => m.Lookup.GetRolesNoTracking()).Returns(new[] { role }); + pimsService.Setup(m => m.Lookup.GetRoles()).Returns(new[] { role }); // Act var result = controller.GetRoles(); @@ -103,7 +106,7 @@ public void GetRoleCodes() JsonResult actionResult = Assert.IsType(result); Model.CodeModel[] resultValue = Assert.IsType(actionResult.Value); Assert.Equal(new[] { mapper.Map(role) }, resultValue); - pimsService.Verify(m => m.Lookup.GetRolesNoTracking(), Times.Once()); + pimsService.Verify(m => m.Lookup.GetRoles(), Times.Once()); } [Fact] @@ -122,13 +125,13 @@ public void GetAll() Name = "Ministry of Health", Description = "The Ministry of Health" }; - pimsService.Setup(m => m.Lookup.GetRolesNoTracking()).Returns(new[] { role }); + pimsService.Setup(m => m.Lookup.GetRoles()).Returns(new[] { role }); var propertyClassification = new Entity.PropertyClassification { Name = "Surplus Active", }; - pimsService.Setup(m => m.Lookup.GetPropertyClassificationsNoTracking()).Returns(new[] { propertyClassification }); + pimsService.Setup(m => m.Lookup.GetPropertyClassifications()).Returns(new[] { propertyClassification }); var agency = new Entity.Agency { @@ -136,7 +139,7 @@ public void GetAll() Name = "Ministry of Health", Description = "The Ministry of Health" }; - pimsService.Setup(m => m.Lookup.GetAgenciesNoTracking()).Returns(new[] { agency }); + pimsService.Setup(m => m.Lookup.GetAgencies()).Returns(new[] { agency }); // Act var agencyResult = controller.GetAgencies(); @@ -159,9 +162,9 @@ public void GetAll() Assert.StartsWith(agenciesResult.Remove(agenciesResult.Length - 1, 1), allResult); Assert.Contains(classificationsResult[1..^1], allResult); Assert.EndsWith(rolesResult.Substring(1), allResult); - pimsService.Verify(m => m.Lookup.GetAgenciesNoTracking(), Times.Exactly(2)); - pimsService.Verify(m => m.Lookup.GetPropertyClassificationsNoTracking(), Times.Exactly(2)); - pimsService.Verify(m => m.Lookup.GetRolesNoTracking(), Times.Exactly(2)); + pimsService.Verify(m => m.Lookup.GetAgencies(), Times.Exactly(2)); + pimsService.Verify(m => m.Lookup.GetPropertyClassifications(), Times.Exactly(2)); + pimsService.Verify(m => m.Lookup.GetRoles(), Times.Exactly(2)); } #endregion } diff --git a/backend/test/Controllers/ParcelControllerTest.cs b/backend/test/Controllers/ParcelControllerTest.cs index 706da680e3..56d9492858 100644 --- a/backend/test/Controllers/ParcelControllerTest.cs +++ b/backend/test/Controllers/ParcelControllerTest.cs @@ -1,22 +1,23 @@ using AutoMapper; using Entity = Pims.Dal.Entities; +using Model = Pims.Api.Models.Parcel; using Microsoft.AspNetCore.Mvc; -using Model = Pims.Api.Models; using Moq; using Pims.Api.Controllers; using Pims.Api.Helpers.Exceptions; using Pims.Api.Test.Helpers; using Pims.Dal; using Pims.Dal.Entities.Models; -using Pims.Dal.Exceptions; using Pims.Dal.Security; using System; using System.Collections.Generic; -using System.Security.Claims; using Xunit; namespace Pims.Api.Test.Controllers { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("group", "parcel")] public class ParcelControllerTest { #region Constructors @@ -26,31 +27,6 @@ public ParcelControllerTest() #endregion #region Tests - #region DeleteParcel - [Fact] - public void DeleteParcel_Success() - { - // Arrange - var helper = new TestHelper(); - var controller = helper.CreateController(Permissions.PropertyAdd); - - var service = helper.GetService>(); - var mapper = helper.GetService(); - var parcel = EntityHelper.CreateParcel(1, 1, 1, 1); - service.Setup(m => m.Parcel.Remove(It.IsAny())); - var modelToDelete = mapper.Map(parcel); - - // Act - var result = controller.DeleteParcel(Guid.NewGuid(), modelToDelete); - - // Assert - var actionResult = Assert.IsType(result); - var actualParcel = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(parcel), actualParcel); - service.Verify(m => m.Parcel.Remove(It.IsAny()), Times.Once()); - } - #endregion - #region GetParcels [Fact] public void GetParcels_FilterLatitude_Success() @@ -63,16 +39,17 @@ public void GetParcels_FilterLatitude_Success() var mapper = helper.GetService(); var parcels = EntityHelper.CreateParcels(1, 3).ToArray(); var filter = new ParcelFilter(50, 25, 50, 20); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); // Act var result = controller.GetParcels(filter); // Assert var actionResult = Assert.IsType(result); - var actualParcels = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(parcels), actualParcels); - service.Verify(m => m.Parcel.GetNoTracking(filter), Times.Once()); + Assert.Null(actionResult.StatusCode); + var actualParcels = Assert.IsType(actionResult.Value); + Assert.Equal(mapper.Map(parcels), actualParcels); + service.Verify(m => m.Parcel.Get(filter), Times.Once()); } [Fact] @@ -86,16 +63,17 @@ public void GetParcels_FilterLongitude_Success() var mapper = helper.GetService(); var parcels = EntityHelper.CreateParcels(1, 3).ToArray(); var filter = new ParcelFilter(50, 25, 50, 25); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); // Act var result = controller.GetParcels(filter); // Assert var actionResult = Assert.IsType(result); - var actualParcels = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(parcels), actualParcels); - service.Verify(m => m.Parcel.GetNoTracking(filter), Times.Once()); + Assert.Null(actionResult.StatusCode); + var actualParcels = Assert.IsType(actionResult.Value); + Assert.Equal(mapper.Map(parcels), actualParcels); + service.Verify(m => m.Parcel.Get(filter), Times.Once()); } [Fact] @@ -112,16 +90,17 @@ public void GetParcels_FilterAgency_Success() { Agencies = new int[] { 3 } }; - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); // Act var result = controller.GetParcels(filter); // Assert var actionResult = Assert.IsType(result); - var actualParcels = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(parcels), actualParcels); - service.Verify(m => m.Parcel.GetNoTracking(filter), Times.Once()); + Assert.Null(actionResult.StatusCode); + var actualParcels = Assert.IsType(actionResult.Value); + Assert.Equal(mapper.Map(parcels), actualParcels); + service.Verify(m => m.Parcel.Get(filter), Times.Once()); } [Fact] @@ -138,16 +117,17 @@ public void GetParcels_FilterClassification_Success() { ClassificationId = 2 }; - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); // Act var result = controller.GetParcels(filter); // Assert var actionResult = Assert.IsType(result); - var actualParcels = Assert.IsType(actionResult.Value); - Assert.Equal(mapper.Map(parcels), actualParcels); - service.Verify(m => m.Parcel.GetNoTracking(filter), Times.Once()); + Assert.Null(actionResult.StatusCode); + var actualParcels = Assert.IsType(actionResult.Value); + Assert.Equal(mapper.Map(parcels), actualParcels); + service.Verify(m => m.Parcel.Get(filter), Times.Once()); } [Fact] @@ -159,16 +139,17 @@ public void GetParcels_Empty__Success() var service = helper.GetService>(); var filter = new ParcelFilter(0, 25, 10, 20); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(new Entity.Parcel[0]); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(new Entity.Parcel[0]); // Act var result = controller.GetParcels(filter); // Assert var actionResult = Assert.IsType(result); - var actualParcels = Assert.IsType(actionResult.Value); + Assert.Null(actionResult.StatusCode); + var actualParcels = Assert.IsType(actionResult.Value); Assert.Empty(actualParcels); - service.Verify(m => m.Parcel.GetNoTracking(filter), Times.Once()); + service.Verify(m => m.Parcel.Get(filter), Times.Once()); } /// @@ -187,17 +168,18 @@ public void GetParcels_Query_Success() var service = helper.GetService>(); var mapper = helper.GetService(); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); // Act var result = controller.GetParcels(); // Assert var actionResult = Assert.IsType(result); - var actualParcels = Assert.IsType(actionResult.Value); - var expectedParcels = mapper.Map(parcels); + Assert.Null(actionResult.StatusCode); + var actualParcels = Assert.IsType(actionResult.Value); + var expectedParcels = mapper.Map(parcels); Assert.Equal(expectedParcels, actualParcels); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Once()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Once()); } /// @@ -215,7 +197,7 @@ public void GetParcels_Query_NoFilter_BadRequest() // Act // Assert Assert.Throws(() => controller.GetParcels()); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Never()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Never()); } /// @@ -233,7 +215,7 @@ public void GetProperties_NoFilter_BadRequest() // Act // Assert Assert.Throws(() => controller.GetParcels(null)); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Never()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Never()); } #endregion @@ -246,18 +228,18 @@ public void GetParcel_BadRequest() var controller = helper.CreateController(Permissions.PropertyView); var service = helper.GetService>(); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Throws(); + service.Setup(m => m.Parcel.Get(It.IsAny())).Throws(); int expectedParcelId = 1; // Act // Assert Assert.Throws(() => controller.GetParcel(expectedParcelId)); - service.Verify(m => m.Parcel.GetNoTracking(expectedParcelId), Times.Once()); + service.Verify(m => m.Parcel.Get(expectedParcelId), Times.Once()); } [Fact] - public void GetParcel_Sucess() + public void GetParcel_Success() { // Arrange var helper = new TestHelper(); @@ -266,19 +248,95 @@ public void GetParcel_Sucess() var service = helper.GetService>(); var mapper = helper.GetService(); var expectedTestParcel = new Entity.Parcel(45, 45); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(expectedTestParcel); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(expectedTestParcel); int expectedParcelId = 1; // Act var result = controller.GetParcel(expectedParcelId); // Assert - var jsonResult = Assert.IsType(result); - var actualParcelDetail = Assert.IsType(jsonResult.Value); + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcelDetail = Assert.IsType(actionResult.Value); Assert.Equal(mapper.Map(expectedTestParcel), actualParcelDetail); - service.Verify(m => m.Parcel.GetNoTracking(expectedParcelId), Times.Once()); + service.Verify(m => m.Parcel.Get(expectedParcelId), Times.Once()); + } + #endregion + + #region AddParcel + [Fact] + public void AddParcel_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyAdd); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var parcel = EntityHelper.CreateParcel(1); + service.Setup(m => m.Parcel.Add(It.IsAny())); + var model = mapper.Map(parcel); + + // Act + var result = controller.AddParcel(model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Equal(201, actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + service.Verify(m => m.Parcel.Add(It.IsAny()), Times.Once()); + } + #endregion + + #region UpdateParcel + [Fact] + public void UpdateParcel_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyEdit); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var parcel = EntityHelper.CreateParcel(1); + service.Setup(m => m.Parcel.Add(It.IsAny())); + var model = mapper.Map(parcel); + + // Act + var result = controller.UpdateParcel(parcel.Id, model); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + service.Verify(m => m.Parcel.Update(It.IsAny()), Times.Once()); } + #endregion + #region DeleteParcel + [Fact] + public void DeleteParcel_Success() + { + // Arrange + var helper = new TestHelper(); + var controller = helper.CreateController(Permissions.PropertyAdd); + + var service = helper.GetService>(); + var mapper = helper.GetService(); + var parcel = EntityHelper.CreateParcel(1); + service.Setup(m => m.Parcel.Remove(It.IsAny())); + var modelToDelete = mapper.Map(parcel); + + // Act + var result = controller.DeleteParcel(parcel.Id, modelToDelete); + + // Assert + var actionResult = Assert.IsType(result); + Assert.Null(actionResult.StatusCode); + var actualParcel = Assert.IsType(actionResult.Value); + Assert.Equal(mapper.Map(parcel), actualParcel); + service.Verify(m => m.Parcel.Remove(It.IsAny()), Times.Once()); + } #endregion #endregion } diff --git a/backend/test/Controllers/PropertyControllerTest.cs b/backend/test/Controllers/PropertyControllerTest.cs index 28ce0545fc..1d572377ce 100644 --- a/backend/test/Controllers/PropertyControllerTest.cs +++ b/backend/test/Controllers/PropertyControllerTest.cs @@ -1,7 +1,7 @@ using AutoMapper; using Entity = Pims.Dal.Entities; using Microsoft.AspNetCore.Mvc; -using Model = Pims.Api.Models; +using Model = Pims.Api.Models.Property; using Moq; using Pims.Api.Controllers; using Pims.Api.Helpers.Exceptions; @@ -15,6 +15,9 @@ namespace Pims.Api.Test.Controllers { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("group", "property")] public class PropertyControllerTest { #region Variables @@ -46,19 +49,19 @@ public void GetProperties_FilterLatitude_Success() var service = helper.GetService>(); var mapper = helper.GetService(); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); - service.Setup(m => m.Building.GetNoTracking(It.IsAny())).Returns(buildings); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); + service.Setup(m => m.Building.Get(It.IsAny())).Returns(buildings); // Act var result = controller.GetProperties(filter); // Assert var actionResult = Assert.IsType(result); - var actualProperties = Assert.IsType(actionResult.Value); - var expectedProperties = mapper.Map(parcels).Concat(mapper.Map(buildings)); + var actualProperties = Assert.IsType(actionResult.Value); + var expectedProperties = mapper.Map(parcels).Concat(mapper.Map(buildings)); Assert.Equal(expectedProperties, actualProperties); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Once()); - service.Verify(m => m.Building.GetNoTracking(It.IsAny()), Times.Once()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Once()); + service.Verify(m => m.Building.Get(It.IsAny()), Times.Once()); } /// @@ -79,19 +82,19 @@ public void GetProperties_FilterLongitude_Success() var service = helper.GetService>(); var mapper = helper.GetService(); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); - service.Setup(m => m.Building.GetNoTracking(It.IsAny())).Returns(buildings); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); + service.Setup(m => m.Building.Get(It.IsAny())).Returns(buildings); // Act var result = controller.GetProperties(filter); // Assert var actionResult = Assert.IsType(result); - var actualProperties = Assert.IsType(actionResult.Value); - var expectedProperties = mapper.Map(parcels).Concat(mapper.Map(buildings)); + var actualProperties = Assert.IsType(actionResult.Value); + var expectedProperties = mapper.Map(parcels).Concat(mapper.Map(buildings)); Assert.Equal(expectedProperties, actualProperties); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Once()); - service.Verify(m => m.Building.GetNoTracking(It.IsAny()), Times.Once()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Once()); + service.Verify(m => m.Building.Get(It.IsAny()), Times.Once()); } /// @@ -111,18 +114,18 @@ public void GetProperties_OnlyParcels_Success() var service = helper.GetService>(); var mapper = helper.GetService(); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); // Act var result = controller.GetProperties(filter); // Assert var actionResult = Assert.IsType(result); - var actualProperties = Assert.IsType(actionResult.Value); - var expectedProperties = mapper.Map(parcels); + var actualProperties = Assert.IsType(actionResult.Value); + var expectedProperties = mapper.Map(parcels); Assert.Equal(expectedProperties, actualProperties); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Once()); - service.Verify(m => m.Building.GetNoTracking(It.IsAny()), Times.Never()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Once()); + service.Verify(m => m.Building.Get(It.IsAny()), Times.Never()); } /// @@ -142,18 +145,18 @@ public void GetProperties_OnlyBuildings_Success() var service = helper.GetService>(); var mapper = helper.GetService(); - service.Setup(m => m.Building.GetNoTracking(It.IsAny())).Returns(buildings); + service.Setup(m => m.Building.Get(It.IsAny())).Returns(buildings); // Act var result = controller.GetProperties(filter); // Assert var actionResult = Assert.IsType(result); - var actualProperties = Assert.IsType(actionResult.Value); - var expectedProperties = mapper.Map(buildings); + var actualProperties = Assert.IsType(actionResult.Value); + var expectedProperties = mapper.Map(buildings); Assert.Equal(expectedProperties, actualProperties); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Never()); - service.Verify(m => m.Building.GetNoTracking(It.IsAny()), Times.Once()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Never()); + service.Verify(m => m.Building.Get(It.IsAny()), Times.Once()); } /// @@ -172,19 +175,19 @@ public void GetProperties_Query_Success() var service = helper.GetService>(); var mapper = helper.GetService(); - service.Setup(m => m.Parcel.GetNoTracking(It.IsAny())).Returns(parcels); - service.Setup(m => m.Building.GetNoTracking(It.IsAny())).Returns(new Entity.Building[0]); + service.Setup(m => m.Parcel.Get(It.IsAny())).Returns(parcels); + service.Setup(m => m.Building.Get(It.IsAny())).Returns(new Entity.Building[0]); // Act var result = controller.GetProperties(); // Assert var actionResult = Assert.IsType(result); - var actualProperties = Assert.IsType(actionResult.Value); - var expectedProperties = mapper.Map(parcels); + var actualProperties = Assert.IsType(actionResult.Value); + var expectedProperties = mapper.Map(parcels); Assert.Equal(expectedProperties, actualProperties); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Once()); - service.Verify(m => m.Building.GetNoTracking(It.IsAny()), Times.Once()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Once()); + service.Verify(m => m.Building.Get(It.IsAny()), Times.Once()); } /// @@ -202,8 +205,8 @@ public void GetProperties_Query_NoFilter_BadRequest() // Act // Assert Assert.Throws(() => controller.GetProperties()); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Never()); - service.Verify(m => m.Building.GetNoTracking(It.IsAny()), Times.Never()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Never()); + service.Verify(m => m.Building.Get(It.IsAny()), Times.Never()); } /// @@ -221,8 +224,8 @@ public void GetProperties_NoFilter_BadRequest() // Act // Assert Assert.Throws(() => controller.GetProperties(null)); - service.Verify(m => m.Parcel.GetNoTracking(It.IsAny()), Times.Never()); - service.Verify(m => m.Building.GetNoTracking(It.IsAny()), Times.Never()); + service.Verify(m => m.Parcel.Get(It.IsAny()), Times.Never()); + service.Verify(m => m.Building.Get(It.IsAny()), Times.Never()); } #endregion #endregion diff --git a/backend/test/Controllers/ImportControllerTest.cs b/backend/test/Controllers/Tools/ImportControllerTest.cs similarity index 91% rename from backend/test/Controllers/ImportControllerTest.cs rename to backend/test/Controllers/Tools/ImportControllerTest.cs index 0732807673..3e1530189b 100644 --- a/backend/test/Controllers/ImportControllerTest.cs +++ b/backend/test/Controllers/Tools/ImportControllerTest.cs @@ -5,12 +5,16 @@ using Pims.Api.Test.Helpers; using Pims.Api.Areas.Admin.Controllers; using Moq; -using Model = Pims.Api.Areas.Tools.Models; +using Model = Pims.Api.Areas.Tools.Models.Import; using Microsoft.AspNetCore.Mvc; using Entity = Pims.Dal.Entities; -namespace Pims.Api.Test.Controllers +namespace Pims.Api.Test.Controllers.Tools { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("area", "tools")] + [Trait("group", "import")] public class ImportControllerTest { #region Variables @@ -69,7 +73,7 @@ public void ImportProperties_UpdateParcel_Success() // Assert JsonResult actionResult = Assert.IsType(result); - var data = Assert.IsAssignableFrom>(actionResult.Value); + var data = Assert.IsAssignableFrom>(actionResult.Value); Assert.Equal(properties.First().ParcelId, data.First().PID); service.Verify(m => m.BuildingConstructionType.GetAll(), Times.Once()); service.Verify(m => m.BuildingPredominateUse.GetAll(), Times.Once()); diff --git a/backend/test/Controllers/UserControllerTest.cs b/backend/test/Controllers/UserControllerTest.cs index 89be00e5ba..f4e3196e7d 100644 --- a/backend/test/Controllers/UserControllerTest.cs +++ b/backend/test/Controllers/UserControllerTest.cs @@ -4,13 +4,16 @@ using Pims.Api.Test.Helpers; using Pims.Api.Controllers; using Moq; -using Model = Pims.Api.Models; +using Model = Pims.Api.Models.User; using Microsoft.AspNetCore.Mvc; using Entity = Pims.Dal.Entities; using AutoMapper; namespace PimsApi.Test.Controllers { + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("group", "user")] public class UserControllerTest { #region Variables @@ -80,7 +83,6 @@ public void AddAccessRequest_Success() service.Verify(m => m.AddAccessRequest(It.IsAny()), Times.Once()); } #endregion - #endregion } } diff --git a/backend/test/Helpers/EntityHelper.cs b/backend/test/Helpers/EntityHelper.cs index 35a2078cf4..58d3edd5f9 100644 --- a/backend/test/Helpers/EntityHelper.cs +++ b/backend/test/Helpers/EntityHelper.cs @@ -17,7 +17,7 @@ public static class EntityHelper /// /// /// - public static Entity.Parcel CreateParcel(int pid, double lat, double lng, int agencyId) + public static Entity.Parcel CreateParcel(int pid, double lat = 0, double lng = 0, int agencyId = 1) { var agency = new Entity.Agency("AGENCY", "Test Agency") { @@ -25,12 +25,18 @@ public static Entity.Parcel CreateParcel(int pid, double lat, double lng, int ag RowVersion = new byte[] { 12, 13, 14 } }; + var city = new Entity.City("VIC", "Victoria") { Id = 1 }; + var province = new Entity.Province("BC", "British Columbia"); + var address = new Entity.Address("1234 Street", null, city.Id, province.Id, "V9C0E4") { Id = pid, City = city, Province = province }; + return new Entity.Parcel(lat, lng) { Id = pid, PID = pid, AgencyId = agency.Id, Agency = agency, + AddressId = address.Id, + Address = address, CreatedById = Guid.NewGuid(), CreatedOn = DateTime.UtcNow, UpdatedById = Guid.NewGuid(), @@ -54,5 +60,45 @@ public static Entity.Parcel CreateParcel(int pid, double lat, double lng, int ag } return parcels; } + + /// + /// Creates a new instance of a Building. + /// + /// + /// + /// + /// + /// + /// + /// + public static Entity.Building CreateBuilding(Entity.Parcel parcel, int id, string localId, int lat = 0, int lng = 0, int agencyId = 1) + { + var agency = new Entity.Agency("AGENCY", "Test Agency") + { + Id = agencyId, + RowVersion = new byte[] { 12, 13, 14 } + }; + + var city = new Entity.City("VIC", "Victoria") { Id = 1 }; + var province = new Entity.Province("BC", "British Columbia"); + var address = new Entity.Address("1234 Street", null, city.Id, province.Id, "V9C0E4") { Id = id, City = city, Province = province }; + + return new Entity.Building(lat, lng) + { + Id = id, + Parcel = parcel, + ParcelId = parcel.Id, + LocalId = localId, + AgencyId = agency.Id, + Agency = agency, + AddressId = address.Id, + Address = address, + CreatedById = Guid.NewGuid(), + CreatedOn = DateTime.UtcNow, + UpdatedById = Guid.NewGuid(), + UpdatedOn = DateTime.UtcNow, + RowVersion = new byte[] { 12, 13, 14 } + }; + } } } diff --git a/backend/test/Routes/Admin/ParcelControllerTest.cs b/backend/test/Routes/Admin/ParcelControllerTest.cs index 8d503a903a..c8c8ce617d 100644 --- a/backend/test/Routes/Admin/ParcelControllerTest.cs +++ b/backend/test/Routes/Admin/ParcelControllerTest.cs @@ -1,4 +1,4 @@ -using Model = Pims.Api.Models; +using Model = Pims.Api.Areas.Admin.Models.Parcel; using Pims.Api.Areas.Admin.Controllers; using Pims.Api.Test.Helpers; using Pims.Core.Extensions; @@ -11,6 +11,11 @@ namespace Pims.Api.Test.Routes.Admin /// /// ParcelControllerTest class, provides a way to test endpoint routes. /// + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("area", "admin")] + [Trait("group", "parcel")] + [Trait("group", "route")] public class ParcelControllerTest { #region Variables @@ -37,12 +42,42 @@ public void Parcel_Route() type.HasApiVersion("1.0"); } + #region AddParcel + [Fact] + public void AddParcel_Route() + { + // Arrange + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.AddParcel), typeof(Model.ParcelModel)); + + // Act + // Assert + Assert.NotNull(endpoint); + endpoint.HasPost(); + endpoint.HasPermissions(Permissions.PropertyAdd); + } + #endregion + + #region UpdateParcel + [Fact] + public void UpdateParcel_Route() + { + // Arrange + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.UpdateParcel), typeof(int), typeof(Model.ParcelModel)); + + // Act + // Assert + Assert.NotNull(endpoint); + endpoint.HasPut("{id}"); + endpoint.HasPermissions(Permissions.PropertyEdit); + } + #endregion + #region DeleteParcel [Fact] public void DeleteParcel_Route() { // Arrange - var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.DeleteParcel), typeof(Guid), typeof(Model.ParcelModel)); + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.DeleteParcel), typeof(int), typeof(Model.ParcelModel)); // Act // Assert diff --git a/backend/test/Routes/ParcelControllerTest.cs b/backend/test/Routes/ParcelControllerTest.cs index 8e1948039e..d336d20c7b 100644 --- a/backend/test/Routes/ParcelControllerTest.cs +++ b/backend/test/Routes/ParcelControllerTest.cs @@ -1,9 +1,8 @@ -using Model = Pims.Api.Models; +using Model = Pims.Api.Models.Parcel; using Pims.Api.Controllers; using Pims.Api.Test.Helpers; using Pims.Core.Extensions; using Pims.Dal.Security; -using System; using Xunit; namespace Pims.Api.Test.Routes @@ -11,6 +10,10 @@ namespace Pims.Api.Test.Routes /// /// ParcelControllerTest class, provides a way to test endpoint routes. /// + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("group", "parcel")] + [Trait("group", "route")] public class ParcelControllerTest { #region Variables @@ -36,12 +39,76 @@ public void Parcel_Route() type.HasApiVersion("1.0"); } - #region DeleteParcel + [Fact] + public void GetParcels_Query_Route() + { + // Arrange + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.GetParcels)); + + // Act + // Assert + Assert.NotNull(endpoint); + endpoint.HasGet(); + endpoint.HasPermissions(Permissions.PropertyView); + } + + [Fact] + public void GetParcels_Route() + { + // Arrange + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.GetParcels), typeof(Dal.Entities.Models.ParcelFilter)); + + // Act + // Assert + Assert.NotNull(endpoint); + endpoint.HasPost("filter"); + endpoint.HasPermissions(Permissions.PropertyView); + } + + [Fact] + public void GetParcel_Route() + { + // Arrange + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.GetParcel), typeof(int)); + + // Act + // Assert + Assert.NotNull(endpoint); + endpoint.HasGet("{id}"); + endpoint.HasPermissions(Permissions.PropertyView); + } + + [Fact] + public void AddParcel_Route() + { + // Arrange + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.AddParcel), typeof(Model.ParcelModel)); + + // Act + // Assert + Assert.NotNull(endpoint); + endpoint.HasPost(); + endpoint.HasPermissions(Permissions.PropertyAdd); + } + + [Fact] + public void UpdateParcel_Route() + { + // Arrange + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.UpdateParcel), typeof(int), typeof(Model.ParcelModel)); + + // Act + // Assert + Assert.NotNull(endpoint); + endpoint.HasPut("{id}"); + endpoint.HasPermissions(Permissions.PropertyEdit); + } + [Fact] public void DeleteParcel_Route() { // Arrange - var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.DeleteParcel), typeof(Guid), typeof(Model.ParcelModel)); + var endpoint = typeof(ParcelController).FindMethod(nameof(ParcelController.DeleteParcel), typeof(int), typeof(Model.ParcelModel)); // Act // Assert @@ -50,6 +117,5 @@ public void DeleteParcel_Route() endpoint.HasPermissions(Permissions.PropertyAdd); } #endregion - #endregion } } diff --git a/backend/test/Routes/PropertyControllerTest.cs b/backend/test/Routes/PropertyControllerTest.cs index d897866906..e6f842055f 100644 --- a/backend/test/Routes/PropertyControllerTest.cs +++ b/backend/test/Routes/PropertyControllerTest.cs @@ -10,6 +10,10 @@ namespace Pims.Api.Test.Routes /// /// PropertyControllerTest class, provides a way to test endpoint routes. /// + [Trait("category", "unit")] + [Trait("category", "api")] + [Trait("group", "property")] + [Trait("group", "route")] public class PropertyControllerTest { #region Variables