From 7b8974830e8a9a02a3bdec4f20452218e4442573 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Mon, 22 Mar 2021 09:22:08 +0100 Subject: [PATCH 01/21] Remove backend cache --- .../Application/ContractorController.cs | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/FunderMaps.WebApi/Controllers/Application/ContractorController.cs b/src/FunderMaps.WebApi/Controllers/Application/ContractorController.cs index 4dcd9e29c..ee3b1119a 100644 --- a/src/FunderMaps.WebApi/Controllers/Application/ContractorController.cs +++ b/src/FunderMaps.WebApi/Controllers/Application/ContractorController.cs @@ -4,7 +4,6 @@ using FunderMaps.Core.Interfaces.Repositories; using FunderMaps.WebApi.DataTransferObjects; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -18,16 +17,14 @@ namespace FunderMaps.WebApi.Controllers.Application public class ContractorController : ControllerBase { private readonly IMapper _mapper; - private readonly IMemoryCache _cache; private readonly IOrganizationRepository _organizationRepository; /// /// Create new instance. /// - public ContractorController(IMapper mapper, IMemoryCache cache, IOrganizationRepository organizationRepository) + public ContractorController(IMapper mapper, IOrganizationRepository organizationRepository) { _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - _cache = cache ?? throw new ArgumentNullException(nameof(cache)); _organizationRepository = organizationRepository ?? throw new ArgumentNullException(nameof(organizationRepository)); } @@ -42,20 +39,11 @@ public ContractorController(IMapper mapper, IMemoryCache cache, IOrganizationRep [HttpGet("contractor"), ResponseCache(Duration = 60 * 60 * 8)] public async Task GetAllAsync([FromQuery] PaginationDto pagination) { - // README: XXX: Response caching is a test + // Assign. + IAsyncEnumerable organizationList = _organizationRepository.ListAllAsync(pagination.Navigation); - // Fetch. - if (!_cache.TryGetValue(nameof(ContractorDto), out IList result)) - { - // Assign. - IAsyncEnumerable organizationList = _organizationRepository.ListAllAsync(pagination.Navigation); - - // Map. - result = await _mapper.MapAsync, Organization>(organizationList); - - // Set. - _cache.Set(nameof(ContractorDto), result, TimeSpan.FromHours(1)); - } + // Map. + var result = await _mapper.MapAsync, Organization>(organizationList); // Return. return Ok(result); From 0fe7b3837a52fef9b06ca33a90b505eee369a626 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Mon, 22 Mar 2021 11:52:34 +0100 Subject: [PATCH 02/21] Change color coding --- database/data/seed_maplayer.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/data/seed_maplayer.sql b/database/data/seed_maplayer.sql index 229da894f..a6f54b925 100644 --- a/database/data/seed_maplayer.sql +++ b/database/data/seed_maplayer.sql @@ -37,7 +37,6 @@ COPY maplayer.layer (id, schema_name, table_name, name, markup) FROM stdin; c7ccf095-cf77-4f41-813a-68b493f7c6cb maplayer building_built_year Bouwjaar {"type": "range_num", "column": "built_year", "values": [{"max": "1960", "min": "0", "color": "#293575", "label": "< 1960"}, {"max": "1970", "min": "1960", "color": "#1261A3", "label": "1960 t/m 1970"}, {"max": "1980", "min": "1970", "color": "#69A8DE", "label": "1970 t/m 1980"}, {"max": "1990", "min": "1980", "color": "#99C1E9", "label": "1980 t/m 1990"}, {"max": "2000", "min": "1990", "color": "#B378B1", "label": "1990 t/m 2000"}, {"max": "2010", "min": "2000", "color": "#bd6495", "label": "2000 t/m 2010"}, {"max": "2020", "min": "2010", "color": "#ba2351", "label": "2010 t/m 2020"}, {"max": "9999", "min": "2020", "color": "#d11313", "label": "> 2020"}]} 5742df10-53fe-44da-b449-2c0c926deab6 maplayer incident Meldingen {"type": "color", "column": "meldingen123kleurtest", "values": {"color": "#bd6495"}} 307e6489-feb3-4be8-919c-4b5392fee8fb maplayer inquiry_sample_enforcement_term Handhavingstermijn (jaar) {"type": "range_num", "column": "enforcement_term", "values": [{"max": "100", "min": "25", "color": "#64DEBC", "label": "> 25"}, {"max": "25", "min": "20", "color": "#55E293", "label": "20 t/m 25"}, {"max": "20", "min": "15", "color": "#46E65F", "label": "15 t/m 20"}, {"max": "15", "min": "10", "color": "#4CEB36", "label": "10 t/m 15"}, {"max": "10", "min": "5", "color": "#77F025", "label": "5 t/m 10"}, {"max": "5", "min": "0", "color": "#AEF614", "label": "0 t/m 5"}, {"max": "0", "min": "-5", "color": "#D0E218", "label": " 0 t/m -5"}, {"max": "-5", "min": "-10", "color": "#CEB31B", "label": "-5 t/m -10"}, {"max": "-10", "min": "-15", "color": "#BB7F1E", "label": "-10 t/m -15"}, {"max": "-15", "min": "-20", "color": "#A85520", "label": "-15 t/m -20"}, {"max": "-20", "min": "-25", "color": "#973321", "label": "-20 t/m -25"}, {"max": "-25", "min": "-100", "color": "#86222A", "label": "< -25"}]} -f42a6826-c3a0-48b1-8c96-6c9ef753ed46 maplayer inquiry_sample_foundation_type Funderingstype vastgesteld {"type": "case_multimatch", "column": "foundation_type", "values": [{"color": "#c75d43", "label": "Houten paal", "match": ["wood", "weighted_pile", "wood_amsterdam", "wood_rotterdam"]}, {"color": "#deb271", "label": "Houten paal met oplanger", "match": ["wood_charger"]}, {"color": "#6a6c70", "label": "Betonnen paal", "match": ["concrete"]}, {"color": "#ff5533", "label": "Op staal", "match": ["no_pile", "no_pile_masonry", "no_pile_strips", "no_pile_concrete_floor", "no_pile_slit", "no_pile_bearing_floor"]}, {"color": "#bdbebf", "label": "Stalen paal", "match": ["steel_pile"]}, {"color": "#7192de", "label": "Verzwaarde betonpuntpaal", "match": ["weighted_pile"]}, {"color": "#b271de", "label": "Combinatie", "match": ["combined"]}, {"color": "#ffec33", "label": "Overig", "match": ["other"]}, {"color": "#71decc", "label": "Onbekend", "match": ["unknown"]}]} 75709b4f-d82e-4d62-9be3-07cb2ca00cec maplayer inquiry_sample_damage_cause Oorzaak Schade {"type": "case_multimatch", "column": "damage_cause", "values": [{"color": "#55B5A7", "label": "Ontwateringsdiepte", "match": ["drainage"]}, {"color": "#4B8FBF", "label": "Overbelasting", "match": ["overcharge"]}, {"color": "#4145C9", "label": "Bacteriële aantasting", "match": ["bio_fungus_infection", "bio_infection"]}, {"color": "#8936D4", "label": "Schimmelaantasting", "match": ["fungus_infection", "drystand", "bio_fungus_infection"]}, {"color": "#DE2CCF", "label": "Bodemdaling", "match": ["subsidence"]}, {"color": "#D2386F", "label": "Planten en wortels", "match": ["vegetation"]}, {"color": "#C75D43", "label": "Aardbeving", "match": ["gas", "vibrations"]}, {"color": "#BBA14F", "label": "Partieel funderingsherstel", "match": ["partial_foundation_recovery"]}, {"color": "#95B05A", "label": "Constructiefout", "match": ["construction_flaw", "foundation_flaw", "construction_heave"]}, {"color": "#6EA466", "label": "Negatieve kleef", "match": ["negative_cling", "overcharge_negative_cling"]}, {"color": "#6A6C70", "label": "Onbekend", "match": ["unknown"]}]} 782bc8e1-ff0f-48aa-9f0d-10232392ceda maplayer building_height Gebouwhoogte (Beta) \N 8beee1ea-9cd5-4999-8759-02e4cf313bd9 maplayer building_hotspot Hotspots {"type": "color", "column": "meldingen123kleurtest", "values": {"color": "#d11313"}} @@ -46,6 +45,7 @@ edd9bf0f-903b-43e6-be9c-2c927089b075 maplayer incident_aggregate Incidenten Geme e0519b1a-ae1a-43ad-a2b9-bdbbeb0c5f86 maplayer incident_aggregate_category Incidenten Gemeente Category {"type": "case", "column": "category", "values": [{"color": "#d8e7f5", "label": "Geen of nauwelijks", "match": "0"}, {"color": "#73b3d8", "label": "Enkele", "match": "1"}, {"color": "#1563aa", "label": "Meerdere", "match": "2"}]} 9dffd130-4019-4440-b3d5-8812a961a87a maplayer recovery_sample_type Hesteld {"type": "case_multimatch", "column": "type", "values": [{"color": "#5cbe55", "label": "Volledig herstel", "match": ["table"]}, {"color": "#47baa5", "label": "Partieel herstel", "match": ["pile_in_wall"]}, {"color": "#8c4bb6", "label": "Paalkop verlaging", "match": ["pile_lowering", "beam_on_pile"]}, {"color": "#c67e70", "label": "Grondverbetering", "match": ["injection"]}, {"color": "#6a6c70", "label": "Onbekend", "match": ["unknown"]}]} dfe1a361-c23f-4a1d-ba49-af8e974270b3 maplayer subsidence_hex Maaiveldzakking {"type": "range_num", "column": "velocity", "values": [{"max": "9999", "min": "0", "color": "#f7fbff", "label": "> 0 mm/jaar"}, {"max": "0", "min": "-0.5", "color": "#d8e7f5", "label": "0,0 t/m -0,5 mm/jaar"}, {"max": "-0.5", "min": "-1", "color": "#b0d2e8", "label": "-0,5 t/m -1,0 mm/jaar"}, {"max": "-1", "min": "-1.5", "color": "#73b3d8", "label": "-1,0 t/m -1,5 mm/jaar"}, {"max": "-1.5", "min": "-2", "color": "#3e8ec4", "label": "-1,5 t/m -2,0 mm/jaar"}, {"max": "-2", "min": "-2.5", "color": "#1563aa", "label": "-2,0 t/m -2,5 mm/jaar"}, {"max": "-2.5", "min": "-9999", "color": "#08306b", "label": "< -2,5 mm/jaar"}]} +f42a6826-c3a0-48b1-8c96-6c9ef753ed46 maplayer inquiry_sample_foundation_type Funderingstype vastgesteld {"type": "case_multimatch", "column": "foundation_type", "values": [{"color": "#c75d43", "label": "Houten paal", "match": ["wood", "weighted_pile", "wood_amsterdam", "wood_rotterdam"]}, {"color": "#deb271", "label": "Houten paal met oplanger", "match": ["wood_charger"]}, {"color": "#6a6c70", "label": "Betonnen paal", "match": ["concrete"]}, {"color": "#ff3333", "label": "Op staal", "match": ["no_pile", "no_pile_masonry", "no_pile_strips", "no_pile_concrete_floor", "no_pile_slit", "no_pile_bearing_floor"]}, {"color": "#bdbebf", "label": "Stalen paal", "match": ["steel_pile"]}, {"color": "#7192de", "label": "Verzwaarde betonpuntpaal", "match": ["weighted_pile"]}, {"color": "#b271de", "label": "Combinatie", "match": ["combined"]}, {"color": "#ffec33", "label": "Overig", "match": ["other"]}, {"color": "#71decc", "label": "Onbekend", "match": ["unknown"]}]} \. From c7ddff6328e7f59331f33dd5d4b46fc2cefe3cb7 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Mon, 22 Mar 2021 12:53:23 +0100 Subject: [PATCH 03/21] Change color --- database/data/seed_maplayer.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/data/seed_maplayer.sql b/database/data/seed_maplayer.sql index a6f54b925..7d7c0450b 100644 --- a/database/data/seed_maplayer.sql +++ b/database/data/seed_maplayer.sql @@ -33,13 +33,13 @@ COPY maplayer.layer (id, schema_name, table_name, name, markup) FROM stdin; 2d151f64-778d-40b6-ac21-40675b7c5c35 maplayer inquiry Rapportage {"type": "case", "column": "type", "values": [{"color": "#B54CB0", "label": "Monitoring", "match": "monitoring"}, {"color": "#8C4BB6", "label": "Notitie", "match": "note"}, {"color": "#5B4AB7", "label": "Snelle scan", "match": "quickscan"}, {"color": "#4969B8", "label": "Sloop onderzoek", "match": "demolition_research"}, {"color": "#489BB9", "label": "Second opinion", "match": "second_opinion"}, {"color": "#47BAA5", "label": "Archief onderzoek", "match": "archieve_research"}, {"color": "#4EBC77", "label": "Architectureel onderzoek", "match": "architectural_research"}, {"color": "#5CBE55", "label": "Funderingsadvies", "match": "foundation_advice"}, {"color": "#8FC05C", "label": "Inspectieput", "match": "inspectionpit"}, {"color": "#BDC262", "label": "Funderingsonderzoek", "match": "foundation_research"}, {"color": "#C4A169", "label": "Extra onderzoek", "match": "additional_research"}, {"color": "#C67E70", "label": "Grondwaterniveau onderzoek", "match": "ground_water_level_research"}, {"color": "#6A6C70", "label": "Onbekend", "match": "unknown"}]} 0f7cc50c-b831-486e-be07-c22c85943f21 maplayer inquiry_sample_quality Kwaliteit Funderingen {"type": "case", "column": "overall_quality", "values": [{"color": "#67B6E4", "label": "Goed", "match": "good"}, {"color": "#4EEBA9", "label": "Redelijk", "match": "tolerable"}, {"color": "#5CF434", "label": "Acceptabel", "match": "mediocre_good"}, {"color": "#FFFF17", "label": "Twijfelachtig", "match": "mediocre"}, {"color": "#DD882F", "label": "Slecht", "match": "mediocre_bad"}, {"color": "#BE4745", "label": "Zeer slecht", "match": "bad"}]} 73258fb5-54a4-4d0c-a85f-e0ca2313e67f maplayer foundation_risk Funderingsrisico {"type": "case", "column": "foundation_risk", "values": [{"color": "#42FF33", "label": "A", "match": "a"}, {"color": "#D1FF33", "label": "B", "match": "b"}, {"color": "#FFEC33", "label": "C", "match": "c"}, {"color": "#FFAC33", "label": "D", "match": "d"}, {"color": "#FF5533", "label": "E", "match": "e"}]} -16cc1db0-5f09-4673-9e07-e5ac573fc1b7 maplayer foundation_indicative Funderingstype Indicatief {"type": "case_multimatch", "column": "foundation_type", "values": [{"color": "#c75d43", "label": "Houten paal", "match": ["wood", "wood_rotterdam", "wood_amsterdam"]}, {"color": "#deb271", "label": "Houten paal met oplanger", "match": ["wood_charger"]}, {"color": "#6a6c70", "label": "Betonnen paal", "match": ["concrete"]}, {"color": "#ff5533", "label": "Op staal", "match": ["no_pile", "no_pile_masonry", "no_pile_strips", "no_pile_concrete_floor", "no_pile_slit", "no_pile_bearing_floor"]}]} c7ccf095-cf77-4f41-813a-68b493f7c6cb maplayer building_built_year Bouwjaar {"type": "range_num", "column": "built_year", "values": [{"max": "1960", "min": "0", "color": "#293575", "label": "< 1960"}, {"max": "1970", "min": "1960", "color": "#1261A3", "label": "1960 t/m 1970"}, {"max": "1980", "min": "1970", "color": "#69A8DE", "label": "1970 t/m 1980"}, {"max": "1990", "min": "1980", "color": "#99C1E9", "label": "1980 t/m 1990"}, {"max": "2000", "min": "1990", "color": "#B378B1", "label": "1990 t/m 2000"}, {"max": "2010", "min": "2000", "color": "#bd6495", "label": "2000 t/m 2010"}, {"max": "2020", "min": "2010", "color": "#ba2351", "label": "2010 t/m 2020"}, {"max": "9999", "min": "2020", "color": "#d11313", "label": "> 2020"}]} 5742df10-53fe-44da-b449-2c0c926deab6 maplayer incident Meldingen {"type": "color", "column": "meldingen123kleurtest", "values": {"color": "#bd6495"}} 307e6489-feb3-4be8-919c-4b5392fee8fb maplayer inquiry_sample_enforcement_term Handhavingstermijn (jaar) {"type": "range_num", "column": "enforcement_term", "values": [{"max": "100", "min": "25", "color": "#64DEBC", "label": "> 25"}, {"max": "25", "min": "20", "color": "#55E293", "label": "20 t/m 25"}, {"max": "20", "min": "15", "color": "#46E65F", "label": "15 t/m 20"}, {"max": "15", "min": "10", "color": "#4CEB36", "label": "10 t/m 15"}, {"max": "10", "min": "5", "color": "#77F025", "label": "5 t/m 10"}, {"max": "5", "min": "0", "color": "#AEF614", "label": "0 t/m 5"}, {"max": "0", "min": "-5", "color": "#D0E218", "label": " 0 t/m -5"}, {"max": "-5", "min": "-10", "color": "#CEB31B", "label": "-5 t/m -10"}, {"max": "-10", "min": "-15", "color": "#BB7F1E", "label": "-10 t/m -15"}, {"max": "-15", "min": "-20", "color": "#A85520", "label": "-15 t/m -20"}, {"max": "-20", "min": "-25", "color": "#973321", "label": "-20 t/m -25"}, {"max": "-25", "min": "-100", "color": "#86222A", "label": "< -25"}]} 75709b4f-d82e-4d62-9be3-07cb2ca00cec maplayer inquiry_sample_damage_cause Oorzaak Schade {"type": "case_multimatch", "column": "damage_cause", "values": [{"color": "#55B5A7", "label": "Ontwateringsdiepte", "match": ["drainage"]}, {"color": "#4B8FBF", "label": "Overbelasting", "match": ["overcharge"]}, {"color": "#4145C9", "label": "Bacteriële aantasting", "match": ["bio_fungus_infection", "bio_infection"]}, {"color": "#8936D4", "label": "Schimmelaantasting", "match": ["fungus_infection", "drystand", "bio_fungus_infection"]}, {"color": "#DE2CCF", "label": "Bodemdaling", "match": ["subsidence"]}, {"color": "#D2386F", "label": "Planten en wortels", "match": ["vegetation"]}, {"color": "#C75D43", "label": "Aardbeving", "match": ["gas", "vibrations"]}, {"color": "#BBA14F", "label": "Partieel funderingsherstel", "match": ["partial_foundation_recovery"]}, {"color": "#95B05A", "label": "Constructiefout", "match": ["construction_flaw", "foundation_flaw", "construction_heave"]}, {"color": "#6EA466", "label": "Negatieve kleef", "match": ["negative_cling", "overcharge_negative_cling"]}, {"color": "#6A6C70", "label": "Onbekend", "match": ["unknown"]}]} 782bc8e1-ff0f-48aa-9f0d-10232392ceda maplayer building_height Gebouwhoogte (Beta) \N 8beee1ea-9cd5-4999-8759-02e4cf313bd9 maplayer building_hotspot Hotspots {"type": "color", "column": "meldingen123kleurtest", "values": {"color": "#d11313"}} +16cc1db0-5f09-4673-9e07-e5ac573fc1b7 maplayer foundation_indicative Funderingstype Indicatief {"type": "case_multimatch", "column": "foundation_type", "values": [{"color": "#c75d43", "label": "Houten paal", "match": ["wood", "wood_rotterdam", "wood_amsterdam"]}, {"color": "#deb271", "label": "Houten paal met oplanger", "match": ["wood_charger"]}, {"color": "#6a6c70", "label": "Betonnen paal", "match": ["concrete"]}, {"color": "#ff3333", "label": "Op staal", "match": ["no_pile", "no_pile_masonry", "no_pile_strips", "no_pile_concrete_floor", "no_pile_slit", "no_pile_bearing_floor"]}]} 806d560e-2931-46d9-a06b-644491a335e8 maplayer building_ownership Eigendom {"type": "color", "column": "meldingen123kleurtest", "values": {"color": "#d11313"}} edd9bf0f-903b-43e6-be9c-2c927089b075 maplayer incident_aggregate Incidenten Gemeente Aantallen {"type": "range_num", "column": "incidents", "values": [{"max": "0", "min": "0", "color": "#293575", "label": "0"}, {"max": "1", "min": "0", "color": "#293575", "label": "1"}, {"max": "2", "min": "1", "color": "#1261A3", "label": "2"}, {"max": "3", "min": "2", "color": "#69A8DE", "label": "3"}, {"max": "5", "min": "3", "color": "#99C1E9", "label": "4-5"}, {"max": "10", "min": "5", "color": "#B378B1", "label": "6-10"}, {"max": "15", "min": "10", "color": "#bd6495", "label": "16-20"}, {"max": "25", "min": "20", "color": "#ba2351", "label": "21-25"}, {"max": "100", "min": "25", "color": "#d11313", "label": "> 25"}]} e0519b1a-ae1a-43ad-a2b9-bdbbeb0c5f86 maplayer incident_aggregate_category Incidenten Gemeente Category {"type": "case", "column": "category", "values": [{"color": "#d8e7f5", "label": "Geen of nauwelijks", "match": "0"}, {"color": "#73b3d8", "label": "Enkele", "match": "1"}, {"color": "#1563aa", "label": "Meerdere", "match": "2"}]} From 005b6237e054059ad6c863de4f503663e4b57ee2 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Mon, 22 Mar 2021 13:23:44 +0100 Subject: [PATCH 04/21] Move faker to integration tests --- FunderMaps.sln | 7 --- .../Backend/Application/ApplicationStub.cs | 1 + .../Backend/Application/AuthTests.cs | 2 +- .../Application/OrganizationAdminTests.cs | 2 +- .../Application/OrganizationProposalTests.cs | 2 +- .../Backend/Application/OrganizationTests.cs | 2 +- .../Application/OrganizationUserAdminTests.cs | 1 + .../Application/OrganizationUserTests.cs | 1 + .../Backend/Application/UserTests.cs | 2 +- .../Backend/Report/IncidentTests.cs | 2 +- .../Backend/Report/InquirySampleTests.cs | 2 +- .../Backend/Report/InquiryTests.cs | 2 +- .../Backend/Report/RecoverySampleTests.cs | 2 +- .../Backend/Report/RecoveryTests.cs | 2 +- .../Backend/Report/ReportStub.cs | 2 +- .../Extensions/BogusDataSetsExtensions.cs | 6 +-- .../Faker/ChangeOrganizationRoleDtoFaker.cs | 2 +- .../Faker/ChangePasswordDtoFaker.cs | 4 +- .../Faker/IncidentDtoFaker.cs | 2 +- .../Faker/InquiryDtoFaker.cs | 4 +- .../Faker/InquirySampleDtoFaker.cs | 2 +- .../Faker/OrganizationDtoFaker.cs | 4 +- .../Faker/OrganizationProposalDtoFaker.cs | 2 +- .../Faker/OrganizationSetupDtoFaker.cs | 4 +- .../Faker/OrganizationUserPasswordDtoFaker.cs | 3 +- .../Faker/RecoveryDtoFaker.cs | 4 +- .../Faker/RecoverySampleDtoFaker.cs | 2 +- .../Faker/StatusChangeDtoFaker.cs | 2 +- .../Faker/UserDtoFaker.cs | 2 +- .../Faker/UserFaker.cs | 2 +- .../FileUploadContent.cs | 3 +- .../FunderMaps.IntegrationTests.csproj | 1 - .../Portal/IncidentPortalTests.cs | 4 +- .../Webservice/AuthTests.cs | 2 +- .../FunderMaps.Testing.csproj | 19 ------- tests/FunderMaps.Testing/Helper.cs | 50 ------------------- 36 files changed, 41 insertions(+), 115 deletions(-) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Extensions/BogusDataSetsExtensions.cs (91%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/ChangeOrganizationRoleDtoFaker.cs (92%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/ChangePasswordDtoFaker.cs (87%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/IncidentDtoFaker.cs (97%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/InquiryDtoFaker.cs (95%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/InquirySampleDtoFaker.cs (99%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/OrganizationDtoFaker.cs (94%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/OrganizationProposalDtoFaker.cs (92%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/OrganizationSetupDtoFaker.cs (87%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/OrganizationUserPasswordDtoFaker.cs (95%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/RecoveryDtoFaker.cs (94%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/RecoverySampleDtoFaker.cs (96%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/StatusChangeDtoFaker.cs (90%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/UserDtoFaker.cs (95%) rename tests/{FunderMaps.Testing => FunderMaps.IntegrationTests}/Faker/UserFaker.cs (98%) delete mode 100644 tests/FunderMaps.Testing/FunderMaps.Testing.csproj delete mode 100644 tests/FunderMaps.Testing/Helper.cs diff --git a/FunderMaps.sln b/FunderMaps.sln index 7ae8a2158..4806f1013 100644 --- a/FunderMaps.sln +++ b/FunderMaps.sln @@ -23,8 +23,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunderMaps.Webservice", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunderMaps.AspNetCore", "src\FunderMaps.AspNetCore\FunderMaps.AspNetCore.csproj", "{8A28CFFE-2028-40AC-9CCD-81796FD360AE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunderMaps.Testing", "tests\FunderMaps.Testing\FunderMaps.Testing.csproj", "{2A345D40-2842-4AAA-A552-630FAD97B9D5}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2E7D6F8C-F448-46D2-88BA-2CA8DD7836E9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunderMaps.Portal", "src\FunderMaps.Portal\FunderMaps.Portal.csproj", "{CF0E7A1A-20A8-4A15-8B57-D7218B9F33D1}" @@ -67,10 +65,6 @@ Global {8A28CFFE-2028-40AC-9CCD-81796FD360AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A28CFFE-2028-40AC-9CCD-81796FD360AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A28CFFE-2028-40AC-9CCD-81796FD360AE}.Release|Any CPU.Build.0 = Release|Any CPU - {2A345D40-2842-4AAA-A552-630FAD97B9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A345D40-2842-4AAA-A552-630FAD97B9D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A345D40-2842-4AAA-A552-630FAD97B9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A345D40-2842-4AAA-A552-630FAD97B9D5}.Release|Any CPU.Build.0 = Release|Any CPU {CF0E7A1A-20A8-4A15-8B57-D7218B9F33D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CF0E7A1A-20A8-4A15-8B57-D7218B9F33D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF0E7A1A-20A8-4A15-8B57-D7218B9F33D1}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -95,7 +89,6 @@ Global {E176260E-0D9C-4374-AC49-F312C7EC23FD} = {3CCE5914-CD6D-4DFD-9FF5-E872044553DD} {278702CF-559B-4ED5-8C57-FD0E23DC4014} = {54F0529D-8DC7-4DCC-8A93-BF0B2E5F8BE2} {8A28CFFE-2028-40AC-9CCD-81796FD360AE} = {54F0529D-8DC7-4DCC-8A93-BF0B2E5F8BE2} - {2A345D40-2842-4AAA-A552-630FAD97B9D5} = {0219D434-DD4F-4187-ADED-8CD4A62E90F1} {CF0E7A1A-20A8-4A15-8B57-D7218B9F33D1} = {2E7D6F8C-F448-46D2-88BA-2CA8DD7836E9} {532227C4-BAD4-46AB-B45D-65F15317AFFA} = {2E7D6F8C-F448-46D2-88BA-2CA8DD7836E9} {ACDD237D-7C7E-425B-88CD-EF90A0037505} = {2E7D6F8C-F448-46D2-88BA-2CA8DD7836E9} diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/ApplicationStub.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/ApplicationStub.cs index 7ad6c67e7..ecddaa872 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/ApplicationStub.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/ApplicationStub.cs @@ -2,6 +2,7 @@ using System.Net.Http.Json; using System.Threading.Tasks; using FunderMaps.AspNetCore.DataTransferObjects; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.Testing.Faker; using FunderMaps.WebApi.DataTransferObjects; using Xunit; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/AuthTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/AuthTests.cs index a01de792b..f1f970956 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/AuthTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/AuthTests.cs @@ -1,6 +1,6 @@ using Bogus; +using Bogus.DataSets; using FunderMaps.AspNetCore.DataTransferObjects; -using FunderMaps.Testing.Extensions; using System; using System.Net; using System.Net.Http.Json; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationAdminTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationAdminTests.cs index 165f916a0..4a06ecd06 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationAdminTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationAdminTests.cs @@ -1,5 +1,5 @@ using FunderMaps.AspNetCore.DataTransferObjects; -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using System.Collections.Generic; using System.Net; using System.Net.Http.Json; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationProposalTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationProposalTests.cs index 4e3deb7ed..022422827 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationProposalTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationProposalTests.cs @@ -1,4 +1,4 @@ -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using System.Collections.Generic; using System.Net; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationTests.cs index 87ef109e2..4088e109b 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationTests.cs @@ -1,6 +1,6 @@ using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Types; -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using System; using System.Net; using System.Net.Http.Json; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserAdminTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserAdminTests.cs index 93602849e..1852444f1 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserAdminTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserAdminTests.cs @@ -1,4 +1,5 @@ using FunderMaps.AspNetCore.DataTransferObjects; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.Testing.Faker; using System.Collections.Generic; using System.Net; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserTests.cs index cb38921d0..9247a5804 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/OrganizationUserTests.cs @@ -1,5 +1,6 @@ using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Types; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.Testing.Faker; using System.Collections.Generic; using System.Net; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Application/UserTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Application/UserTests.cs index f582277ac..8d7ca7941 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Application/UserTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Application/UserTests.cs @@ -1,5 +1,5 @@ using FunderMaps.AspNetCore.DataTransferObjects; -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using System; using System.Net; using System.Net.Http.Json; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs index 25e295684..d08251f18 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs @@ -1,11 +1,11 @@ using FunderMaps.Core.Types; -using FunderMaps.Testing.Faker; using FunderMaps.AspNetCore.DataTransferObjects; using System; using System.Net; using System.Net.Http.Json; using System.Threading.Tasks; using Xunit; +using FunderMaps.IntegrationTests.Faker; namespace FunderMaps.IntegrationTests.Backend.Report { diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs index 94388e01a..c44cf7950 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs @@ -1,4 +1,4 @@ -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using System; using System.Collections.Generic; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs index 4060583c2..a1e9ad1a2 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs @@ -1,6 +1,6 @@ using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Types; -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using System; using System.Collections.Generic; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs index d4a22eab5..101e28c11 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs @@ -1,6 +1,6 @@ using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Types; -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using System; using System.Collections.Generic; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs index 8cdb02648..c18a5c096 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs @@ -1,6 +1,6 @@ using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Types; -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using System; using System.Collections.Generic; diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs index 47662d481..7224ecccf 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs @@ -3,7 +3,7 @@ using System.Net.Http.Json; using System.Threading.Tasks; using FunderMaps.Core.Types; -using FunderMaps.Testing.Faker; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using Xunit; diff --git a/tests/FunderMaps.Testing/Extensions/BogusDataSetsExtensions.cs b/tests/FunderMaps.IntegrationTests/Extensions/BogusDataSetsExtensions.cs similarity index 91% rename from tests/FunderMaps.Testing/Extensions/BogusDataSetsExtensions.cs rename to tests/FunderMaps.IntegrationTests/Extensions/BogusDataSetsExtensions.cs index 1195c0bcd..2cfa7b5c2 100644 --- a/tests/FunderMaps.Testing/Extensions/BogusDataSetsExtensions.cs +++ b/tests/FunderMaps.IntegrationTests/Extensions/BogusDataSetsExtensions.cs @@ -1,7 +1,7 @@ -using Bogus; -using Bogus.DataSets; +// using Bogus; +// using Bogus.DataSets; -namespace FunderMaps.Testing.Extensions +namespace Bogus.DataSets { public static class BogusDataSetsExtensions { diff --git a/tests/FunderMaps.Testing/Faker/ChangeOrganizationRoleDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/ChangeOrganizationRoleDtoFaker.cs similarity index 92% rename from tests/FunderMaps.Testing/Faker/ChangeOrganizationRoleDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/ChangeOrganizationRoleDtoFaker.cs index 1b6906c12..c5c263cfb 100644 --- a/tests/FunderMaps.Testing/Faker/ChangeOrganizationRoleDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/ChangeOrganizationRoleDtoFaker.cs @@ -2,7 +2,7 @@ using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Types; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/ChangePasswordDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/ChangePasswordDtoFaker.cs similarity index 87% rename from tests/FunderMaps.Testing/Faker/ChangePasswordDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/ChangePasswordDtoFaker.cs index ee53cf403..ed7df2cac 100644 --- a/tests/FunderMaps.Testing/Faker/ChangePasswordDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/ChangePasswordDtoFaker.cs @@ -1,8 +1,8 @@ using Bogus; +using Bogus.DataSets; using FunderMaps.AspNetCore.DataTransferObjects; -using FunderMaps.Testing.Extensions; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/IncidentDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/IncidentDtoFaker.cs similarity index 97% rename from tests/FunderMaps.Testing/Faker/IncidentDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/IncidentDtoFaker.cs index 790038d37..e32e41e18 100644 --- a/tests/FunderMaps.Testing/Faker/IncidentDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/IncidentDtoFaker.cs @@ -5,7 +5,7 @@ using System; using System.Linq; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/InquiryDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/InquiryDtoFaker.cs similarity index 95% rename from tests/FunderMaps.Testing/Faker/InquiryDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/InquiryDtoFaker.cs index 899c8e1e7..bba778833 100644 --- a/tests/FunderMaps.Testing/Faker/InquiryDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/InquiryDtoFaker.cs @@ -1,11 +1,11 @@ using Bogus; +using Bogus.DataSets; using Bogus.Extensions; using FunderMaps.Core.Types; -using FunderMaps.Testing.Extensions; using FunderMaps.WebApi.DataTransferObjects; using System; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/InquirySampleDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/InquirySampleDtoFaker.cs similarity index 99% rename from tests/FunderMaps.Testing/Faker/InquirySampleDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/InquirySampleDtoFaker.cs index df066746b..0a37f3591 100644 --- a/tests/FunderMaps.Testing/Faker/InquirySampleDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/InquirySampleDtoFaker.cs @@ -3,7 +3,7 @@ using FunderMaps.Core.Types; using FunderMaps.WebApi.DataTransferObjects; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/OrganizationDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/OrganizationDtoFaker.cs similarity index 94% rename from tests/FunderMaps.Testing/Faker/OrganizationDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/OrganizationDtoFaker.cs index f6f931614..ff0f57bcb 100644 --- a/tests/FunderMaps.Testing/Faker/OrganizationDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/OrganizationDtoFaker.cs @@ -1,11 +1,11 @@ using Bogus; +using Bogus.DataSets; using Bogus.Extensions; using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Entities; -using FunderMaps.Testing.Extensions; using System; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/OrganizationProposalDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/OrganizationProposalDtoFaker.cs similarity index 92% rename from tests/FunderMaps.Testing/Faker/OrganizationProposalDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/OrganizationProposalDtoFaker.cs index 3b02779c3..81a768eba 100644 --- a/tests/FunderMaps.Testing/Faker/OrganizationProposalDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/OrganizationProposalDtoFaker.cs @@ -1,7 +1,7 @@ using Bogus; using FunderMaps.WebApi.DataTransferObjects; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/OrganizationSetupDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/OrganizationSetupDtoFaker.cs similarity index 87% rename from tests/FunderMaps.Testing/Faker/OrganizationSetupDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/OrganizationSetupDtoFaker.cs index da1b6dbbe..1a56a8568 100644 --- a/tests/FunderMaps.Testing/Faker/OrganizationSetupDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/OrganizationSetupDtoFaker.cs @@ -1,8 +1,8 @@ using Bogus; -using FunderMaps.Testing.Extensions; +using Bogus.DataSets; using FunderMaps.WebApi.DataTransferObjects; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/OrganizationUserPasswordDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/OrganizationUserPasswordDtoFaker.cs similarity index 95% rename from tests/FunderMaps.Testing/Faker/OrganizationUserPasswordDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/OrganizationUserPasswordDtoFaker.cs index 190cb01a5..0f8fa53a0 100644 --- a/tests/FunderMaps.Testing/Faker/OrganizationUserPasswordDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/OrganizationUserPasswordDtoFaker.cs @@ -1,8 +1,9 @@ using Bogus; +using Bogus.DataSets; using Bogus.Extensions; using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Types; -using FunderMaps.Testing.Extensions; +using FunderMaps.IntegrationTests.Faker; using System.Linq; namespace FunderMaps.Testing.Faker diff --git a/tests/FunderMaps.Testing/Faker/RecoveryDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/RecoveryDtoFaker.cs similarity index 94% rename from tests/FunderMaps.Testing/Faker/RecoveryDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/RecoveryDtoFaker.cs index 43fee7f07..fefa21576 100644 --- a/tests/FunderMaps.Testing/Faker/RecoveryDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/RecoveryDtoFaker.cs @@ -1,11 +1,11 @@ using Bogus; +using Bogus.DataSets; using Bogus.Extensions; using FunderMaps.Core.Types; -using FunderMaps.Testing.Extensions; using FunderMaps.WebApi.DataTransferObjects; using System; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/RecoverySampleDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/RecoverySampleDtoFaker.cs similarity index 96% rename from tests/FunderMaps.Testing/Faker/RecoverySampleDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/RecoverySampleDtoFaker.cs index 44146b083..6d57af0f4 100644 --- a/tests/FunderMaps.Testing/Faker/RecoverySampleDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/RecoverySampleDtoFaker.cs @@ -3,7 +3,7 @@ using FunderMaps.WebApi.DataTransferObjects; using System; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/StatusChangeDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/StatusChangeDtoFaker.cs similarity index 90% rename from tests/FunderMaps.Testing/Faker/StatusChangeDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/StatusChangeDtoFaker.cs index f9a34a816..96c7cbd99 100644 --- a/tests/FunderMaps.Testing/Faker/StatusChangeDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/StatusChangeDtoFaker.cs @@ -1,7 +1,7 @@ using Bogus; using FunderMaps.WebApi.DataTransferObjects; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/UserDtoFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/UserDtoFaker.cs similarity index 95% rename from tests/FunderMaps.Testing/Faker/UserDtoFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/UserDtoFaker.cs index e9ccbe167..186fb8418 100644 --- a/tests/FunderMaps.Testing/Faker/UserDtoFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/UserDtoFaker.cs @@ -4,7 +4,7 @@ using FunderMaps.Core.Types; using System.Linq; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.Testing/Faker/UserFaker.cs b/tests/FunderMaps.IntegrationTests/Faker/UserFaker.cs similarity index 98% rename from tests/FunderMaps.Testing/Faker/UserFaker.cs rename to tests/FunderMaps.IntegrationTests/Faker/UserFaker.cs index 622bcf8d8..f5c316670 100644 --- a/tests/FunderMaps.Testing/Faker/UserFaker.cs +++ b/tests/FunderMaps.IntegrationTests/Faker/UserFaker.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; -namespace FunderMaps.Testing.Faker +namespace FunderMaps.IntegrationTests.Faker { /// /// Faker for . diff --git a/tests/FunderMaps.IntegrationTests/FileUploadContent.cs b/tests/FunderMaps.IntegrationTests/FileUploadContent.cs index 500ffe289..67dfb12f4 100644 --- a/tests/FunderMaps.IntegrationTests/FileUploadContent.cs +++ b/tests/FunderMaps.IntegrationTests/FileUploadContent.cs @@ -1,6 +1,5 @@ using System.Net.Http; using System.Net.Http.Headers; -using Bogus; namespace FunderMaps.IntegrationTests { @@ -9,7 +8,7 @@ namespace FunderMaps.IntegrationTests /// public class FileUploadContent : MultipartFormDataContent { - private Faker faker = new(); + private Bogus.Faker faker = new(); private HttpContent byteContent; diff --git a/tests/FunderMaps.IntegrationTests/FunderMaps.IntegrationTests.csproj b/tests/FunderMaps.IntegrationTests/FunderMaps.IntegrationTests.csproj index ea1795c16..384fcc3f4 100644 --- a/tests/FunderMaps.IntegrationTests/FunderMaps.IntegrationTests.csproj +++ b/tests/FunderMaps.IntegrationTests/FunderMaps.IntegrationTests.csproj @@ -24,7 +24,6 @@ - diff --git a/tests/FunderMaps.IntegrationTests/Portal/IncidentPortalTests.cs b/tests/FunderMaps.IntegrationTests/Portal/IncidentPortalTests.cs index 62313e6db..7a8099ea2 100644 --- a/tests/FunderMaps.IntegrationTests/Portal/IncidentPortalTests.cs +++ b/tests/FunderMaps.IntegrationTests/Portal/IncidentPortalTests.cs @@ -1,5 +1,4 @@ using FunderMaps.Core.Types; -using FunderMaps.Testing.Faker; using FunderMaps.AspNetCore.DataTransferObjects; using System; using System.Collections.Generic; @@ -8,6 +7,7 @@ using System.Net.Http.Json; using System.Threading.Tasks; using Xunit; +using FunderMaps.IntegrationTests.Faker; namespace FunderMaps.IntegrationTests.Portal { @@ -248,7 +248,7 @@ public async Task UploadEmptyDocumentReturnBadRequest() mediaType: "application/pdf", fileExtension: "pdf", byteContentLength: 0); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(); // Act var response = await client.PostAsync("api/incident-portal/upload-document", formContent); diff --git a/tests/FunderMaps.IntegrationTests/Webservice/AuthTests.cs b/tests/FunderMaps.IntegrationTests/Webservice/AuthTests.cs index 6fe698f67..c0af70a81 100644 --- a/tests/FunderMaps.IntegrationTests/Webservice/AuthTests.cs +++ b/tests/FunderMaps.IntegrationTests/Webservice/AuthTests.cs @@ -1,6 +1,6 @@ using Bogus; +using Bogus.DataSets; using FunderMaps.AspNetCore.DataTransferObjects; -using FunderMaps.Testing.Extensions; using System; using System.Net; using System.Net.Http.Json; diff --git a/tests/FunderMaps.Testing/FunderMaps.Testing.csproj b/tests/FunderMaps.Testing/FunderMaps.Testing.csproj deleted file mode 100644 index 7eacd6ad7..000000000 --- a/tests/FunderMaps.Testing/FunderMaps.Testing.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - - net5.0 - latest - - - - - - - - - - - - - diff --git a/tests/FunderMaps.Testing/Helper.cs b/tests/FunderMaps.Testing/Helper.cs deleted file mode 100644 index ff91aa6d4..000000000 --- a/tests/FunderMaps.Testing/Helper.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using FunderMaps.Core; - -namespace FunderMaps.Testing -{ - /// - /// Helper class. - /// - public static class Helper - { - /// - /// Return as . - /// - /// Generic type. - /// Input enumerable. - /// Instance of . - public static async IAsyncEnumerable AsAsyncEnumerable(IEnumerable list) - { - await Task.CompletedTask; - foreach (var item in list) - { - yield return item; - } - } - - /// - /// Apply navigation rules to . - /// - /// Generic type. - /// Input enumerable. - /// Navigation rules. - /// - public static IEnumerable ApplyNavigation(IEnumerable list, Navigation navigation) - { - if (navigation.Offset > 0) - { - list = list.Skip(navigation.Offset); - } - if (navigation.Limit > 0) - { - list = list.Take(navigation.Limit); - } - - return list; - } - } -} From 802e464f3fed3e2faa5f403e19c386ecceb49f40 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Mon, 22 Mar 2021 14:38:18 +0100 Subject: [PATCH 05/21] Set description --- .../Services/ProductService.cs | 72 ++++++++++++--- .../Types/Products/AnalysisProduct.cs | 88 +++++++++---------- 2 files changed, 103 insertions(+), 57 deletions(-) diff --git a/src/FunderMaps.Core/Services/ProductService.cs b/src/FunderMaps.Core/Services/ProductService.cs index c08874108..a243e9aa2 100644 --- a/src/FunderMaps.Core/Services/ProductService.cs +++ b/src/FunderMaps.Core/Services/ProductService.cs @@ -59,6 +59,54 @@ private async Task GetStatisticsByExternalIdAsync(string id) MunicipalityReportCount = await _statisticsRepository.GetMunicipalityReportCountByExternalIdAsync(id), }; + private string DescriptionDrystand(FoundationRisk? risk, double? drystand) + => risk switch + { + var risk_low when + risk_low == FoundationRisk.A || + risk_low == FoundationRisk.B => $"Er komt doorgaans geen droogstand voor. Er is een veilige marge van {drystand}m dat het grondwater hoger staat dan het hoogstgelegen funderingshout. Hierdoor kan het funderingshout niet rotten door een tekort aan zuurstof.", + + var risk_medium when + risk_medium == FoundationRisk.C || + risk_medium == FoundationRisk.D => $"Er kan een droogstand van ca. {drystand}m van het hoogstgelegen funderingshout voorkomen maar deze bevindt zich doorgaans in een acceptabele marge. Bij wijzigende omstandigheden kan de situatie verergeren waardoor het funderingshout regelmatig droog kan komen te staan en het hout kan gaan rotten. Na jarenlange droogstand kan het draagvermogen van de fundering zijn aangetast waardoor het pand kan gaan deformeren. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.​", + + FoundationRisk.E => $"Er kan een droogstand van ca. {drystand}m van het hoogstgelegen funderingshout voorkomen. Hierdoor kan het funderingshout regelmatig droog komen te staan waardoor het hout kan gaan rotten. Na jarenlange droogstand kan het draagvermogen van de fundering zijn aangetast waardoor het pand kan gaan deformeren. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.", + + _ => "Onbekend", + }; + + private string DescriptionDewateringDepth(FoundationRisk? risk, double? dewateringDepth) + => risk switch + { + var risk_low when + risk_low == FoundationRisk.A || + risk_low == FoundationRisk.B => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] laag.​", + + var risk_medium when + risk_medium == FoundationRisk.C || + risk_medium == FoundationRisk.D => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] gemiddeld.​", + + FoundationRisk.E => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] verhoogd.", + + _ => "Onbekend", + }; + + private string DescriptionBioInfection(FoundationRisk? risk) + => risk switch + { + var risk_low when + risk_low == FoundationRisk.A || + risk_low == FoundationRisk.B => "Toepassing van grenen palen is hoogstwaarschijnlijk niet toegepast bij dit adres gelet op de diepteligging van de draagkrachtige zandlaag. Deze ligt dieper dan 10 m onder het maaiveld. Grenen palen zijn zelden langer dan 10 m. Toepassing is daarmee onwaarschijnlijk.​", + + var risk_medium when + risk_medium == FoundationRisk.C || + risk_medium == FoundationRisk.D => "Toepassing van grenen palen kan voorkomen bij dit adres gelet op de diepteligging van de ondiepe draagkrachtige zandlaag. Grenen palen zijn zelden langer dan 10 m. Grenen palen zijn gevoelig voor bacteriële aantasting waardoor het hout van buitenaf wordt aangetast over de gehele lengte van de paal. Het draagvermogen van de paal neemt daarmee af waardoor het pand gaat verzakken. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.​", + + FoundationRisk.E => "Grenen palen zijn hoogstwaarschijnlijk toegepast bij dit adres, gelet op de diepteligging van de ondiepe draagkrachtige zandlaag en het bouwjaar. Grenen palen zijn zelden langer dan 10 m. Grenen palen zijn gevoelig voor bacteriële aantasting waardoor het hout van buitenaf wordt aangetast over de gehele lengte van de paal. Het draagvermogen van de paal neemt daarmee af waardoor het pand gaat verzakken. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.", + + _ => "Onbekend", + }; + /// /// Get an analysis product. /// @@ -74,21 +122,19 @@ public virtual async IAsyncEnumerable GetAnalysisAsync(Analysis _ => throw new InvalidIdentifierException(), }) { - // FUTURE: Retrieve the description from a service. - product.DescriptionDrystand = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"; - product.DescriptionDewateringDepth = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"; - product.DescriptionBioInfection = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"; - product.DescriptionRestorationCosts = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"; - - switch (productType) + yield return product with { - case AnalysisProductType.RiskPlus: - case AnalysisProductType.Complete: - product.Statistics = await GetStatisticsByIdAsync(product.NeighborhoodId); - break; - }; + DescriptionDrystand = DescriptionDrystand(product.DrystandRisk, product.Drystand), + DescriptionDewateringDepth = DescriptionDewateringDepth(product.DewateringDepthRisk, product.DewateringDepth), + DescriptionBioInfection = DescriptionBioInfection(product.BioInfectionRisk), - yield return product; + Statistics = productType switch + { + AnalysisProductType.RiskPlus => await GetStatisticsByIdAsync(product.NeighborhoodId), + AnalysisProductType.Complete => await GetStatisticsByIdAsync(product.NeighborhoodId), + _ => null, + }, + }; } } diff --git a/src/FunderMaps.Core/Types/Products/AnalysisProduct.cs b/src/FunderMaps.Core/Types/Products/AnalysisProduct.cs index 225b3aa81..182a79564 100644 --- a/src/FunderMaps.Core/Types/Products/AnalysisProduct.cs +++ b/src/FunderMaps.Core/Types/Products/AnalysisProduct.cs @@ -6,224 +6,224 @@ namespace FunderMaps.Core.Types.Products /// /// Represents a model for the complete endpoint. /// - public sealed class AnalysisProduct + public sealed record AnalysisProduct { /// /// Building identifier. /// [Geocoder] - public string Id { get; set; } + public string Id { get; init; } /// /// Building external identifier. /// - public string ExternalId { get; set; } + public string ExternalId { get; init; } /// /// Postal code /// - public string PostalCode { get; set; } + public string PostalCode { get; init; } /// /// Represents the external data source of this building. /// - public ExternalDataSource? ExternalSource { get; set; } + public ExternalDataSource? ExternalSource { get; init; } /// /// Built year. /// - public DateTimeOffset ConstructionYear { get; set; } + public DateTimeOffset ConstructionYear { get; init; } /// /// Built year source. /// - public BuiltYearSource ConstructionYearSource { get; set; } + public BuiltYearSource ConstructionYearSource { get; init; } /// /// Address identifier. /// [Geocoder] - public string AddressId { get; set; } + public string AddressId { get; init; } /// /// Address external identifier. /// - public string AddressExternalId { get; set; } + public string AddressExternalId { get; init; } /// /// Neighborhood identifier. /// [Geocoder] - public string NeighborhoodId { get; set; } + public string NeighborhoodId { get; init; } /// /// Represents the ground water level. /// - public double? GroundWaterLevel { get; set; } + public double? GroundWaterLevel { get; init; } /// /// Soil code. /// - public string Soil { get; set; } + public string Soil { get; init; } /// /// Represents the height of this building. /// - public double? BuildingHeight { get; set; } + public double? BuildingHeight { get; init; } /// /// Ground level in meters. /// - public double? GroundLevel { get; set; } + public double? GroundLevel { get; init; } /// /// Cone penetration test name. /// - public string Cpt { get; set; } + public string Cpt { get; init; } /// /// Monitoring well. /// - public string MonitoringWell { get; set; } + public string MonitoringWell { get; init; } /// /// Recovery advised. /// - public bool? RecoveryAdvised { get; set; } + public bool? RecoveryAdvised { get; init; } /// /// Damage cause. /// - public FoundationDamageCause? DamageCause { get; set; } + public FoundationDamageCause? DamageCause { get; init; } /// /// Substructure. /// - public Substructure? Substructure { get; set; } + public Substructure? Substructure { get; init; } /// /// Client document identifier. /// - public string DocumentName { get; set; } + public string DocumentName { get; init; } /// /// Original document creation. /// - public DateTime? DocumentDate { get; set; } + public DateTime? DocumentDate { get; init; } /// /// Report type. /// - public InquiryType? InquiryType { get; set; } + public InquiryType? InquiryType { get; init; } /// /// Foundation recovery type. /// - public RecoveryDocumentType? RecoveryType { get; set; } + public RecoveryDocumentType? RecoveryType { get; init; } /// /// Foundation recovery status. /// - public RecoveryStatus? RecoveryStatus { get; set; } + public RecoveryStatus? RecoveryStatus { get; init; } /// /// Building surface area in square meters. /// - public double? SurfaceArea { get; set; } + public double? SurfaceArea { get; init; } /// /// Address living surface area in square meters. /// - public double? LivingArea { get; set; } + public double? LivingArea { get; init; } /// /// Foundation bearing ground layer. /// - public double? FoundationBearingLayer { get; set; } + public double? FoundationBearingLayer { get; init; } /// /// Represents the estimated restoration costs for this building. /// - public double? RestorationCosts { get; set; } + public double? RestorationCosts { get; init; } /// /// Description for restoration costs. /// - public string DescriptionRestorationCosts { get; set; } + public string DescriptionRestorationCosts { get; init; } /// /// Foundation type. /// - public FoundationType? FoundationType { get; set; } + public FoundationType? FoundationType { get; init; } /// /// Foundation type reliability. /// - public Reliability FoundationTypeReliability { get; set; } + public Reliability FoundationTypeReliability { get; init; } /// /// Represents the period of drought (droogstand) for this building. /// - public double? Drystand { get; set; } + public double? Drystand { get; init; } /// /// Foundation type reliability. /// - public Reliability DrystandReliability { get; set; } + public Reliability DrystandReliability { get; init; } /// /// Represents the foundation risk for this building. /// - public FoundationRisk? DrystandRisk { get; set; } + public FoundationRisk? DrystandRisk { get; init; } /// /// Description for drystand. /// - public string DescriptionDrystand { get; set; } + public string DescriptionDrystand { get; init; } /// /// Dewatering depth. /// - public double? DewateringDepth { get; set; } + public double? DewateringDepth { get; init; } /// /// Dewatering depth reliability. /// - public Reliability DewateringDepthReliability { get; set; } + public Reliability DewateringDepthReliability { get; init; } /// /// Dewatering depth risk. /// - public FoundationRisk? DewateringDepthRisk { get; set; } + public FoundationRisk? DewateringDepthRisk { get; init; } /// /// Description for dewatering depth. /// - public string DescriptionDewateringDepth { get; set; } + public string DescriptionDewateringDepth { get; init; } /// /// Biological infection. /// - public string BioInfection { get; set; } + public string BioInfection { get; init; } /// /// Biological infection reliability. /// - public Reliability BioInfectionReliability { get; set; } + public Reliability BioInfectionReliability { get; init; } /// /// Biological infection risk. /// - public FoundationRisk? BioInfectionRisk { get; set; } + public FoundationRisk? BioInfectionRisk { get; init; } /// /// Description for biological infection. /// - public string DescriptionBioInfection { get; set; } + public string DescriptionBioInfection { get; init; } /// /// Statistisch per region. /// - public StatisticsProduct Statistics { get; set; } + public StatisticsProduct Statistics { get; init; } } } From bcdc4faab3067ea7dc97aea423c7494d6a7caf17 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Tue, 23 Mar 2021 10:18:35 +0100 Subject: [PATCH 06/21] Move auth logic to AspNetCore package --- .../Authentication/AuthResult.cs | 30 ---- .../Authentication/SignInContext.cs | 54 ------- .../Authentication/SignInHandler.cs | 105 -------------- .../Controllers/AuthController.cs | 11 +- .../Controllers/OrganizationUserController.cs | 12 +- .../Controllers/UserController.cs | 12 +- .../AuthenticationBuilderExtensions.cs | 27 ---- .../Filters/FunderMapsCoreExceptionFilter.cs | 2 +- .../FunderMapsStartup.cs | 46 ++++++ .../SignInService.cs | 136 ++++++------------ .../OrganizationUserAdminController.cs | 12 +- src/FunderMaps.WebApi/Startup.cs | 34 +---- src/FunderMaps.Webservice/Startup.cs | 32 ----- 13 files changed, 112 insertions(+), 401 deletions(-) delete mode 100644 src/FunderMaps.AspNetCore/Authentication/AuthResult.cs delete mode 100644 src/FunderMaps.AspNetCore/Authentication/SignInContext.cs delete mode 100644 src/FunderMaps.AspNetCore/Authentication/SignInHandler.cs delete mode 100644 src/FunderMaps.AspNetCore/Extensions/AuthenticationBuilderExtensions.cs rename src/FunderMaps.AspNetCore/{Authentication => Services}/SignInService.cs (53%) diff --git a/src/FunderMaps.AspNetCore/Authentication/AuthResult.cs b/src/FunderMaps.AspNetCore/Authentication/AuthResult.cs deleted file mode 100644 index 4715e1887..000000000 --- a/src/FunderMaps.AspNetCore/Authentication/AuthResult.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace FunderMaps.AspNetCore.Authentication -{ - /// - /// Represents the result authentication operation. - /// - public enum AuthResult - { - /// - /// Successful operation. - /// - Success = 0, - - /// - /// Failed operation. - /// - Failed = 1, - - /// - /// Operation failed because the - /// user was locked out. - /// - LockedOut = 2, - - /// - /// Operation failed because the user - /// is not allowed to sign-in. - /// - NotAllowed = 3, - } -} diff --git a/src/FunderMaps.AspNetCore/Authentication/SignInContext.cs b/src/FunderMaps.AspNetCore/Authentication/SignInContext.cs deleted file mode 100644 index ffa13da2a..000000000 --- a/src/FunderMaps.AspNetCore/Authentication/SignInContext.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Security.Claims; - -namespace FunderMaps.AspNetCore.Authentication -{ - /// - /// Context returned from various authentication operations. - /// - public record SignInContext - { - /// - /// Return result as Failed. - /// - public static SignInContext Failed { get; } = new SignInContext(AuthResult.Failed); - - /// - /// Return result as Success. - /// - public static SignInContext Success { get; } = new SignInContext(AuthResult.Success); - - /// - /// Return result as LockedOut. - /// - public static SignInContext LockedOut { get; } = new SignInContext(AuthResult.LockedOut); - - /// - /// Return result as NotAllowed. - /// - public static SignInContext NotAllowed { get; } = new SignInContext(AuthResult.NotAllowed); - - /// - /// Authentication result. - /// - public AuthResult Result { get; } - - /// - /// Claims principal. - /// - public ClaimsPrincipal Principal { get; } - - /// - /// Create new instance. - /// - public SignInContext(AuthResult result) => Result = result; - - /// - /// Create new instance. - /// - public SignInContext(AuthResult result, ClaimsPrincipal principal) - { - Result = result; - Principal = principal; - } - } -} diff --git a/src/FunderMaps.AspNetCore/Authentication/SignInHandler.cs b/src/FunderMaps.AspNetCore/Authentication/SignInHandler.cs deleted file mode 100644 index e3d188a97..000000000 --- a/src/FunderMaps.AspNetCore/Authentication/SignInHandler.cs +++ /dev/null @@ -1,105 +0,0 @@ -using FunderMaps.Core.Exceptions; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using System; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace FunderMaps.AspNetCore.Authentication -{ - /// - /// Helper for the sign-in process. Consolidates different authentication - /// services and serves functionallity through a facade. - /// - /// - /// This is the only object which should handle authentication and sign-in requests - /// for the entire web framework. - /// - public class SignInHandler - { - /// - /// The used. - /// - public ISecurityTokenProvider TokenProvider { get; } - - /// - /// The used. - /// - public SignInService SignInService { get; } - - /// - /// Create new instance. - /// - public SignInHandler(SignInService signInService, ISecurityTokenProvider tokenProvider) - { - SignInService = signInService ?? throw new ArgumentNullException(nameof(signInService)); - TokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); - } - - /// - /// Signin the user by email and password. - /// - /// - /// Discrepanties in the input can lead to different behaviour further down - /// the call tree, possibly leaking information on exceptional cases. - /// If any is thrown we must consider - /// it an authentication failure. - /// - /// User email. - /// User password. - /// Security token if the authentication attempt was successful. - public async Task SignInAsync(string email, string password) - { - try - { - var result = await SignInService.PasswordSignInAsync(email, password, JwtBearerDefaults.AuthenticationScheme); - if (result.Result != AuthResult.Success) - { - throw new AuthenticationException(); - } - - if (result.Principal is null) - { - throw new AuthenticationException(); - } - return TokenProvider.GetTokenContext(result.Principal); - } - catch (FunderMapsCoreException) - { - throw new AuthenticationException(); - } - } - - /// - /// Signin the user by active session. - /// - /// - /// Discrepanties in the input can lead to different behaviour further down - /// the call tree, possibly leaking information on exceptional cases. - /// If any is thrown we must consider - /// it an authentication failure. - /// - /// Context principal. - /// Security token if the authentication attempt was successful. - public async Task RefreshSignInAsync(ClaimsPrincipal principal) - { - try - { - var result = await SignInService.SignInAsync(principal, JwtBearerDefaults.AuthenticationScheme); - if (result.Result != AuthResult.Success) - { - throw new AuthenticationException(); - } - - if (result.Principal is null) - { - throw new AuthenticationException(); - } - return TokenProvider.GetTokenContext(result.Principal); - } - catch (FunderMapsCoreException) - { - throw new AuthenticationException(); - } - } - } -} diff --git a/src/FunderMaps.AspNetCore/Controllers/AuthController.cs b/src/FunderMaps.AspNetCore/Controllers/AuthController.cs index 21466928e..75f228b0e 100644 --- a/src/FunderMaps.AspNetCore/Controllers/AuthController.cs +++ b/src/FunderMaps.AspNetCore/Controllers/AuthController.cs @@ -1,6 +1,7 @@ using AutoMapper; using FunderMaps.AspNetCore.Authentication; using FunderMaps.AspNetCore.DataTransferObjects; +using FunderMaps.AspNetCore.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; @@ -16,15 +17,15 @@ namespace FunderMaps.AspNetCore.Controllers public class AuthController : ControllerBase { private readonly IMapper _mapper; - private readonly SignInHandler _authenticationHelper; + private readonly SignInService _signInService; /// /// Create new instance. /// - public AuthController(IMapper mapper, SignInHandler authenticationHelper) + public AuthController(IMapper mapper, SignInService signInService) { _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - _authenticationHelper = authenticationHelper ?? throw new ArgumentNullException(nameof(authenticationHelper)); + _signInService = signInService ?? throw new ArgumentNullException(nameof(signInService)); } // POST: auth/signin @@ -36,7 +37,7 @@ public AuthController(IMapper mapper, SignInHandler authenticationHelper) public async Task SignInAsync([FromBody] SignInDto input) { // Act. - TokenContext context = await _authenticationHelper.SignInAsync(input.Email, input.Password); + TokenContext context = await _signInService.PasswordSignInAsync(input.Email, input.Password); // Map. var output = _mapper.Map(context); @@ -53,7 +54,7 @@ public async Task SignInAsync([FromBody] SignInDto input) public async Task RefreshSignInAsync() { // Act. - TokenContext context = await _authenticationHelper.RefreshSignInAsync(User); + TokenContext context = await _signInService.SignInAsync(User); // Map. var output = _mapper.Map(context); diff --git a/src/FunderMaps.AspNetCore/Controllers/OrganizationUserController.cs b/src/FunderMaps.AspNetCore/Controllers/OrganizationUserController.cs index a4da3ecdf..dd896176a 100644 --- a/src/FunderMaps.AspNetCore/Controllers/OrganizationUserController.cs +++ b/src/FunderMaps.AspNetCore/Controllers/OrganizationUserController.cs @@ -1,6 +1,6 @@ using AutoMapper; -using FunderMaps.AspNetCore.Authentication; using FunderMaps.AspNetCore.DataTransferObjects; +using FunderMaps.AspNetCore.Services; using FunderMaps.Core.Entities; using FunderMaps.Core.Exceptions; using FunderMaps.Core.Interfaces.Repositories; @@ -27,7 +27,7 @@ public class OrganizationUserController : ControllerBase private readonly Core.AppContext _appContext; private readonly IUserRepository _userRepository; private readonly IOrganizationUserRepository _organizationUserRepository; - private readonly SignInService _signinService; + private readonly SignInService _signInService; /// /// Create new instance. @@ -37,13 +37,13 @@ public OrganizationUserController( Core.AppContext appContext, IUserRepository userRepository, IOrganizationUserRepository organizationUserRepository, - SignInService signinService) + SignInService signInService) { _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _appContext = appContext ?? throw new ArgumentNullException(nameof(appContext)); _userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository)); _organizationUserRepository = organizationUserRepository ?? throw new ArgumentNullException(nameof(organizationUserRepository)); - _signinService = signinService ?? throw new ArgumentNullException(nameof(signinService)); + _signInService = signInService ?? throw new ArgumentNullException(nameof(signInService)); } // POST: organization/user @@ -60,7 +60,7 @@ public async Task AddUserAsync([FromBody] OrganizationUserPasswor // Act. // FUTURE: Do in 1 call. user = await _userRepository.AddGetAsync(user); - await _signinService.SetPasswordAsync(user.Id, input.Password); + await _signInService.SetPasswordAsync(user.Id, input.Password); await _organizationUserRepository.AddAsync(_appContext.TenantId, user.Id, input.OrganizationRole); // Map. @@ -153,7 +153,7 @@ public async Task ChangePasswordAsync(Guid id, [FromBody] ChangeP } // Act. - await _signinService.SetPasswordAsync(id, input.NewPassword); + await _signInService.SetPasswordAsync(id, input.NewPassword); // Return. return NoContent(); diff --git a/src/FunderMaps.AspNetCore/Controllers/UserController.cs b/src/FunderMaps.AspNetCore/Controllers/UserController.cs index 1e450c582..449a9246b 100644 --- a/src/FunderMaps.AspNetCore/Controllers/UserController.cs +++ b/src/FunderMaps.AspNetCore/Controllers/UserController.cs @@ -1,6 +1,6 @@ using AutoMapper; -using FunderMaps.AspNetCore.Authentication; using FunderMaps.AspNetCore.DataTransferObjects; +using FunderMaps.AspNetCore.Services; using FunderMaps.Core.Entities; using FunderMaps.Core.Exceptions; using FunderMaps.Core.Interfaces.Repositories; @@ -25,17 +25,17 @@ public class UserController : ControllerBase private readonly IMapper _mapper; private readonly Core.AppContext _appContext; private readonly IUserRepository _userRepository; - private readonly SignInService _signinService; + private readonly SignInService _signInService; /// /// Create new instance. /// - public UserController(IMapper mapper, Core.AppContext appContext, IUserRepository userRepository, SignInService signinService) + public UserController(IMapper mapper, Core.AppContext appContext, IUserRepository userRepository, SignInService signInService) { _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _appContext = appContext ?? throw new ArgumentNullException(nameof(appContext)); _userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository)); - _signinService = signinService ?? throw new ArgumentNullException(nameof(signinService)); + _signInService = signInService ?? throw new ArgumentNullException(nameof(signInService)); } // GET: user @@ -81,12 +81,12 @@ public async Task UpdateAsync([FromBody] UserDto input) public async Task ChangePasswordAsync([FromBody] ChangePasswordDto input) { // Act. - if (!await _signinService.CheckPasswordAsync(_appContext.UserId, input.OldPassword)) + if (!await _signInService.CheckPasswordAsync(_appContext.UserId, input.OldPassword)) { throw new InvalidCredentialException(); } - await _signinService.SetPasswordAsync(_appContext.UserId, input.NewPassword); + await _signInService.SetPasswordAsync(_appContext.UserId, input.NewPassword); // Return. return NoContent(); diff --git a/src/FunderMaps.AspNetCore/Extensions/AuthenticationBuilderExtensions.cs b/src/FunderMaps.AspNetCore/Extensions/AuthenticationBuilderExtensions.cs deleted file mode 100644 index a5a4416ed..000000000 --- a/src/FunderMaps.AspNetCore/Extensions/AuthenticationBuilderExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FunderMaps.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication; -using System; - -namespace Microsoft.Extensions.DependencyInjection -{ - /// - /// Extensions to the authentication builder. - /// - public static class AuthenticationBuilderExtensions - { - /// - /// Register as . - /// - public static AuthenticationBuilder AddJwtBearerTokenProvider(this AuthenticationBuilder authenticationBuilder) - { - if (authenticationBuilder is null) - { - throw new ArgumentNullException(nameof(authenticationBuilder)); - } - - authenticationBuilder.Services.AddTransient(); - - return authenticationBuilder; - } - } -} diff --git a/src/FunderMaps.AspNetCore/Filters/FunderMapsCoreExceptionFilter.cs b/src/FunderMaps.AspNetCore/Filters/FunderMapsCoreExceptionFilter.cs index b5bd7f552..c79d835b3 100644 --- a/src/FunderMaps.AspNetCore/Filters/FunderMapsCoreExceptionFilter.cs +++ b/src/FunderMaps.AspNetCore/Filters/FunderMapsCoreExceptionFilter.cs @@ -25,7 +25,7 @@ public class FunderMapsCoreExceptionFilter : IExceptionFilter { typeof(ProcessException), HttpStatusCode.InternalServerError }, { typeof(QueueOverflowException), HttpStatusCode.InternalServerError }, { typeof(ReferenceNotFoundException), HttpStatusCode.NotFound }, - { typeof(ServiceUnavailableException), HttpStatusCode.NotAcceptable }, + { typeof(ServiceUnavailableException), HttpStatusCode.ServiceUnavailable }, { typeof(StateTransitionException), HttpStatusCode.Forbidden }, { typeof(StorageException), HttpStatusCode.InternalServerError }, { typeof(UnhandledTaskException), HttpStatusCode.InternalServerError }, diff --git a/src/FunderMaps.AspNetCore/FunderMapsStartup.cs b/src/FunderMaps.AspNetCore/FunderMapsStartup.cs index bcb7c8891..95b52e41d 100644 --- a/src/FunderMaps.AspNetCore/FunderMapsStartup.cs +++ b/src/FunderMaps.AspNetCore/FunderMapsStartup.cs @@ -2,16 +2,22 @@ using System.Diagnostics.CodeAnalysis; using AutoMapper; using FunderMaps.AspNetCore.Authentication; +using FunderMaps.AspNetCore.Authorization; using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.AspNetCore.HealthChecks; using FunderMaps.AspNetCore.Middleware; +using FunderMaps.AspNetCore.Services; using FunderMaps.Core.Entities; using FunderMaps.Core.Types; using FunderMaps.Core.Types.Distributions; using FunderMaps.Core.Types.Products; +using FunderMaps.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; [assembly: ApiController] [assembly: HostingStartup(typeof(FunderMaps.AspNetCore.FunderMapsStartup))] @@ -22,6 +28,16 @@ namespace FunderMaps.AspNetCore /// public class FunderMapsStartup : IHostingStartup { + /// + /// Configuration. + /// + public static IConfiguration Configuration { get; set; } + + /// + /// Host environment. + /// + public static IHostEnvironment HostEnvironment { get; set; } + /// /// Use this method to add entity and object mapping configurations. /// @@ -73,6 +89,9 @@ public void Configure([DisallowNull] IWebHostBuilder builder) builder.ConfigureServices(services => { + // The startup essential properties can be used to setup components. + (Configuration, HostEnvironment) = services.BuildStartupProperties(); + services.AddAutoMapper(mapper => ConfigureMapper(mapper)); // FUTURE: Only load specific parts. @@ -84,8 +103,35 @@ public void Configure([DisallowNull] IWebHostBuilder builder) // Register components from reference assemblies. services.AddFunderMapsCoreServices(); + { + // Add the authentication layer. + services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.SaveToken = false; + options.TokenValidationParameters = new JwtTokenValidationParameters + { + ValidIssuer = Configuration.GetJwtIssuer(), + ValidAudience = Configuration.GetJwtAudience(), + IssuerSigningKey = Configuration.GetJwtSigningKey(), + Valid = Configuration.GetJwtTokenExpirationInMinutes(), + }; + }); + + // Add the authorization layer. + services.AddAuthorization(options => + { + options.FallbackPolicy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .Build(); + + options.AddFunderMapsPolicy(); + }); + } + // Adds the core authentication service to the container. services.AddScoped(); + services.AddTransient(); // NOTE: Register the HttpContextAccessor service to the container. // The HttpContextAccessor exposes a singleton holding the diff --git a/src/FunderMaps.AspNetCore/Authentication/SignInService.cs b/src/FunderMaps.AspNetCore/Services/SignInService.cs similarity index 53% rename from src/FunderMaps.AspNetCore/Authentication/SignInService.cs rename to src/FunderMaps.AspNetCore/Services/SignInService.cs index f864a317d..b46a6e242 100644 --- a/src/FunderMaps.AspNetCore/Authentication/SignInService.cs +++ b/src/FunderMaps.AspNetCore/Services/SignInService.cs @@ -1,14 +1,17 @@ -using FunderMaps.Core.Entities; +using FunderMaps.AspNetCore.Authentication; +using FunderMaps.Core.Entities; +using FunderMaps.Core.Exceptions; using FunderMaps.Core.Identity; using FunderMaps.Core.Interfaces; using FunderMaps.Core.Interfaces.Repositories; using FunderMaps.Core.Types; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Logging; using System; using System.Security.Claims; using System.Threading.Tasks; -namespace FunderMaps.AspNetCore.Authentication +namespace FunderMaps.AspNetCore.Services { /// /// Provides the APIs for authentication. @@ -35,6 +38,11 @@ public class SignInService /// public IPasswordHasher PasswordHasher { get; } + /// + /// The used. + /// + public ISecurityTokenProvider TokenProvider { get; } + /// /// Gets the used to log messages. /// @@ -48,32 +56,17 @@ public SignInService( IOrganizationUserRepository organizationUserRepository, IOrganizationRepository organizationRepository, IPasswordHasher passwordHasher, + ISecurityTokenProvider tokenProvider, ILogger logger) { UserRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository)); OrganizationUserRepository = organizationUserRepository ?? throw new ArgumentNullException(nameof(organizationUserRepository)); OrganizationRepository = organizationRepository ?? throw new ArgumentNullException(nameof(organizationRepository)); PasswordHasher = passwordHasher ?? throw new ArgumentNullException(nameof(passwordHasher)); + TokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); } - /// - /// Returns a flag indicating whether the specified user can sign in. - /// - /// The user id whose sign-in status should be returned. - /// True if the specified user can sign-in, otherwise false. - public virtual async Task CanSignInAsync(Guid id) - { - if (await UserRepository.IsLockedOutAsync(id)) - { - Logger.LogWarning($"User '{id}' is currently locked out."); - - return SignInContext.LockedOut; - } - - return SignInContext.Success; - } - /// /// Test if the provided password is valid for the user. /// @@ -82,7 +75,7 @@ public virtual async Task CanSignInAsync(Guid id) /// True if the specified password is valid for the user, otherwise false. public virtual async Task CheckPasswordAsync(Guid id, string password) { - var passwordHash = await UserRepository.GetPasswordHashAsync(id); + string passwordHash = await UserRepository.GetPasswordHashAsync(id); return PasswordHasher.IsPasswordValid(passwordHash, password); } @@ -93,7 +86,7 @@ public virtual async Task CheckPasswordAsync(Guid id, string password) /// The plaintext password to be set on the user. public virtual async Task SetPasswordAsync(Guid id, string password) { - var passwordHash = PasswordHasher.HashPassword(password); + string passwordHash = PasswordHasher.HashPassword(password); await UserRepository.SetPasswordHashAsync(id, passwordHash); } @@ -101,9 +94,8 @@ public virtual async Task SetPasswordAsync(Guid id, string password) /// Attempts to sign in the specified . /// /// The principal to sign in. - /// Authentication type to use in authentication scheme. - /// Instance of . - public virtual async Task SignInAsync(ClaimsPrincipal principal, string authenticationType) + /// Instance of . + public virtual async Task SignInAsync(ClaimsPrincipal principal) { if (principal is null) { @@ -113,49 +105,28 @@ public virtual async Task SignInAsync(ClaimsPrincipal principal, var (user, tenant) = PrincipalProvider.GetUserAndTenant(principal); if (user is null || tenant is null) { - return SignInContext.Failed; + throw new AuthenticationException(); } Claim claim = principal.FindFirst(FunderMapsAuthenticationClaimTypes.TenantRole); if (claim is null) { - return SignInContext.Failed; - } - - return await SignInAsync(user, tenant, Enum.Parse(claim.Value), authenticationType); - } - - /// - /// Attempts to sign in the specified . - /// - /// The user to sign in. - /// The associated tenant. - /// The role within the organization. - /// Authentication type to use in authentication scheme. - /// Instance of . - public virtual async Task SignInAsync(IUser user, ITenant tenant, OrganizationRole organizationRole, string authenticationType) - { - if (user is null) - { - throw new ArgumentNullException(nameof(user)); + throw new AuthenticationException(); } - if (tenant is null) + if (await UserRepository.IsLockedOutAsync(user.Id)) { - throw new ArgumentNullException(nameof(tenant)); - } + Logger.LogWarning($"User '{user}' is currently locked out."); - var result = await CanSignInAsync(user.Id); - if (result != SignInContext.Success) - { - return result; + throw new AuthenticationException(); } Logger.LogTrace($"User '{user}' sign in was successful."); - return new SignInContext( - result: AuthResult.Success, - principal: PrincipalProvider.CreateTenantUserPrincipal(user, tenant, organizationRole, authenticationType)); + principal = PrincipalProvider.CreateTenantUserPrincipal(user, tenant, + Enum.Parse(claim.Value), + JwtBearerDefaults.AuthenticationScheme); + return TokenProvider.GetTokenContext(principal); } /// @@ -163,54 +134,26 @@ public virtual async Task SignInAsync(IUser user, ITenant tenant, /// /// The user email to sign in. /// The password to attempt to authenticate. - /// Authentication type to use in authentication scheme. - /// Instance of . - public virtual async Task PasswordSignInAsync(string email, string password, string authenticationType) + /// Instance of . + public virtual async Task PasswordSignInAsync(string email, string password) { IUser user = await UserRepository.GetByEmailAsync(email); if (user is null) { - return SignInContext.Failed; + throw new AuthenticationException(); } - // FUTURE: Single call? - var organizationId = await OrganizationUserRepository.GetOrganizationByUserIdAsync(user.Id); - Organization organization = await OrganizationRepository.GetByIdAsync(organizationId); - OrganizationRole organizationRole = await OrganizationUserRepository.GetOrganizationRoleByUserIdAsync(user.Id); - return await PasswordSignInAsync(user, organization, organizationRole, password, authenticationType); - } - - /// - /// Attempts a password sign in for a user. - /// - /// The user to sign in. - /// The associated tenant. - /// The role within the organization. - /// The password to attempt to authenticate. - /// Authentication type to use in authentication scheme. - /// Instance of . - public virtual async Task PasswordSignInAsync( - IUser user, - ITenant tenant, - OrganizationRole organizationRole, - string password, - string authenticationType) - { - if (user is null) + if (await UserRepository.IsLockedOutAsync(user.Id)) { - throw new ArgumentNullException(nameof(user)); - } + Logger.LogWarning($"User '{user}' is currently locked out."); - if (tenant is null) - { - throw new ArgumentNullException(nameof(tenant)); + throw new AuthenticationException(); } - SignInContext result = await CanSignInAsync(user.Id); - if (result != SignInContext.Success) - { - return result; - } + // FUTURE: Single call? + var organizationId = await OrganizationUserRepository.GetOrganizationByUserIdAsync(user.Id); + Organization organization = await OrganizationRepository.GetByIdAsync(organizationId); + OrganizationRole organizationRole = await OrganizationUserRepository.GetOrganizationRoleByUserIdAsync(user.Id); if (await CheckPasswordAsync(user.Id, password)) { @@ -219,16 +162,17 @@ public virtual async Task PasswordSignInAsync( Logger.LogInformation($"User '{user}' password sign in was successful."); - return new SignInContext( - result: AuthResult.Success, - principal: PrincipalProvider.CreateTenantUserPrincipal(user, tenant, organizationRole, authenticationType)); + ClaimsPrincipal principal = PrincipalProvider.CreateTenantUserPrincipal(user, organization, + organizationRole, + JwtBearerDefaults.AuthenticationScheme); + return TokenProvider.GetTokenContext(principal); } Logger.LogWarning($"User '{user}' failed to provide the correct password."); await UserRepository.BumpAccessFailed(user.Id); - return SignInContext.Failed; + throw new AuthenticationException(); } } } diff --git a/src/FunderMaps.WebApi/Controllers/Application/OrganizationUserAdminController.cs b/src/FunderMaps.WebApi/Controllers/Application/OrganizationUserAdminController.cs index f7953cd74..fea25ceb9 100644 --- a/src/FunderMaps.WebApi/Controllers/Application/OrganizationUserAdminController.cs +++ b/src/FunderMaps.WebApi/Controllers/Application/OrganizationUserAdminController.cs @@ -1,6 +1,6 @@ using AutoMapper; -using FunderMaps.AspNetCore.Authentication; using FunderMaps.AspNetCore.DataTransferObjects; +using FunderMaps.AspNetCore.Services; using FunderMaps.Core.Entities; using FunderMaps.Core.Exceptions; using FunderMaps.Core.Interfaces.Repositories; @@ -30,7 +30,7 @@ public class OrganizationUserAdminController : ControllerBase private readonly IMapper _mapper; private readonly IUserRepository _userRepository; private readonly IOrganizationUserRepository _organizationUserRepository; - private readonly SignInService _signinService; + private readonly SignInService _signInService; /// /// Create new instance. @@ -39,12 +39,12 @@ public OrganizationUserAdminController( IMapper mapper, IUserRepository userRepository, IOrganizationUserRepository organizationUserRepository, - SignInService signinService) + SignInService signInService) { _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository)); _organizationUserRepository = organizationUserRepository ?? throw new ArgumentNullException(nameof(organizationUserRepository)); - _signinService = signinService ?? throw new ArgumentNullException(nameof(signinService)); + _signInService = signInService ?? throw new ArgumentNullException(nameof(signInService)); } // POST: api/admin/organization/{id}/user @@ -60,7 +60,7 @@ public async Task AddUserAsync(Guid id, [FromBody] OrganizationUs // Act. // FUTURE: Do in 1 call. user = await _userRepository.AddGetAsync(user); - await _signinService.SetPasswordAsync(user.Id, input.Password); + await _signInService.SetPasswordAsync(user.Id, input.Password); await _organizationUserRepository.AddAsync(id, user.Id, input.OrganizationRole); // Map. @@ -150,7 +150,7 @@ public async Task ChangePasswordAsync(Guid id, Guid userId, [From } // Act. - await _signinService.SetPasswordAsync(userId, input.NewPassword); + await _signInService.SetPasswordAsync(userId, input.NewPassword); // Return. return NoContent(); diff --git a/src/FunderMaps.WebApi/Startup.cs b/src/FunderMaps.WebApi/Startup.cs index 941553cc2..fcf853018 100644 --- a/src/FunderMaps.WebApi/Startup.cs +++ b/src/FunderMaps.WebApi/Startup.cs @@ -1,8 +1,4 @@ -using FunderMaps.AspNetCore.Authentication; -using FunderMaps.AspNetCore.Authorization; -using FunderMaps.AspNetCore.Extensions; -using FunderMaps.Extensions; -using Microsoft.AspNetCore.Authentication.JwtBearer; +using FunderMaps.AspNetCore.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.HttpOverrides; @@ -40,31 +36,6 @@ private void StartupConfigureServices(IServiceCollection services) { services.AddAutoMapper(typeof(Startup)); - // Add the authentication layer. - services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(options => - { - options.SaveToken = false; - options.TokenValidationParameters = new JwtTokenValidationParameters - { - ValidIssuer = Configuration.GetJwtIssuer(), - ValidAudience = Configuration.GetJwtAudience(), - IssuerSigningKey = Configuration.GetJwtSigningKey(), - Valid = Configuration.GetJwtTokenExpirationInMinutes(), - }; - }) - .AddJwtBearerTokenProvider(); - - // Add the authorization layer. - services.AddAuthorization(options => - { - options.FallbackPolicy = new AuthorizationPolicyBuilder() - .RequireAuthenticatedUser() - .Build(); - - options.AddFunderMapsPolicy(); - }); - services.AddLocalization(options => { options.ResourcesPath = "Resources"; @@ -73,9 +44,6 @@ private void StartupConfigureServices(IServiceCollection services) // Register components from reference assemblies. services.AddFunderMapsInfrastructureServices(); services.AddFunderMapsDataServices("FunderMapsConnection"); - - // Configure project specific services. - services.AddTransient(); } /// diff --git a/src/FunderMaps.Webservice/Startup.cs b/src/FunderMaps.Webservice/Startup.cs index f8573d0a1..1afb3dee3 100644 --- a/src/FunderMaps.Webservice/Startup.cs +++ b/src/FunderMaps.Webservice/Startup.cs @@ -1,11 +1,7 @@ -using FunderMaps.AspNetCore.Authentication; -using FunderMaps.AspNetCore.Authorization; using FunderMaps.AspNetCore.Extensions; using FunderMaps.Core.Interfaces; using FunderMaps.Core.Services; -using FunderMaps.Extensions; using FunderMaps.Webservice.Documentation; -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.HttpOverrides; @@ -43,37 +39,9 @@ public class Startup /// See . private void StartupConfigureServices(IServiceCollection services) { - // Add the authentication layer. - services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(options => - { - options.SaveToken = false; - options.TokenValidationParameters = new JwtTokenValidationParameters - { - ValidIssuer = Configuration.GetJwtIssuer(), - ValidAudience = Configuration.GetJwtAudience(), - IssuerSigningKey = Configuration.GetJwtSigningKey(), - Valid = Configuration.GetJwtTokenExpirationInMinutes(), - }; - }) - .AddJwtBearerTokenProvider(); - - // Add the authorization layer. - services.AddAuthorization(options => - { - options.FallbackPolicy = new AuthorizationPolicyBuilder() - .RequireAuthenticatedUser() - .Build(); - - options.AddFunderMapsPolicy(); - }); - // Register components from reference assemblies. services.AddFunderMapsDataServices("FunderMapsConnection"); - // Configure project specific services. - services.AddTransient(); - // Override default product service by tracking variant of product service. services.Replace(ServiceDescriptor.Transient()); } From 2135a0ec511ab3003dc1393aaa351361c0d5d792 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Tue, 23 Mar 2021 21:06:59 +0100 Subject: [PATCH 07/21] Add soil investigation --- database/fundermaps_base.sql | 3 ++- src/FunderMaps.Core/Types/InquiryType.cs | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/database/fundermaps_base.sql b/database/fundermaps_base.sql index 16834062f..6aeadafb5 100644 --- a/database/fundermaps_base.sql +++ b/database/fundermaps_base.sql @@ -774,7 +774,8 @@ CREATE TYPE report.inquiry_type AS ENUM ( 'inspectionpit', 'foundation_research', 'additional_research', - 'ground_water_level_research' + 'ground_water_level_research', + 'soil_investigation' ); diff --git a/src/FunderMaps.Core/Types/InquiryType.cs b/src/FunderMaps.Core/Types/InquiryType.cs index a2058ae9d..2ca88915c 100644 --- a/src/FunderMaps.Core/Types/InquiryType.cs +++ b/src/FunderMaps.Core/Types/InquiryType.cs @@ -66,8 +66,13 @@ public enum InquiryType FoundationResearch = 11, /// - /// Groundwaterlevel research + /// Groundwaterlevel research. /// GroundWaterLevelResearch = 12, + + /// + /// Soil investigation. + /// + SoilInvestigation = 13, } } From 64d12d7a2a48babed1ee40f71070b258f437298e Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 10:17:38 +0100 Subject: [PATCH 08/21] Set authorization on Inquiry calls --- .../Controllers/Report/InquiryController.cs | 16 +++++++----- .../Report/InquirySampleController.cs | 4 +++ .../Backend/Report/InquirySampleTests.cs | 13 +++++----- .../Backend/Report/InquiryTests.cs | 25 ++++++++++--------- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs b/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs index 24f4d5b8f..0c82fd618 100644 --- a/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs +++ b/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs @@ -9,6 +9,7 @@ using FunderMaps.Core.Notification; using FunderMaps.Core.Types; using FunderMaps.WebApi.DataTransferObjects; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; @@ -110,6 +111,7 @@ public async Task GetAllAsync([FromQuery] PaginationDto paginatio /// Create inquiry. /// [HttpPost] + [Authorize(Policy = "WriterAdministratorPolicy")] public async Task CreateAsync([FromBody] InquiryDto input) { // Map. @@ -135,6 +137,7 @@ public async Task CreateAsync([FromBody] InquiryDto input) /// [HttpPost("upload-document")] [RequestSizeLimit(128 * 1024 * 1024)] + [Authorize(Policy = "WriterAdministratorPolicy")] public async Task UploadDocumentAsync([Required][FormFile(Core.Constants.AllowedFileMimes)] IFormFile input) { // Act. @@ -162,8 +165,8 @@ await _blobStorageService.StoreFileAsync( public async Task GetDocumentAccessLinkAsync(int id) { // Act. - var inquiry = await _inquiryRepository.GetByIdAsync(id); - var link = await _blobStorageService.GetAccessLinkAsync( + InquiryFull inquiry = await _inquiryRepository.GetByIdAsync(id); + Uri link = await _blobStorageService.GetAccessLinkAsync( containerName: Core.Constants.InquiryStorageFolderName, fileName: inquiry.DocumentFile, hoursValid: 1); @@ -183,6 +186,7 @@ public async Task GetDocumentAccessLinkAsync(int id) /// Update inquiry by id. /// [HttpPut("{id:int}")] + [Authorize(Policy = "WriterAdministratorPolicy")] public async Task UpdateAsync(int id, [FromBody] InquiryDto input) { // Map. @@ -214,12 +218,12 @@ public async Task UpdateAsync(int id, [FromBody] InquiryDto input return NoContent(); } - // TODO: Check permissions. // POST: api/inquiry/{id}/status_review /// /// Set inquiry status to review by id. /// [HttpPost("{id:int}/status_review")] + [Authorize(Policy = "WriterAdministratorPolicy")] public async Task SetStatusReviewAsync(int id) { // Act. @@ -264,12 +268,12 @@ await _notifyService.NotifyAsync(new() return NoContent(); } - // TODO: Check permissions. // POST: api/inquiry/{id}/status_rejected /// /// Set inquiry status to rejected by id. /// [HttpPost("{id:int}/status_rejected")] + [Authorize(Policy = "VerifierAdministratorPolicy")] public async Task SetStatusRejectedAsync(int id, StatusChangeDto input) { // Act. @@ -315,12 +319,12 @@ await _notifyService.NotifyAsync(new() return NoContent(); } - // TODO: Check permissions. // POST: api/inquiry/{id}/status_approved /// /// Set inquiry status to done by id. /// [HttpPost("{id:int}/status_approved")] + [Authorize(Policy = "VerifierAdministratorPolicy")] public async Task SetStatusApprovedAsync(int id) { // Act. @@ -364,12 +368,12 @@ await _notifyService.NotifyAsync(new() return NoContent(); } - // TODO: Check permissions. // DELETE: api/inquiry/{id} /// /// Delete inquiry by id. /// [HttpDelete("{id:int}")] + [Authorize(Policy = "SuperuserAdministratorPolicy")] public async Task DeleteAsync(int id) { // Act. diff --git a/src/FunderMaps.WebApi/Controllers/Report/InquirySampleController.cs b/src/FunderMaps.WebApi/Controllers/Report/InquirySampleController.cs index f5e7bf314..5ff3f2340 100644 --- a/src/FunderMaps.WebApi/Controllers/Report/InquirySampleController.cs +++ b/src/FunderMaps.WebApi/Controllers/Report/InquirySampleController.cs @@ -5,6 +5,7 @@ using FunderMaps.Core.Interfaces; using FunderMaps.Core.Interfaces.Repositories; using FunderMaps.WebApi.DataTransferObjects; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; @@ -96,6 +97,7 @@ public async Task GetAllAsync(int inquiryId, [FromQuery] Paginati /// was successfully created within this . /// [HttpPost] + [Authorize(Policy = "WriterAdministratorPolicy")] public async Task CreateAsync(int inquiryId, [FromBody] InquirySampleDto input, [FromServices] IGeocoderTranslation geocoderTranslation) { Address address = await geocoderTranslation.GetAddressIdAsync(input.Address); @@ -134,6 +136,7 @@ public async Task CreateAsync(int inquiryId, [FromBody] InquirySa /// was successfully updated within this . /// [HttpPut("{id:int}")] + [Authorize(Policy = "WriterAdministratorPolicy")] public async Task UpdateAsync(int inquiryId, int id, [FromBody] InquirySampleDto input) { // Map. @@ -166,6 +169,7 @@ public async Task UpdateAsync(int inquiryId, int id, [FromBody] I /// within this are deleted. /// [HttpDelete("{id:int}")] + [Authorize(Policy = "WriterAdministratorPolicy")] public async Task DeleteAsync(int id) { // Act. diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs index c44cf7950..08b0f6caf 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs @@ -1,4 +1,5 @@ -using FunderMaps.IntegrationTests.Faker; +using FunderMaps.Core.Types; +using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using System; using System.Collections.Generic; @@ -31,7 +32,7 @@ public async Task CreateInquirySampleReturnInquirySample() var sample = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); // Act @@ -54,7 +55,7 @@ public async Task GetInquirySampleByIdReturnSingleInquirySample() var sample = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); @@ -79,7 +80,7 @@ public async Task GetAllInquirySampleReturnNavigationInquirySample() var sample = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); @@ -103,7 +104,7 @@ public async Task UpdateInquirySampleReturnNoContent() var samples = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(2); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); var sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", samples.First()); @@ -125,7 +126,7 @@ public async Task DeleteInquirySampleReturnNoContent() var sample = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Superuser); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs index a1e9ad1a2..6eee0f378 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs @@ -30,7 +30,7 @@ public async Task CreateInquiryReturnInquiry() .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); // Act var response = await client.PostAsJsonAsync("api/inquiry", inquiry); @@ -47,7 +47,7 @@ public async Task UploadDocumentReturnDocument() { // Arrange using var formContent = new FileUploadContent(mediaType: "application/pdf", fileExtension: "pdf"); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); // Act var response = await client.PostAsync("api/inquiry/upload-document", formContent); @@ -66,7 +66,7 @@ public async Task GetInquiryByIdReturnSingleInquiry() .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); // Act @@ -87,7 +87,7 @@ public async Task GetAllInquiryReturnNavigationInquiry() .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); // Act @@ -107,7 +107,7 @@ public async Task UpdateInquiryReturnNoContent() .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) .Generate(2); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); var inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiries.First()); // Act @@ -128,7 +128,7 @@ public async Task SetStatusReviewInquiryReturnNoContent() var sample = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); @@ -152,13 +152,14 @@ public async Task SetStatusRejectedAndApprovedInquiryReturnNoContent(string uri) var sample = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_review", new StatusChangeDtoFaker().Generate()); // Act - var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/{uri}", new StatusChangeDtoFaker().Generate()); + using var client2 = Factory.CreateClient(OrganizationRole.Verifier); + var response = await client2.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/{uri}", new StatusChangeDtoFaker().Generate()); // Assert Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); @@ -172,7 +173,7 @@ public async Task DeleteInquiryReturnNoContent() .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Superuser); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); // Act @@ -193,7 +194,7 @@ public async Task DeleteInquiryCascadeReturnNoContent() var sample = new InquirySampleDtoFaker() .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Superuser); inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); @@ -209,10 +210,10 @@ public async Task CreateInquirySameReviewerCreatorReturnForbidden() { // Arrange var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("1a93cfb3-f097-4697-a998-71cdd9cfaead")) + .RuleFor(f => f.Reviewer, f => Guid.Parse("aadc6b80-b447-443b-b4ed-fdfcb00976f2")) .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) .Generate(); - using var client = Factory.CreateClient(); + using var client = Factory.CreateClient(OrganizationRole.Writer); // Act var response = await client.PostAsJsonAsync("api/inquiry", inquiry); From 41d84654086fb5e22b0e111497ee165f12d5913b Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 11:15:51 +0100 Subject: [PATCH 09/21] Remove unused entity --- src/FunderMaps.Core/Entities/StateControl.cs | 170 ------------------- 1 file changed, 170 deletions(-) delete mode 100644 src/FunderMaps.Core/Entities/StateControl.cs diff --git a/src/FunderMaps.Core/Entities/StateControl.cs b/src/FunderMaps.Core/Entities/StateControl.cs deleted file mode 100644 index 3445cac55..000000000 --- a/src/FunderMaps.Core/Entities/StateControl.cs +++ /dev/null @@ -1,170 +0,0 @@ -using FunderMaps.Core.Exceptions; -using FunderMaps.Core.Types; -using System; - -namespace FunderMaps.Core.Entities -{ - // FUTURE: This needs to be rewritten. - /// - /// Entity state control. - /// - public abstract class StateControl : AttributionControl - where TEntity : class - where TEntryIdentifier : IEquatable, IComparable - { - /// - /// Create new instance. - /// - protected StateControl(Func entryPrimaryKey) - : base(entryPrimaryKey) - { - } - - /// - /// Enitity status. - /// - public AuditStatus AuditStatus { get; set; } = AuditStatus.Todo; - - /// - /// Is write allowed in entry state. - /// - /// True if allowed. - public bool AllowWrite => AuditStatus == AuditStatus.Todo || AuditStatus == AuditStatus.Pending; - - /// - /// Move to next state. - /// - /// - /// Not every state can transition from one state to - /// another without intervention. - /// - public void MoveNext() - { - switch (AuditStatus) - { - case AuditStatus.Todo: - AuditStatus = AuditStatus.Pending; - break; - case AuditStatus.Pending: - AuditStatus = AuditStatus.PendingReview; - break; - } - } - - /// - /// Move state to todo. - /// - /// - /// Can move to this state from: - /// - /// Pending - /// - /// - public void TransitionToTodo() - { - if (AuditStatus != AuditStatus.Pending) - { - throw new StateTransitionException(AuditStatus, AuditStatus.Todo); - } - AuditStatus = AuditStatus.Todo; - } - - /// - /// Move state to pending. - /// - /// - /// Can move to this state from: - /// - /// Todo - /// Pending - /// Rejected - /// - /// - public void TransitionToPending() - { - if (AuditStatus != AuditStatus.Todo - && AuditStatus != AuditStatus.Rejected - && AuditStatus != AuditStatus.Pending) - { - throw new StateTransitionException(AuditStatus, AuditStatus.Pending); - } - AuditStatus = AuditStatus.Pending; - } - - /// - /// Move state to review. - /// - /// - /// Can move to this state from: - /// - /// Pending - /// - /// - public void TransitionToReview() - { - if (AuditStatus != AuditStatus.Pending) - { - throw new StateTransitionException(AuditStatus, AuditStatus.PendingReview); - } - AuditStatus = AuditStatus.PendingReview; - } - - /// - /// Move state to done. - /// - /// - /// Can move to this state from: - /// - /// PendingReview - /// - /// - public void TransitionToDone() - { - if (AuditStatus != AuditStatus.PendingReview) - { - throw new StateTransitionException(AuditStatus, AuditStatus.Done); - } - AuditStatus = AuditStatus.Done; - } - - /// - /// Move state to rejected. - /// - /// - /// Can move to this state from: - /// - /// PendingReview - /// - /// - public void TransitionToRejected() - { - if (AuditStatus != AuditStatus.PendingReview) - { - throw new StateTransitionException(AuditStatus, AuditStatus.Rejected); - } - AuditStatus = AuditStatus.Rejected; - } - - /// - /// Move state to discarded. - /// - /// - /// Can move to this state from: - /// - /// Todo - /// Pending - /// PendingReview - /// Done - /// Rejected - /// - /// - public void TransitionToDiscarded() - { - if (AuditStatus == AuditStatus.Done) - { - throw new StateTransitionException(AuditStatus, AuditStatus.Discarded); - } - AuditStatus = AuditStatus.Discarded; - } - } -} From 6527d576c85497eace77fada4d361adc289a167a Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 11:17:31 +0100 Subject: [PATCH 10/21] Remove the discarded state transition --- src/FunderMaps.Core/Types/AuditStatus.cs | 1 + .../Types/Control/StateControl.cs | 25 ++----------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/FunderMaps.Core/Types/AuditStatus.cs b/src/FunderMaps.Core/Types/AuditStatus.cs index cf4b00e9c..b99a50775 100644 --- a/src/FunderMaps.Core/Types/AuditStatus.cs +++ b/src/FunderMaps.Core/Types/AuditStatus.cs @@ -20,6 +20,7 @@ public enum AuditStatus /// Done = 2, + // TODO: Remove. /// /// Discarded. /// diff --git a/src/FunderMaps.Core/Types/Control/StateControl.cs b/src/FunderMaps.Core/Types/Control/StateControl.cs index 0e22311db..3604ebaef 100644 --- a/src/FunderMaps.Core/Types/Control/StateControl.cs +++ b/src/FunderMaps.Core/Types/Control/StateControl.cs @@ -73,7 +73,8 @@ public void TransitionToPending() { if (AuditStatus != AuditStatus.Todo && AuditStatus != AuditStatus.Rejected - && AuditStatus != AuditStatus.Pending) + && AuditStatus != AuditStatus.Done + && AuditStatus != AuditStatus.PendingReview) { throw new StateTransitionException(AuditStatus, AuditStatus.Pending); } @@ -133,27 +134,5 @@ public void TransitionToRejected() } AuditStatus = AuditStatus.Rejected; } - - /// - /// Move state to discarded. - /// - /// - /// Can move to this state from: - /// - /// Todo - /// Pending - /// PendingReview - /// Done - /// Rejected - /// - /// - public void TransitionToDiscarded() - { - if (AuditStatus == AuditStatus.Done) - { - throw new StateTransitionException(AuditStatus, AuditStatus.Discarded); - } - AuditStatus = AuditStatus.Discarded; - } } } From be0a1c3cad26accf20ad418cf0724e58dcdb93dc Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 11:18:59 +0100 Subject: [PATCH 11/21] Allow reports to be reset by the superuser --- .../Controllers/Report/InquiryController.cs | 21 ++++++++++ .../Controllers/Report/RecoveryController.cs | 23 +++++++++- .../Backend/Report/RecoverySampleTests.cs | 42 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs b/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs index 0c82fd618..f5f7917d3 100644 --- a/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs +++ b/src/FunderMaps.WebApi/Controllers/Report/InquiryController.cs @@ -218,6 +218,27 @@ public async Task UpdateAsync(int id, [FromBody] InquiryDto input return NoContent(); } + // POST: api/inquiry/{id}/reset + /// + /// Reset inquiry status to pending by id. + /// + [HttpPost("{id:int}/reset")] + [Authorize(Policy = "SuperuserAdministratorPolicy")] + public async Task ResetAsync(int id) + { + // Act. + InquiryFull inquiry = await _inquiryRepository.GetByIdAsync(id); + + // Transition. + inquiry.State.TransitionToPending(); + + // Act. + await _inquiryRepository.SetAuditStatusAsync(inquiry.Id, inquiry); + + // Return. + return NoContent(); + } + // POST: api/inquiry/{id}/status_review /// /// Set inquiry status to review by id. diff --git a/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs b/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs index b342b2461..baecb6245 100644 --- a/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs +++ b/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using AutoMapper; using FunderMaps.AspNetCore.DataAnnotations; using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Entities; @@ -207,6 +207,27 @@ public async Task UpdateAsync(int id, [FromBody] RecoveryDto inpu return NoContent(); } + // POST: api/recovery/{id}/reset + /// + /// Reset recovery status to pending by id. + /// + [HttpPost("{id:int}/reset")] + [Authorize(Policy = "SuperuserAdministratorPolicy")] + public async Task ResetAsync(int id) + { + // Act. + Recovery recovery = await _recoveryRepository.GetByIdAsync(id); + + // Transition. + recovery.State.TransitionToPending(); + + // Act. + await _recoveryRepository.SetAuditStatusAsync(recovery.Id, recovery); + + // Return. + return NoContent(); + } + // POST: api/recovery/{id}/status_review /// /// Set recovery status to review by id. diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs index 101e28c11..f976b08bd 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs @@ -85,6 +85,37 @@ public async Task RecoverySampleLifeCycle() await ReportStub.DeleteRecoveryAsync(Factory, recovery); } + [Fact] + public async Task RecoverySampleResetLifeCycle() + { + var recovery = await ReportStub.CreateRecoveryAsync(Factory); + var sample = await ReportStub.CreateRecoverySampleAsync(Factory, recovery); + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + + // Act + var response = await client.PostAsJsonAsync($"api/recovery/{recovery.Id}/status_review", new StatusChangeDtoFaker().Generate()); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Superuser); + + // Act + var response = await client.PostAsJsonAsync($"api/recovery/{recovery.Id}/reset", new { }); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + await ReportStub.DeleteRecoveryAsync(Factory, recovery); + } + [Theory] [InlineData("status_approved")] [InlineData("status_rejected")] @@ -115,6 +146,17 @@ public async Task RecoverySampleStatusLifeCycle(string uri) Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); } + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Superuser); + + // Act + var response = await client.PostAsJsonAsync($"api/recovery/{recovery.Id}/reset", new { }); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + await ReportStub.DeleteRecoveryAsync(Factory, recovery); } From dafc61929c394e2e7b54ada226057094e2b8282c Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 11:19:10 +0100 Subject: [PATCH 12/21] Change store --- .../Controllers/Report/RecoveryController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs b/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs index baecb6245..06817062e 100644 --- a/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs +++ b/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using AutoMapper; using FunderMaps.AspNetCore.DataAnnotations; using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Entities; @@ -162,7 +162,7 @@ public async Task GetDocumentAccessLinkAsync(int id) // Act. Recovery recovery = await _recoveryRepository.GetByIdAsync(id); Uri link = await _blobStorageService.GetAccessLinkAsync( - containerName: Core.Constants.InquiryStorageFolderName, + containerName: Core.Constants.RecoveryStorageFolderName, fileName: recovery.DocumentFile, hoursValid: 1); From 67c3ceaf46f5caa175fe4020802564035627b1c8 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 11:32:19 +0100 Subject: [PATCH 13/21] Forbid self reviewing --- .../Controllers/Report/RecoveryController.cs | 11 ++++++ .../Backend/Report/RecoveryTests.cs | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs b/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs index 06817062e..37aa4ef97 100644 --- a/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs +++ b/src/FunderMaps.WebApi/Controllers/Report/RecoveryController.cs @@ -2,6 +2,7 @@ using FunderMaps.AspNetCore.DataAnnotations; using FunderMaps.AspNetCore.DataTransferObjects; using FunderMaps.Core.Entities; +using FunderMaps.Core.Exceptions; using FunderMaps.Core.Helpers; using FunderMaps.Core.Interfaces; using FunderMaps.Core.Interfaces.Repositories; @@ -115,6 +116,10 @@ public async Task CreateAsync([FromBody] RecoveryDto input) { // Map. var recovery = _mapper.Map(input); + if (_appContext.UserId == input.Reviewer) + { + throw new AuthorizationException(); + } // Act. recovery = await _recoveryRepository.AddGetAsync(recovery); @@ -188,6 +193,12 @@ public async Task UpdateAsync(int id, [FromBody] RecoveryDto inpu var recovery = _mapper.Map(input); recovery.Id = id; + Recovery recovery_existing = await _recoveryRepository.GetByIdAsync(id); + if (recovery_existing.Attribution.Creator == input.Reviewer) + { + throw new AuthorizationException(); + } + // Act. await _recoveryRepository.UpdateAsync(recovery); diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs index c18a5c096..4100b88e9 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoveryTests.cs @@ -199,5 +199,43 @@ public async Task RecoveryLifeCycleForbidden(OrganizationRole role) await ReportStub.DeleteRecoveryAsync(Factory, recovery); } + + [Fact] + public async Task RecoverySelfReviewForbidden() + { + var recovery = await ReportStub.CreateRecoveryAsync(Factory); + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + var newObject = new RecoveryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("aadc6b80-b447-443b-b4ed-fdfcb00976f2")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + + // Act + var response = await client.PostAsJsonAsync("api/recovery", newObject); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(); + var newObject = new RecoveryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("aadc6b80-b447-443b-b4ed-fdfcb00976f2")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + + // Act + var response = await client.PutAsJsonAsync($"api/recovery/{recovery.Id}", newObject); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + await ReportStub.DeleteRecoveryAsync(Factory, recovery); + } } } From 0f90a8f12fad4e224c5dbe575b1cb4fbaee4c13e Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 11:48:23 +0100 Subject: [PATCH 14/21] Allow pending from any state --- src/FunderMaps.Core/Types/Control/StateControl.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/FunderMaps.Core/Types/Control/StateControl.cs b/src/FunderMaps.Core/Types/Control/StateControl.cs index 3604ebaef..75f9cdb52 100644 --- a/src/FunderMaps.Core/Types/Control/StateControl.cs +++ b/src/FunderMaps.Core/Types/Control/StateControl.cs @@ -61,23 +61,8 @@ public void TransitionToTodo() /// /// Move state to pending. /// - /// - /// Can move to this state from: - /// - /// Todo - /// Pending - /// Rejected - /// - /// public void TransitionToPending() { - if (AuditStatus != AuditStatus.Todo - && AuditStatus != AuditStatus.Rejected - && AuditStatus != AuditStatus.Done - && AuditStatus != AuditStatus.PendingReview) - { - throw new StateTransitionException(AuditStatus, AuditStatus.Pending); - } AuditStatus = AuditStatus.Pending; } From 7563eefb28f1da378b8be7cd2c8dfad0735ae446 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 12:09:43 +0100 Subject: [PATCH 15/21] Refactor inquiry to lifecycle tests --- .../Backend/Report/IncidentTests.cs | 26 +- .../Backend/Report/InquiryTests.cs | 318 +++++++++--------- .../Backend/Report/ReportStub.cs | 33 ++ 3 files changed, 213 insertions(+), 164 deletions(-) diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs index d08251f18..dc286597f 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/IncidentTests.cs @@ -20,38 +20,38 @@ public IncidentTests(BackendFixtureFactory factory) => Factory = factory; [Fact] - public async Task CreateIncidentReturnIncident() + public async Task UploadDocumentReturnDocument() { // Arrange - var incident = new IncidentDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); + using var formContent = new FileUploadContent(mediaType: "application/pdf", fileExtension: "pdf"); using var client = Factory.CreateClient(); // Act - var response = await client.PostAsJsonAsync("api/incident", incident); - var returnObject = await response.Content.ReadFromJsonAsync(); + var response = await client.PostAsync("api/incident/upload-document", formContent); + var returnObject = await response.Content.ReadFromJsonAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("FIR", returnObject.Id, StringComparison.InvariantCulture); - Assert.Equal(AuditStatus.Todo, returnObject.AuditStatus); + Assert.NotNull(returnObject.Name); } [Fact] - public async Task UploadDocumentReturnDocument() + public async Task CreateIncidentReturnIncident() { // Arrange - using var formContent = new FileUploadContent(mediaType: "application/pdf", fileExtension: "pdf"); + var incident = new IncidentDtoFaker() + .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") + .Generate(); using var client = Factory.CreateClient(); // Act - var response = await client.PostAsync("api/incident/upload-document", formContent); - var returnObject = await response.Content.ReadFromJsonAsync(); + var response = await client.PostAsJsonAsync("api/incident", incident); + var returnObject = await response.Content.ReadFromJsonAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.NotNull(returnObject.Name); + Assert.StartsWith("FIR", returnObject.Id, StringComparison.InvariantCulture); + Assert.Equal(AuditStatus.Todo, returnObject.AuditStatus); } [Fact] diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs index 6eee0f378..8e7c2a9f2 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/InquiryTests.cs @@ -4,7 +4,6 @@ using FunderMaps.WebApi.DataTransferObjects; using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http.Json; using System.Threading.Tasks; @@ -22,26 +21,6 @@ public class InquiryTests : IClassFixture public InquiryTests(BackendFixtureFactory factory) => Factory = factory; - [Fact] - public async Task CreateInquiryReturnInquiry() - { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - - // Act - var response = await client.PostAsJsonAsync("api/inquiry", inquiry); - var returnObject = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(AuditStatus.Todo, returnObject.AuditStatus); - Assert.Null(returnObject.UpdateDate); - } - [Fact] public async Task UploadDocumentReturnDocument() { @@ -59,167 +38,204 @@ public async Task UploadDocumentReturnDocument() } [Fact] - public async Task GetInquiryByIdReturnSingleInquiry() + public async Task UploadDocumentReturnForbidden() { // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); + using var formContent = new FileUploadContent(mediaType: "application/pdf", fileExtension: "pdf"); + using var client = Factory.CreateClient(); // Act - var response = await client.GetAsync($"api/inquiry/{inquiry.Id}"); - var returnObject = await response.Content.ReadFromJsonAsync(); + var response = await client.PostAsync("api/inquiry/upload-document", formContent); // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(AuditStatus.Todo, returnObject.AuditStatus); - Assert.Null(returnObject.UpdateDate); + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact] - public async Task GetAllInquiryReturnNavigationInquiry() + public async Task InquiryLifeCycle() { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - - // Act - var response = await client.GetAsync($"api/inquiry?limit=10"); - var returnList = await response.Content.ReadFromJsonAsync>(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.True(returnList.Count >= 1); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.GetAsync($"api/inquiry/stats"); + var returnObject = await response.Content.ReadFromJsonAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.True(returnObject.Count >= 1); + } + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.GetAsync($"api/inquiry/{inquiry.Id}"); + var returnObject = await response.Content.ReadFromJsonAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(AuditStatus.Todo, returnObject.AuditStatus); + Assert.Null(returnObject.UpdateDate); + } + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.GetAsync($"api/inquiry"); + var returnList = await response.Content.ReadFromJsonAsync>(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.True(returnList.Count >= 1); + } + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + var newObject = new InquiryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + + // Act + var response = await client.PutAsJsonAsync($"api/inquiry/{inquiry.Id}", newObject); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } - [Fact] - public async Task UpdateInquiryReturnNoContent() + [Theory] + [InlineData(OrganizationRole.Reader)] + [InlineData(OrganizationRole.Writer)] + [InlineData(OrganizationRole.Verifier)] + [InlineData(OrganizationRole.Superuser)] + public async Task InquiryLifeCycleForbidden(OrganizationRole role) { - // Arrange - var inquiries = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(2); - using var client = Factory.CreateClient(OrganizationRole.Writer); - var inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiries.First()); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); - // Act - var response = await client.PutAsJsonAsync($"api/inquiry/{inquiry.Id}", inquiries.Last()); + { + // Arrange + using var client = Factory.CreateClient(); + var newObject = new InquiryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); - // Assert - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); - } + // Act + var response = await client.PostAsJsonAsync("api/inquiry", newObject); - [Fact] - public async Task SetStatusReviewInquiryReturnNoContent() - { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var sample = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } - // Act - var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_review", new StatusChangeDtoFaker().Generate()); + { + // Arrange + using var client = Factory.CreateClient(); + var newObject = new InquiryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); - // Assert - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); - } + // Act + var response = await client.PutAsJsonAsync($"api/inquiry/{inquiry.Id}", newObject); - [Theory] - [InlineData("status_rejected")] - [InlineData("status_approved")] - public async Task SetStatusRejectedAndApprovedInquiryReturnNoContent(string uri) - { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var sample = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); - await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_review", new StatusChangeDtoFaker().Generate()); + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } - // Act - using var client2 = Factory.CreateClient(OrganizationRole.Verifier); - var response = await client2.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/{uri}", new StatusChangeDtoFaker().Generate()); + { + // Arrange + using var client = Factory.CreateClient(role); - // Assert - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); - } + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_review", new StatusChangeDtoFaker().Generate()); - [Fact] - public async Task DeleteInquiryReturnNoContent() - { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Superuser); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } - // Act - var response = await client.DeleteAsync($"api/inquiry/{inquiry.Id}"); + { + // Arrange + using var client = Factory.CreateClient(role); - // Assert - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); - } + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_approved", new StatusChangeDtoFaker().Generate()); - [Fact] - public async Task DeleteInquiryCascadeReturnNoContent() - { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var sample = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Superuser); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } - // Act - var response = await client.DeleteAsync($"api/inquiry/{inquiry.Id}"); + { + // Arrange + using var client = Factory.CreateClient(role); - // Assert - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_rejected", new StatusChangeDtoFaker().Generate()); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.DeleteAsync($"api/inquiry/{inquiry.Id}"); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } [Fact] - public async Task CreateInquirySameReviewerCreatorReturnForbidden() + public async Task InquirySelfReviewForbidden() { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("aadc6b80-b447-443b-b4ed-fdfcb00976f2")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - - // Act - var response = await client.PostAsJsonAsync("api/inquiry", inquiry); - - // Assert - Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + var newObject = new RecoveryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("aadc6b80-b447-443b-b4ed-fdfcb00976f2")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + + // Act + var response = await client.PostAsJsonAsync("api/inquiry", newObject); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(); + var newObject = new InquiryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("aadc6b80-b447-443b-b4ed-fdfcb00976f2")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + + // Act + var response = await client.PutAsJsonAsync($"api/inquiry/{inquiry.Id}", newObject); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } } } diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs index 7224ecccf..ea38808d8 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs @@ -35,6 +35,27 @@ public static async Task CreateRecoveryAsync(BackendFixtureFactory return returnObject; } + public static async Task CreateInquiryAsync(BackendFixtureFactory factory) + { + // Arrange + var inquiry = new InquiryDtoFaker() + .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + using var client = factory.CreateClient(OrganizationRole.Writer); + + // Act + var response = await client.PostAsJsonAsync("api/inquiry", inquiry); + var returnObject = await response.Content.ReadFromJsonAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(AuditStatus.Todo, returnObject.AuditStatus); + Assert.Null(returnObject.UpdateDate); + + return returnObject; + } + public static async Task DeleteRecoveryAsync(BackendFixtureFactory factory, RecoveryDto recovery) { // Arrange @@ -47,6 +68,18 @@ public static async Task DeleteRecoveryAsync(BackendFixtureFactory factory, Reco Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); } + public static async Task DeleteInquiryAsync(BackendFixtureFactory factory, InquiryDto inquiry) + { + // Arrange + using var client = factory.CreateClient(OrganizationRole.Superuser); + + // Act + var response = await client.DeleteAsync($"api/inquiry/{inquiry.Id}"); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + public static async Task CreateRecoverySampleAsync(BackendFixtureFactory factory, RecoveryDto recovery) { // Arrange From 8ba4ba2bf1bfd77ce9ca21138b03eee51932c001 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 13:13:04 +0100 Subject: [PATCH 16/21] Refactor inquiry to lifecycle tests --- .../Backend/Report/InquirySampleTests.cs | 290 ++++++++++++------ .../Backend/Report/RecoverySampleTests.cs | 2 +- .../Backend/Report/ReportStub.cs | 20 ++ 3 files changed, 212 insertions(+), 100 deletions(-) diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs index 08b0f6caf..18e5538d4 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs @@ -1,9 +1,9 @@ -using FunderMaps.Core.Types; +using FunderMaps.AspNetCore.DataTransferObjects; +using FunderMaps.Core.Types; using FunderMaps.IntegrationTests.Faker; using FunderMaps.WebApi.DataTransferObjects; using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http.Json; using System.Threading.Tasks; @@ -22,119 +22,211 @@ public InquirySampleTests(BackendFixtureFactory factory) => Factory = factory; [Fact] - public async Task CreateInquirySampleReturnInquirySample() + public async Task InquirySampleLifeCycle() { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var sample = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - - // Act - var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); - var returnObject = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(inquiry.Id, returnObject.Inquiry); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); + var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.GetAsync($"api/inquiry/{inquiry.Id}/sample/stats"); + var returnObject = await response.Content.ReadFromJsonAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.True(returnObject.Count >= 1); + } + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.GetAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}"); + var returnObject = await response.Content.ReadFromJsonAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(sample.Id, returnObject.Id); + Assert.Equal(inquiry.Id, returnObject.Inquiry); + } + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.GetAsync($"api/inquiry/{inquiry.Id}/sample"); + var returnList = await response.Content.ReadFromJsonAsync>(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.True(returnList.Count >= 1); + } + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + var newObject = new InquirySampleDtoFaker() + .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") + .Generate(); + + // Act + var response = await client.PutAsJsonAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}", newObject); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } [Fact] - public async Task GetInquirySampleByIdReturnSingleInquirySample() + public async Task IinquirySampleResetLifeCycle() { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var sample = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); - - // Act - var response = await client.GetAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}"); - var returnObject = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(sample.Id, returnObject.Id); - Assert.Equal(inquiry.Id, returnObject.Inquiry); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); + var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_review", new StatusChangeDtoFaker().Generate()); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Superuser); + + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/reset", new { }); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } - [Fact] - public async Task GetAllInquirySampleReturnNavigationInquirySample() + [Theory] + [InlineData("status_approved")] + [InlineData("status_rejected")] + public async Task IinquirySampleStatusLifeCycle(string uri) { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var sample = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); - - // Act - var response = await client.GetAsync($"api/inquiry/{inquiry.Id}/sample?limit=10"); - var returnList = await response.Content.ReadFromJsonAsync>(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.True(returnList.Count >= 1); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); + var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/status_review", new StatusChangeDtoFaker().Generate()); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Verifier); + + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/{uri}", new StatusChangeDtoFaker().Generate()); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Superuser); + + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/reset", new { }); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } [Fact] - public async Task UpdateInquirySampleReturnNoContent() + public async Task IinquirySampleDeleteLifeCycle() { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var samples = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(2); - using var client = Factory.CreateClient(OrganizationRole.Writer); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - var sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", samples.First()); - - // Act - var response = await client.PutAsJsonAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}", samples.Last()); - - // Assert - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); + var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); + + { + // Arrange + using var client = Factory.CreateClient(OrganizationRole.Writer); + + // Act + var response = await client.DeleteAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}"); + + // Assert + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } [Fact] - public async Task DeleteInquirySampleReturnNoContent() + public async Task InquirySampleLifeCycleForbidden() { - // Arrange - var inquiry = new InquiryDtoFaker() - .RuleFor(f => f.Reviewer, f => Guid.Parse("21c403fe-45fc-4106-9551-3aada1bbdec3")) - .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) - .Generate(); - var sample = new InquirySampleDtoFaker() - .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") - .Generate(); - using var client = Factory.CreateClient(OrganizationRole.Superuser); - inquiry = await client.PostAsJsonGetFromJsonAsync("api/inquiry", inquiry); - sample = await client.PostAsJsonGetFromJsonAsync($"api/inquiry/{inquiry.Id}/sample", sample); - - // Act - var response = await client.DeleteAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}"); - - // Assert - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + var inquiry = await ReportStub.CreateInquiryAsync(Factory); + var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); + + { + // Arrange + using var client = Factory.CreateClient(); + var newObject = new RecoverySampleDtoFaker() + .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/sample", newObject); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(); + var newObject = new RecoverySampleDtoFaker() + .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") + .RuleFor(f => f.Contractor, f => Guid.Parse("62af863e-2021-4438-a5ea-730ed3db9eda")) + .Generate(); + + // Act + var response = await client.PutAsJsonAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}", newObject); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + { + // Arrange + using var client = Factory.CreateClient(); + + // Act + var response = await client.DeleteAsync($"api/inquiry/{inquiry.Id}/sample/{sample.Id}"); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + await ReportStub.DeleteInquiryAsync(Factory, inquiry); } } } diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs index f976b08bd..95345a1cc 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/RecoverySampleTests.cs @@ -181,7 +181,7 @@ public async Task RecoverySampleDeleteLifeCycle() } [Fact] - public async Task RecoveryLifeCycleForbidden() + public async Task RecoverySampleLifeCycleForbidden() { var recovery = await ReportStub.CreateRecoveryAsync(Factory); var sample = await ReportStub.CreateRecoverySampleAsync(Factory, recovery); diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs index ea38808d8..4e07db37b 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/ReportStub.cs @@ -95,6 +95,26 @@ public static async Task CreateRecoverySampleAsync(BackendFix // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(recovery.Id, returnObject.Recovery); + Assert.Equal(newObject.Address, returnObject.Address); + + return returnObject; + } + + public static async Task CreateInquirySampleAsync(BackendFixtureFactory factory, InquiryDto inquiry) + { + using var client = factory.CreateClient(OrganizationRole.Writer); + var newObject = new InquirySampleDtoFaker() + .RuleFor(f => f.Address, f => "gfm-351cc5645ab7457b92d3629e8c163f0b") + .Generate(); + + // Act + var response = await client.PostAsJsonAsync($"api/inquiry/{inquiry.Id}/sample", newObject); + var returnObject = await response.Content.ReadFromJsonAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(inquiry.Id, returnObject.Inquiry); Assert.Equal(newObject.Address, returnObject.Address); return returnObject; From b63cf22135691af51d98174467fb6b0efbe77a9e Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 14:19:59 +0100 Subject: [PATCH 17/21] Ignore test when value is null --- .../Services/ProductService.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/FunderMaps.Core/Services/ProductService.cs b/src/FunderMaps.Core/Services/ProductService.cs index a243e9aa2..9a9720c25 100644 --- a/src/FunderMaps.Core/Services/ProductService.cs +++ b/src/FunderMaps.Core/Services/ProductService.cs @@ -62,15 +62,19 @@ private async Task GetStatisticsByExternalIdAsync(string id) private string DescriptionDrystand(FoundationRisk? risk, double? drystand) => risk switch { - var risk_low when + var risk_low when ( risk_low == FoundationRisk.A || - risk_low == FoundationRisk.B => $"Er komt doorgaans geen droogstand voor. Er is een veilige marge van {drystand}m dat het grondwater hoger staat dan het hoogstgelegen funderingshout. Hierdoor kan het funderingshout niet rotten door een tekort aan zuurstof.", + risk_low == FoundationRisk.B) && + drystand is not null => $"Er komt doorgaans geen droogstand voor. Er is een veilige marge van {drystand}m dat het grondwater hoger staat dan het hoogstgelegen funderingshout. Hierdoor kan het funderingshout niet rotten door een tekort aan zuurstof.", - var risk_medium when + var risk_medium when ( risk_medium == FoundationRisk.C || - risk_medium == FoundationRisk.D => $"Er kan een droogstand van ca. {drystand}m van het hoogstgelegen funderingshout voorkomen maar deze bevindt zich doorgaans in een acceptabele marge. Bij wijzigende omstandigheden kan de situatie verergeren waardoor het funderingshout regelmatig droog kan komen te staan en het hout kan gaan rotten. Na jarenlange droogstand kan het draagvermogen van de fundering zijn aangetast waardoor het pand kan gaan deformeren. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.​", + risk_medium == FoundationRisk.D) && + drystand is not null => $"Er kan een droogstand van ca. {drystand}m van het hoogstgelegen funderingshout voorkomen maar deze bevindt zich doorgaans in een acceptabele marge. Bij wijzigende omstandigheden kan de situatie verergeren waardoor het funderingshout regelmatig droog kan komen te staan en het hout kan gaan rotten. Na jarenlange droogstand kan het draagvermogen van de fundering zijn aangetast waardoor het pand kan gaan deformeren. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.​", - FoundationRisk.E => $"Er kan een droogstand van ca. {drystand}m van het hoogstgelegen funderingshout voorkomen. Hierdoor kan het funderingshout regelmatig droog komen te staan waardoor het hout kan gaan rotten. Na jarenlange droogstand kan het draagvermogen van de fundering zijn aangetast waardoor het pand kan gaan deformeren. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.", + var risk_low when ( + risk_low == FoundationRisk.E) && + drystand is not null => $"Er kan een droogstand van ca. {drystand}m van het hoogstgelegen funderingshout voorkomen. Hierdoor kan het funderingshout regelmatig droog komen te staan waardoor het hout kan gaan rotten. Na jarenlange droogstand kan het draagvermogen van de fundering zijn aangetast waardoor het pand kan gaan deformeren. Indien dit in vergevorderd stadium is, zijn er scheuren in de gevel zichtbaar, klemmen ramen en deuren en vertoont het gevelaanzicht tekenen van verzakkingen.", _ => "Onbekend", }; @@ -78,15 +82,19 @@ var risk_medium when private string DescriptionDewateringDepth(FoundationRisk? risk, double? dewateringDepth) => risk switch { - var risk_low when + var risk_low when ( risk_low == FoundationRisk.A || - risk_low == FoundationRisk.B => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] laag.​", + risk_low == FoundationRisk.B) && + dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] laag.​", - var risk_medium when + var risk_medium when ( risk_medium == FoundationRisk.C || - risk_medium == FoundationRisk.D => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] gemiddeld.​", + risk_medium == FoundationRisk.D) && + dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] gemiddeld.​", - FoundationRisk.E => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] verhoogd.", + var risk_low when ( + risk_low == FoundationRisk.E) && + dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] verhoogd.", _ => "Onbekend", }; From e0ab98522b13cbf7c4605b54006821d594e126f3 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 16:06:47 +0100 Subject: [PATCH 18/21] Small changes --- database/data/seed_maplayer.sql | 2 +- .../FunderMapsInfrastructureServiceCollectionExtensions.cs | 4 ++-- .../Backend/Report/InquirySampleTests.cs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/database/data/seed_maplayer.sql b/database/data/seed_maplayer.sql index 7d7c0450b..7fe44f4e6 100644 --- a/database/data/seed_maplayer.sql +++ b/database/data/seed_maplayer.sql @@ -43,9 +43,9 @@ c7ccf095-cf77-4f41-813a-68b493f7c6cb maplayer building_built_year Bouwjaar {"typ 806d560e-2931-46d9-a06b-644491a335e8 maplayer building_ownership Eigendom {"type": "color", "column": "meldingen123kleurtest", "values": {"color": "#d11313"}} edd9bf0f-903b-43e6-be9c-2c927089b075 maplayer incident_aggregate Incidenten Gemeente Aantallen {"type": "range_num", "column": "incidents", "values": [{"max": "0", "min": "0", "color": "#293575", "label": "0"}, {"max": "1", "min": "0", "color": "#293575", "label": "1"}, {"max": "2", "min": "1", "color": "#1261A3", "label": "2"}, {"max": "3", "min": "2", "color": "#69A8DE", "label": "3"}, {"max": "5", "min": "3", "color": "#99C1E9", "label": "4-5"}, {"max": "10", "min": "5", "color": "#B378B1", "label": "6-10"}, {"max": "15", "min": "10", "color": "#bd6495", "label": "16-20"}, {"max": "25", "min": "20", "color": "#ba2351", "label": "21-25"}, {"max": "100", "min": "25", "color": "#d11313", "label": "> 25"}]} e0519b1a-ae1a-43ad-a2b9-bdbbeb0c5f86 maplayer incident_aggregate_category Incidenten Gemeente Category {"type": "case", "column": "category", "values": [{"color": "#d8e7f5", "label": "Geen of nauwelijks", "match": "0"}, {"color": "#73b3d8", "label": "Enkele", "match": "1"}, {"color": "#1563aa", "label": "Meerdere", "match": "2"}]} -9dffd130-4019-4440-b3d5-8812a961a87a maplayer recovery_sample_type Hesteld {"type": "case_multimatch", "column": "type", "values": [{"color": "#5cbe55", "label": "Volledig herstel", "match": ["table"]}, {"color": "#47baa5", "label": "Partieel herstel", "match": ["pile_in_wall"]}, {"color": "#8c4bb6", "label": "Paalkop verlaging", "match": ["pile_lowering", "beam_on_pile"]}, {"color": "#c67e70", "label": "Grondverbetering", "match": ["injection"]}, {"color": "#6a6c70", "label": "Onbekend", "match": ["unknown"]}]} dfe1a361-c23f-4a1d-ba49-af8e974270b3 maplayer subsidence_hex Maaiveldzakking {"type": "range_num", "column": "velocity", "values": [{"max": "9999", "min": "0", "color": "#f7fbff", "label": "> 0 mm/jaar"}, {"max": "0", "min": "-0.5", "color": "#d8e7f5", "label": "0,0 t/m -0,5 mm/jaar"}, {"max": "-0.5", "min": "-1", "color": "#b0d2e8", "label": "-0,5 t/m -1,0 mm/jaar"}, {"max": "-1", "min": "-1.5", "color": "#73b3d8", "label": "-1,0 t/m -1,5 mm/jaar"}, {"max": "-1.5", "min": "-2", "color": "#3e8ec4", "label": "-1,5 t/m -2,0 mm/jaar"}, {"max": "-2", "min": "-2.5", "color": "#1563aa", "label": "-2,0 t/m -2,5 mm/jaar"}, {"max": "-2.5", "min": "-9999", "color": "#08306b", "label": "< -2,5 mm/jaar"}]} f42a6826-c3a0-48b1-8c96-6c9ef753ed46 maplayer inquiry_sample_foundation_type Funderingstype vastgesteld {"type": "case_multimatch", "column": "foundation_type", "values": [{"color": "#c75d43", "label": "Houten paal", "match": ["wood", "weighted_pile", "wood_amsterdam", "wood_rotterdam"]}, {"color": "#deb271", "label": "Houten paal met oplanger", "match": ["wood_charger"]}, {"color": "#6a6c70", "label": "Betonnen paal", "match": ["concrete"]}, {"color": "#ff3333", "label": "Op staal", "match": ["no_pile", "no_pile_masonry", "no_pile_strips", "no_pile_concrete_floor", "no_pile_slit", "no_pile_bearing_floor"]}, {"color": "#bdbebf", "label": "Stalen paal", "match": ["steel_pile"]}, {"color": "#7192de", "label": "Verzwaarde betonpuntpaal", "match": ["weighted_pile"]}, {"color": "#b271de", "label": "Combinatie", "match": ["combined"]}, {"color": "#ffec33", "label": "Overig", "match": ["other"]}, {"color": "#71decc", "label": "Onbekend", "match": ["unknown"]}]} +9dffd130-4019-4440-b3d5-8812a961a87a maplayer recovery_sample_type Hesteld {"type": "case_multimatch", "column": "type", "values": [{"color": "#5cbe55", "label": "Volledig herstel", "match": ["table"]}, {"color": "#47baa5", "label": "Partieel herstel", "match": ["pile_in_wall"]}, {"color": "#8c4bb6", "label": "Paalkop verlaging", "match": ["pile_lowering", "beam_on_pile"]}, {"color": "#c67e70", "label": "Grondverbetering", "match": ["injection"]}, {"color": "#5B4AB7", "label": "Onbekend", "match": ["unknown"]}]} \. diff --git a/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs b/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs index e65a0cdfd..7d13a7711 100644 --- a/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs +++ b/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs @@ -33,12 +33,12 @@ public static class FunderMapsInfrastructureServiceCollectionExtensions private static void ConfigureExternalServices(IServiceCollection services) { // Remove all existing email services and inject local email service. - services.AddOrReplace(ServiceLifetime.Singleton); services.Configure(Configuration.GetSection(SmtpOptions.Section)); + services.AddOrReplace(ServiceLifetime.Singleton); // Remove all existing file storage services and inject local file stoage service. - services.AddOrReplace(ServiceLifetime.Singleton); services.Configure(Configuration.GetSection("BlobStorage")); + services.AddOrReplace(ServiceLifetime.Singleton); } /// diff --git a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs index 18e5538d4..ee3cc9f44 100644 --- a/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs +++ b/tests/FunderMaps.IntegrationTests/Backend/Report/InquirySampleTests.cs @@ -85,7 +85,7 @@ public async Task InquirySampleLifeCycle() } [Fact] - public async Task IinquirySampleResetLifeCycle() + public async Task InquirySampleResetLifeCycle() { var inquiry = await ReportStub.CreateInquiryAsync(Factory); var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); @@ -118,7 +118,7 @@ public async Task IinquirySampleResetLifeCycle() [Theory] [InlineData("status_approved")] [InlineData("status_rejected")] - public async Task IinquirySampleStatusLifeCycle(string uri) + public async Task InquirySampleStatusLifeCycle(string uri) { var inquiry = await ReportStub.CreateInquiryAsync(Factory); var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); @@ -160,7 +160,7 @@ public async Task IinquirySampleStatusLifeCycle(string uri) } [Fact] - public async Task IinquirySampleDeleteLifeCycle() + public async Task InquirySampleDeleteLifeCycle() { var inquiry = await ReportStub.CreateInquiryAsync(Factory); var sample = await ReportStub.CreateInquirySampleAsync(Factory, inquiry); From 9eb4cfba1c847235b4defc32cad242394aa6b822 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Wed, 24 Mar 2021 16:17:30 +0100 Subject: [PATCH 19/21] Add soil to description --- src/FunderMaps.Core/Services/ProductService.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FunderMaps.Core/Services/ProductService.cs b/src/FunderMaps.Core/Services/ProductService.cs index 9a9720c25..f02042522 100644 --- a/src/FunderMaps.Core/Services/ProductService.cs +++ b/src/FunderMaps.Core/Services/ProductService.cs @@ -79,22 +79,22 @@ var risk_low when ( _ => "Onbekend", }; - private string DescriptionDewateringDepth(FoundationRisk? risk, double? dewateringDepth) + private string DescriptionDewateringDepth(FoundationRisk? risk, double? dewateringDepth, string soil) => risk switch { var risk_low when ( risk_low == FoundationRisk.A || risk_low == FoundationRisk.B) && - dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] laag.​", + dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit {soil} laag.​", var risk_medium when ( risk_medium == FoundationRisk.C || risk_medium == FoundationRisk.D) && - dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] gemiddeld.​", + dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit {soil} gemiddeld.​", var risk_low when ( risk_low == FoundationRisk.E) && - dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit [[soil]] verhoogd.", + dewateringDepth is not null => $"De gemiddelde grondwaterlevelfluctuatie is {dewateringDepth}m. Bij deze waarden is de kans op verzakkingen van een fundering op staal bij de ondergrond die bestaat uit {soil} verhoogd.", _ => "Onbekend", }; @@ -133,7 +133,7 @@ public virtual async IAsyncEnumerable GetAnalysisAsync(Analysis yield return product with { DescriptionDrystand = DescriptionDrystand(product.DrystandRisk, product.Drystand), - DescriptionDewateringDepth = DescriptionDewateringDepth(product.DewateringDepthRisk, product.DewateringDepth), + DescriptionDewateringDepth = DescriptionDewateringDepth(product.DewateringDepthRisk, product.DewateringDepth, product.Soil), DescriptionBioInfection = DescriptionBioInfection(product.BioInfectionRisk), Statistics = productType switch From 1c2e3a788dd5a41c102336f30d7760e4cb22f3af Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Thu, 25 Mar 2021 08:52:21 +0100 Subject: [PATCH 20/21] Register as transient --- .../Extensions/FunderMapsCoreServiceCollectionExtensions.cs | 2 +- .../FunderMapsInfrastructureServiceCollectionExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FunderMaps.Core/Extensions/FunderMapsCoreServiceCollectionExtensions.cs b/src/FunderMaps.Core/Extensions/FunderMapsCoreServiceCollectionExtensions.cs index 41789cc6a..e07ec9582 100644 --- a/src/FunderMaps.Core/Extensions/FunderMapsCoreServiceCollectionExtensions.cs +++ b/src/FunderMaps.Core/Extensions/FunderMapsCoreServiceCollectionExtensions.cs @@ -136,7 +136,7 @@ public static IServiceCollection AddFunderMapsCoreServices(this IServiceCollecti // them as a singleton will keep the services alife for the entire lifetime // of the application. Beware to add new services as singletons. services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddTransient(); // The application core (as well as many other components) depends upon the ability to cache // objects to memory. The memory cache may have already been registered with the container diff --git a/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs b/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs index 7d13a7711..f936d8bd8 100644 --- a/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs +++ b/src/FunderMaps.Infrastructure/Extensions/FunderMapsInfrastructureServiceCollectionExtensions.cs @@ -38,7 +38,7 @@ private static void ConfigureExternalServices(IServiceCollection services) // Remove all existing file storage services and inject local file stoage service. services.Configure(Configuration.GetSection("BlobStorage")); - services.AddOrReplace(ServiceLifetime.Singleton); + services.AddOrReplace(ServiceLifetime.Transient); } /// From 9fdd8d6b318cf5c51682decc2490a59aef4fa3c2 Mon Sep 17 00:00:00 2001 From: Yorick de Wid Date: Thu, 25 Mar 2021 09:27:51 +0100 Subject: [PATCH 21/21] Create client per scope --- .../Interfaces/IBlobStorageService.cs | 16 --- .../Storage/SpacesBlobStorageService.cs | 106 +++--------------- 2 files changed, 18 insertions(+), 104 deletions(-) diff --git a/src/FunderMaps.Core/Interfaces/IBlobStorageService.cs b/src/FunderMaps.Core/Interfaces/IBlobStorageService.cs index 6c36b3a3d..7fe703056 100644 --- a/src/FunderMaps.Core/Interfaces/IBlobStorageService.cs +++ b/src/FunderMaps.Core/Interfaces/IBlobStorageService.cs @@ -10,14 +10,6 @@ namespace FunderMaps.Core.Interfaces /// public interface IBlobStorageService : IServiceHealthCheck { - /// - /// Check if a file exist in storage. - /// - /// Storage container. - /// File name. - /// True if file exist, false otherwise. - Task FileExistsAsync(string containerName, string fileName); - /// /// Retrieve file access link as uri. /// @@ -27,14 +19,6 @@ public interface IBlobStorageService : IServiceHealthCheck /// The generated link. Task GetAccessLinkAsync(string containerName, string fileName, double hoursValid); - /// - /// Store the file in the data store. - /// - /// Storage container. - /// File name. - /// Content stream. - Task StoreFileAsync(string containerName, string fileName, Stream stream); - // FUTURE: Refactor /// /// Stores a file in Amazon S3. diff --git a/src/FunderMaps.Infrastructure/Storage/SpacesBlobStorageService.cs b/src/FunderMaps.Infrastructure/Storage/SpacesBlobStorageService.cs index bf03b1ae2..810bbdc8f 100644 --- a/src/FunderMaps.Infrastructure/Storage/SpacesBlobStorageService.cs +++ b/src/FunderMaps.Infrastructure/Storage/SpacesBlobStorageService.cs @@ -23,14 +23,14 @@ namespace FunderMaps.Infrastructure.Storage /// This creates an client once in its constructor. /// Register this service as a singleton if dependency injection is used. /// - internal class SpacesBlobStorageService : IBlobStorageService, IDisposable + internal class SpacesBlobStorageService : IBlobStorageService { private static readonly byte MaxKeys = 255; - private static readonly byte ConcurrentServiceRequests = 10; private readonly BlobStorageOptions _options; - private readonly IAmazonS3 client; private readonly ILogger _logger; + private readonly AWSCredentials _awsCredentials; + private readonly AmazonS3Config _clientConfig; /// /// Create new instance. @@ -40,52 +40,17 @@ public SpacesBlobStorageService(IOptions options, ILogger - /// Called on graceful shutdown. + /// Create a new Amazon S3 client. /// - public void Dispose() => client.Dispose(); - - // TODO Amazon has no clean way to check for object existence. - /// - /// Checks if a file exists or not. - /// - /// The container name. - /// The file name. - /// Boolean result. - public async Task FileExistsAsync(string containerName, string fileName) - { - try - { - // TODO Maybe use list keys with a filter? - - var result = await client.GetObjectAsync(new GetObjectRequest - { - BucketName = _options.BlobStorageName, - Key = string.IsNullOrEmpty(containerName) ? fileName : $"{containerName}/{fileName}" - }); - - return true; - } - catch (Exception e) - { - // This type of exception indicates that the file does not exist. - if (e is AmazonS3Exception && ((AmazonS3Exception)e).ErrorCode == "NoSuchKey") - { - return false; - } - - _logger.LogError("Could not check file existence in Spaces using S3"); - - throw new StorageException("Could not check file existence", e); - } - } + protected AmazonS3Client CreateClient => new(_awsCredentials, _clientConfig); /// /// Gets an access uri for a given file. @@ -98,7 +63,7 @@ public Task GetAccessLinkAsync(string containerName, string fileName, doubl { try { - var url = client.GetPreSignedURL(new GetPreSignedUrlRequest + var url = CreateClient.GetPreSignedURL(new GetPreSignedUrlRequest { BucketName = _options.BlobStorageName, Key = string.IsNullOrEmpty(containerName) ? fileName : $"{containerName}/{fileName}", @@ -115,31 +80,6 @@ public Task GetAccessLinkAsync(string containerName, string fileName, doubl } } - /// - /// Stores a file. - /// - /// The container name. - /// The file name. - /// See . - /// See . - public async Task StoreFileAsync(string containerName, string fileName, Stream stream) - { - try - { - var key = string.IsNullOrEmpty(containerName) ? fileName : $"{containerName}/{fileName}"; - using var transferUtility = new TransferUtility(client); - - await transferUtility.UploadAsync(stream, _options.BlobStorageName, key); - } - catch (AmazonS3Exception e) - { - _logger.LogError("Could not store file to Spaces using S3"); - - throw new StorageException("Could not store file", e); - } - } - - // FUTURE: Refactor /// /// Stores a file. /// @@ -170,7 +110,7 @@ public async Task StoreFileAsync(string containerName, string fileName, string c request.Headers.ContentEncoding = storageObject.ContentEncoding ?? request.Headers.ContentEncoding; } - using TransferUtility transferUtility = new(client); + using TransferUtility transferUtility = new(CreateClient); await transferUtility.UploadAsync(request); } catch (AmazonS3Exception e) @@ -181,7 +121,6 @@ public async Task StoreFileAsync(string containerName, string fileName, string c } } - // FUTURE: Refactor /// /// Stores a directory. /// @@ -211,17 +150,8 @@ public async Task StoreDirectoryAsync(string directoryName, string directoryPath uploadDirectoryRequest.UploadRequest.Headers.ContentEncoding = storageObject?.ContentEncoding ?? uploadDirectoryRequest.UploadRequest.Headers.ContentEncoding; }; - TransferUtilityConfig config = new() - { - // Note: This is currently set to the default value of 10. I did some benchmarking on 23 dec 2020 - // and discovered that turning this value up will cause some unstable behaviour resulting in - // the task throwing an exception and being cancelled. Setting this to 20 seemed to work fine, - // however 50 or above seems to result in crashes. I've left it on 10 for now to be safe for use - // in production. Worth investigating later for a major performance increase! - ConcurrentServiceRequests = ConcurrentServiceRequests - }; - - await new TransferUtility(client, config).UploadDirectoryAsync(request); + using TransferUtility transferUtility = new(CreateClient); + await transferUtility.UploadDirectoryAsync(request); } catch (AmazonS3Exception e) { @@ -253,9 +183,9 @@ public async Task RemoveDirectoryAsync(string directoryPath) List tasklist = new(); - for (ListObjectsV2Response response = await client.ListObjectsV2Async(request); + for (ListObjectsV2Response response = await CreateClient.ListObjectsV2Async(request); response.IsTruncated; - request.ContinuationToken = response.NextContinuationToken, response = await client.ListObjectsV2Async(request)) + request.ContinuationToken = response.NextContinuationToken, response = await CreateClient.ListObjectsV2Async(request)) { // TODO; Move this into for loop if (response.S3Objects.Count <= 0) @@ -263,7 +193,7 @@ public async Task RemoveDirectoryAsync(string directoryPath) break; } - Task deleteTask = client.DeleteObjectsAsync(new() + Task deleteTask = CreateClient.DeleteObjectsAsync(new() { BucketName = _options.BlobStorageName, Objects = response.S3Objects.Select(x => new KeyVersion() @@ -291,7 +221,7 @@ public async Task RemoveDirectoryAsync(string directoryPath) /// Test the Amazon S3 service backend. /// public async Task HealthCheck() - => await client.ListBucketsAsync(); + => await CreateClient.ListBucketsAsync(); } } #pragma warning restore CA1812 // Internal class is never instantiated