diff --git a/docker-compose.override.yml b/docker-compose.override.yml index b840bda61..14528ccff 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -29,6 +29,7 @@ services: - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word - ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - EventBusConnection=rabbitmq + - UseCustomizationData=True ports: - "5101:80" @@ -40,6 +41,7 @@ services: - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback #localhost do not work for UWP login, so we have to use "external" IP always - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105. + - UseCustomizationData=True ports: - "5105:80" @@ -50,6 +52,7 @@ services: - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - EventBusConnection=rabbitmq + - UseCustomizationData=True ports: - "5102:80" @@ -79,6 +82,7 @@ services: - OrderingUrlHC=http://ordering.api/hc - IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser. - BasketUrlHC=http://basket.api/hc + - UseCustomizationData=True ports: - "5104:80" @@ -92,6 +96,7 @@ services: - IdentityUrl=http://10.0.75.1:5105 - MarketingUrl=http://marketing.api #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser. + - UseCustomizationData=True ports: - "5100:80" diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 0a36c0c57..9a8fefaac 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26430.12 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3AF739CD-81D8-428D-A08A-0A58372DEBF6}" @@ -42,8 +44,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\U EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}" EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj", "{F16E3C6A-1C94-4EAB-BE91-099618060B68}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\Services\IntegrationTests\IntegrationTests.csproj", "{5B810E3D-112E-4857-B197-F09D2FD41E27}" diff --git a/eShopOnContainers.sln b/eShopOnContainers.sln index 0650b6465..67c8993d6 100644 --- a/eShopOnContainers.sln +++ b/eShopOnContainers.sln @@ -1,8 +1,20 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.6 +VisualStudioVersion = 15.0.26430.13 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}" + ProjectSection(ProjectDependencies) = postProject + {A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B} + {9842DB3A-1391-48C7-A49C-2FABD0A18AC2} = {9842DB3A-1391-48C7-A49C-2FABD0A18AC2} + {23FB706A-2701-41E9-8BF9-28936001CA41} = {23FB706A-2701-41E9-8BF9-28936001CA41} + {F0333D8E-0B27-42B7-B2C6-78F3657624E2} = {F0333D8E-0B27-42B7-B2C6-78F3657624E2} + {42681D9D-750A-4DF7-BD9F-9292CFD5C253} = {42681D9D-750A-4DF7-BD9F-9292CFD5C253} + {2110CBB0-3B38-4EE4-A743-DF6968D80D90} = {2110CBB0-3B38-4EE4-A743-DF6968D80D90} + {231226CE-690B-4979-8870-9A79D80928E2} = {231226CE-690B-4979-8870-9A79D80928E2} + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357} = {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357} + EndProjectSection +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{91CF7717-08AB-4E65-B10E-0B426F01E2E8}" @@ -49,7 +61,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.UnitTests EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.TestRunner.Droid", "src\Mobile\eShopOnContainers\eShopOnContainers.TestRunner.Droid\eShopOnContainers.TestRunner.Droid.csproj", "{A289A7F0-ACD8-42AE-87B6-AB1AFD310BF1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.TestRunner.Windows", "src\Mobile\eShopOnContainers\eShopOnContainers.TestRunner.Windows\eShopOnContainers.TestRunner.Windows.csproj", "{02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.TestRunner.Windows", "src\Mobile\eShopOnContainers\eShopOnContainers.TestRunner.Windows\eShopOnContainers.TestRunner.Windows.csproj", "{A7337243-33B8-463A-87AD-944B75EFD820}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.TestRunner.iOS", "src\Mobile\eShopOnContainers\eShopOnContainers.TestRunner.iOS\eShopOnContainers.TestRunner.iOS.csproj", "{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}" EndProject @@ -67,18 +79,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\U EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}" EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}" - ProjectSection(ProjectDependencies) = postProject - {A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B} - {9842DB3A-1391-48C7-A49C-2FABD0A18AC2} = {9842DB3A-1391-48C7-A49C-2FABD0A18AC2} - {23FB706A-2701-41E9-8BF9-28936001CA41} = {23FB706A-2701-41E9-8BF9-28936001CA41} - {F0333D8E-0B27-42B7-B2C6-78F3657624E2} = {F0333D8E-0B27-42B7-B2C6-78F3657624E2} - {42681D9D-750A-4DF7-BD9F-9292CFD5C253} = {42681D9D-750A-4DF7-BD9F-9292CFD5C253} - {2110CBB0-3B38-4EE4-A743-DF6968D80D90} = {2110CBB0-3B38-4EE4-A743-DF6968D80D90} - {231226CE-690B-4979-8870-9A79D80928E2} = {231226CE-690B-4979-8870-9A79D80928E2} - {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357} = {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357} - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{1EF3AC0F-F27C-46DD-AC53-D762D2C11C45}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventBus", "EventBus", "{B473B70F-0796-4862-B1AD-BB742D93B868}" @@ -133,6 +133,54 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|ARM.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhone.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x64.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x64.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x86.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x86.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|ARM.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|ARM.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhone.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x64.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x64.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x86.ActiveCfg = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x86.Build.0 = Debug|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|Any CPU.Build.0 = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|ARM.ActiveCfg = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|ARM.Build.0 = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhone.ActiveCfg = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhone.Build.0 = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x64.ActiveCfg = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x64.Build.0 = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.ActiveCfg = Release|Any CPU + {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.Build.0 = Release|Any CPU {2110CBB0-3B38-4EE4-A743-DF6968D80D90}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {2110CBB0-3B38-4EE4-A743-DF6968D80D90}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU {2110CBB0-3B38-4EE4-A743-DF6968D80D90}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -599,72 +647,72 @@ Global {A289A7F0-ACD8-42AE-87B6-AB1AFD310BF1}.Release|x86.ActiveCfg = Release|Any CPU {A289A7F0-ACD8-42AE-87B6-AB1AFD310BF1}.Release|x86.Build.0 = Release|Any CPU {A289A7F0-ACD8-42AE-87B6-AB1AFD310BF1}.Release|x86.Deploy.0 = Release|Any CPU - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|Any CPU.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|Any CPU.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|Any CPU.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|ARM.ActiveCfg = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|ARM.Build.0 = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|ARM.Deploy.0 = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|iPhone.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|iPhone.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|iPhone.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|x64.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|x64.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|x64.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|x86.ActiveCfg = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|x86.Build.0 = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Ad-Hoc|x86.Deploy.0 = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|Any CPU.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|Any CPU.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|Any CPU.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|ARM.ActiveCfg = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|ARM.Build.0 = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|ARM.Deploy.0 = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|iPhone.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|iPhone.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|iPhone.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|iPhoneSimulator.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|iPhoneSimulator.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|iPhoneSimulator.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|x64.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|x64.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|x64.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|x86.ActiveCfg = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|x86.Build.0 = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.AppStore|x86.Deploy.0 = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|Any CPU.ActiveCfg = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|Any CPU.Build.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|Any CPU.Deploy.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|ARM.ActiveCfg = Debug|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|ARM.Build.0 = Debug|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|ARM.Deploy.0 = Debug|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|iPhone.ActiveCfg = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|iPhone.Build.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|iPhone.Deploy.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|iPhoneSimulator.ActiveCfg = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|iPhoneSimulator.Build.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|iPhoneSimulator.Deploy.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|x64.ActiveCfg = Debug|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|x64.Build.0 = Debug|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|x64.Deploy.0 = Debug|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|x86.ActiveCfg = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|x86.Build.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Debug|x86.Deploy.0 = Debug|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|Any CPU.ActiveCfg = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|ARM.ActiveCfg = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|ARM.Build.0 = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|ARM.Deploy.0 = Release|ARM - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|iPhone.ActiveCfg = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|iPhoneSimulator.ActiveCfg = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|x64.ActiveCfg = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|x64.Build.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|x64.Deploy.0 = Release|x64 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|x86.ActiveCfg = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|x86.Build.0 = Release|x86 - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F}.Release|x86.Deploy.0 = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|Any CPU.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|Any CPU.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|Any CPU.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|ARM.ActiveCfg = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|ARM.Build.0 = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|ARM.Deploy.0 = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|iPhone.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|iPhone.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|iPhone.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|x64.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|x64.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|x64.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|x86.ActiveCfg = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|x86.Build.0 = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Ad-Hoc|x86.Deploy.0 = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|Any CPU.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|Any CPU.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|Any CPU.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|ARM.ActiveCfg = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|ARM.Build.0 = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|ARM.Deploy.0 = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|iPhone.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|iPhone.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|iPhone.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|iPhoneSimulator.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|iPhoneSimulator.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|iPhoneSimulator.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|x64.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|x64.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|x64.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|x86.ActiveCfg = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|x86.Build.0 = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.AppStore|x86.Deploy.0 = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|Any CPU.ActiveCfg = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|Any CPU.Build.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|Any CPU.Deploy.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|ARM.ActiveCfg = Debug|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|ARM.Build.0 = Debug|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|ARM.Deploy.0 = Debug|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|iPhone.ActiveCfg = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|iPhone.Build.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|iPhone.Deploy.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|iPhoneSimulator.ActiveCfg = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|iPhoneSimulator.Build.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|iPhoneSimulator.Deploy.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|x64.ActiveCfg = Debug|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|x64.Build.0 = Debug|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|x64.Deploy.0 = Debug|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|x86.ActiveCfg = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|x86.Build.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Debug|x86.Deploy.0 = Debug|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|Any CPU.ActiveCfg = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|ARM.ActiveCfg = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|ARM.Build.0 = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|ARM.Deploy.0 = Release|ARM + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|iPhone.ActiveCfg = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|iPhoneSimulator.ActiveCfg = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|x64.ActiveCfg = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|x64.Build.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|x64.Deploy.0 = Release|x64 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|x86.ActiveCfg = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|x86.Build.0 = Release|x86 + {A7337243-33B8-463A-87AD-944B75EFD820}.Release|x86.Deploy.0 = Release|x86 {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|ARM.ActiveCfg = Ad-Hoc|iPhone {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone @@ -986,54 +1034,6 @@ Global {A579E108-5445-403D-A407-339AC4D1611B}.Release|x64.Build.0 = Release|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Release|x86.ActiveCfg = Release|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Release|x86.Build.0 = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|ARM.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhone.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x64.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x64.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x86.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.AppStore|x86.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|ARM.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|ARM.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhone.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x64.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x64.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x86.ActiveCfg = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Debug|x86.Build.0 = Debug|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|Any CPU.Build.0 = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|ARM.ActiveCfg = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|ARM.Build.0 = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhone.ActiveCfg = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhone.Build.0 = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x64.ActiveCfg = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x64.Build.0 = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.ActiveCfg = Release|Any CPU - {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.Build.0 = Release|Any CPU {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -1492,7 +1492,7 @@ Global {B7B1D395-4E06-4036-BE86-C216756B9367} = {A857AD10-40FF-4303-BEC2-FF1C58D5735E} {F7B6A162-BC4D-4924-B16A-713F9B0344E7} = {B7B1D395-4E06-4036-BE86-C216756B9367} {A289A7F0-ACD8-42AE-87B6-AB1AFD310BF1} = {B7B1D395-4E06-4036-BE86-C216756B9367} - {02680C26-CA1D-4D9D-A7E3-D66AF5BE6F2F} = {B7B1D395-4E06-4036-BE86-C216756B9367} + {A7337243-33B8-463A-87AD-944B75EFD820} = {B7B1D395-4E06-4036-BE86-C216756B9367} {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3} = {B7B1D395-4E06-4036-BE86-C216756B9367} {95F1F07C-4D92-4742-BD07-E5B805AAB651} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} {02DF7FEE-C302-433D-A6CD-237A2569F236} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj index 8f30f7ca3..2bb53692a 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -21,6 +21,11 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest @@ -50,6 +55,7 @@ + @@ -72,6 +78,9 @@ PreserveNewest + + PreserveNewest + diff --git a/src/Services/Catalog/Catalog.API/CatalogSettings.cs b/src/Services/Catalog/Catalog.API/CatalogSettings.cs index af6e0ab13..55d65ae9c 100644 --- a/src/Services/Catalog/Catalog.API/CatalogSettings.cs +++ b/src/Services/Catalog/Catalog.API/CatalogSettings.cs @@ -5,5 +5,7 @@ public string ExternalCatalogBaseUrl {get;set;} public string EventBusConnection { get; set; } + + public bool UseCustomizationData { get; set; } } } diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 374d8ec7c..bb411f4d9 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -235,7 +235,10 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers items.ForEach(x => { - x.PictureUri = x.PictureUri.Replace("http://externalcatalogbaseurltobereplaced", baseUri); + if (!x.PictureUri.Contains('/')) + { + x.PictureUri = $"{baseUri}/api/v1/pic/{x.PictureUri}"; + } }); return items; diff --git a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs index 8d8aaf9f2..47bc33a02 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs @@ -15,16 +15,58 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers _env = env; } - [HttpGet("{id}")] + [HttpGet("{filename}")] // GET: // - public IActionResult GetImage(int id) + public IActionResult GetImage(string filename) { var webRoot = _env.WebRootPath; - var path = Path.Combine(webRoot, id + ".png"); + var path = Path.Combine(webRoot, filename); - var buffer = System.IO.File.ReadAllBytes(path); - - return File(buffer, "image/png"); + string imageFileExtension = Path.GetExtension(filename); + string mimetype = GetImageMimeTypeFromImageFileExtension(imageFileExtension); + + var buffer = System.IO.File.ReadAllBytes(path); + + return File(buffer, mimetype); + } + + private string GetImageMimeTypeFromImageFileExtension(string extension) + { + string mimetype; + + switch (extension) + { + case "png": + mimetype = "image/png"; + break; + case "gif": + mimetype = "image/gif"; + break; + case "jpg": + case "jpeg": + mimetype = "image/jpeg"; + break; + case "bmp": + mimetype = "image/bmp"; + break; + case "tiff": + mimetype = "image/tiff"; + break; + case "wmf": + mimetype = "image/wmf"; + break; + case "jp2": + mimetype = "image/jp2"; + break; + case "svg": + mimetype = "image/svg+xml"; + break; + default: + mimetype = "application/octet-stream"; + break; + } + + return mimetype; } } } diff --git a/src/Services/Catalog/Catalog.API/Extensions/LinqSelectExtensions.cs b/src/Services/Catalog/Catalog.API/Extensions/LinqSelectExtensions.cs new file mode 100644 index 000000000..1e5fbf789 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Extensions/LinqSelectExtensions.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Catalog.API.Extensions +{ + public static class LinqSelectExtensions + { + public static IEnumerable> SelectTry(this IEnumerable enumerable, Func selector) + { + foreach (TSource element in enumerable) + { + SelectTryResult returnedValue; + try + { + returnedValue = new SelectTryResult(element, selector(element), null); + } + catch (Exception ex) + { + returnedValue = new SelectTryResult(element, default(TResult), ex); + } + yield return returnedValue; + } + } + + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); + } + + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); + } + + public class SelectTryResult + { + internal SelectTryResult(TSource source, TResult result, Exception exception) + { + Source = source; + Result = result; + CaughtException = exception; + } + + public TSource Source { get; private set; } + public TResult Result { get; private set; } + public Exception CaughtException { get; private set; } + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs index 669768abd..c83696293 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs @@ -2,47 +2,115 @@ { using EntityFrameworkCore; using Extensions.Logging; + using global::Catalog.API.Extensions; using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; using Model; using System; using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.IO.Compression; using System.Linq; + using System.Text.RegularExpressions; using System.Threading.Tasks; public class CatalogContextSeed { - public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0) + public static async Task SeedAsync(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory, int? retry = 0) { + var log = loggerFactory.CreateLogger("catalog seed"); + var context = (CatalogContext)applicationBuilder .ApplicationServices.GetService(typeof(CatalogContext)); context.Database.Migrate(); + var settings = (CatalogSettings)applicationBuilder + .ApplicationServices.GetRequiredService>().Value; + + var useCustomizationData = settings.UseCustomizationData; + var contentRootPath = env.ContentRootPath; + var picturePath = env.WebRootPath; + if (!context.CatalogBrands.Any()) { - context.CatalogBrands.AddRange( - GetPreconfiguredCatalogBrands()); + context.CatalogBrands.AddRange(useCustomizationData + ? GetCatalogBrandsFromFile(contentRootPath, log) + : GetPreconfiguredCatalogBrands() + ); await context.SaveChangesAsync(); } if (!context.CatalogTypes.Any()) { - context.CatalogTypes.AddRange( - GetPreconfiguredCatalogTypes()); + context.CatalogTypes.AddRange(useCustomizationData + ? GetCatalogTypesFromFile(contentRootPath, log) + : GetPreconfiguredCatalogTypes() + ); await context.SaveChangesAsync(); } if (!context.CatalogItems.Any()) { - context.CatalogItems.AddRange( - GetPreconfiguredItems()); + context.CatalogItems.AddRange(useCustomizationData + ? GetCatalogItemsFromFile(contentRootPath, context, log) + : GetPreconfiguredItems() + ); await context.SaveChangesAsync(); + + GetCatalogItemPictures(contentRootPath, picturePath); } } + static IEnumerable GetCatalogBrandsFromFile(string contentRootPath, ILogger log) + { + string csvFileCatalogBrands = Path.Combine(contentRootPath, "Setup", "CatalogBrands.csv"); + + if (!File.Exists(csvFileCatalogBrands)) + { + return GetPreconfiguredCatalogBrands(); + } + + string[] csvheaders; + try + { + string[] requiredHeaders = { "catalogbrand" }; + csvheaders = GetHeaders( csvFileCatalogBrands, requiredHeaders ); + } + catch (Exception ex) + { + log.LogError(ex.Message); + return GetPreconfiguredCatalogBrands(); + } + + return File.ReadAllLines(csvFileCatalogBrands) + .Skip(1) // skip header row + .SelectTry(x => CreateCatalogBrand(x)) + .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .Where(x => x != null); + } + + static CatalogBrand CreateCatalogBrand(string brand) + { + brand = brand.Trim('"').Trim(); + + if (String.IsNullOrEmpty(brand)) + { + throw new Exception("catalog Brand Name is empty"); + } + + return new CatalogBrand + { + Brand = brand, + }; + } + static IEnumerable GetPreconfiguredCatalogBrands() { return new List() @@ -50,11 +118,54 @@ new CatalogBrand() { Brand = "Azure"}, new CatalogBrand() { Brand = ".NET" }, new CatalogBrand() { Brand = "Visual Studio" }, - new CatalogBrand() { Brand = "SQL Server" }, + new CatalogBrand() { Brand = "SQL Server" }, new CatalogBrand() { Brand = "Other" } }; } + static IEnumerable GetCatalogTypesFromFile(string contentRootPath, ILogger log) + { + string csvFileCatalogTypes = Path.Combine(contentRootPath, "Setup", "CatalogTypes.csv"); + + if (!File.Exists(csvFileCatalogTypes)) + { + return GetPreconfiguredCatalogTypes(); + } + + string[] csvheaders; + try + { + string[] requiredHeaders = { "catalogtype" }; + csvheaders = GetHeaders( csvFileCatalogTypes, requiredHeaders ); + } + catch (Exception ex) + { + log.LogError(ex.Message); + return GetPreconfiguredCatalogTypes(); + } + + return File.ReadAllLines(csvFileCatalogTypes) + .Skip(1) // skip header row + .SelectTry(x => CreateCatalogType(x)) + .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .Where(x => x != null); + } + + static CatalogType CreateCatalogType(string type) + { + type = type.Trim('"').Trim(); + + if (String.IsNullOrEmpty(type)) + { + throw new Exception("catalog Type Name is empty"); + } + + return new CatalogType + { + Type = type, + }; + } + static IEnumerable GetPreconfiguredCatalogTypes() { return new List() @@ -66,23 +177,204 @@ }; } + static IEnumerable GetCatalogItemsFromFile(string contentRootPath, CatalogContext context, ILogger log) + { + string csvFileCatalogItems = Path.Combine(contentRootPath, "Setup", "CatalogItems.csv"); + + if (!File.Exists(csvFileCatalogItems)) + { + return GetPreconfiguredItems(); + } + + string[] csvheaders; + try + { + string[] requiredHeaders = { "catalogtypename", "catalogbrandname", "description", "name", "price", "pictureuri" }; + string[] optionalheaders = { "availablestock", "restockthreshold", "maxstockthreshold", "onreorder" }; + csvheaders = GetHeaders(csvFileCatalogItems, requiredHeaders, optionalheaders ); + } + catch (Exception ex) + { + log.LogError(ex.Message); + return GetPreconfiguredItems(); + } + + var catalogTypeIdLookup = context.CatalogTypes.ToDictionary(ct => ct.Type, ct => ct.Id); + var catalogBrandIdLookup = context.CatalogBrands.ToDictionary(ct => ct.Brand, ct => ct.Id); + + return File.ReadAllLines(csvFileCatalogItems) + .Skip(1) // skip header row + .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") ) + .SelectTry(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup)) + .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .Where(x => x != null); + } + + static CatalogItem CreateCatalogItem(string[] column, string[] headers, Dictionary catalogTypeIdLookup, Dictionary catalogBrandIdLookup) + { + if (column.Count() != headers.Count()) + { + throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); + } + + string catalogTypeName = column[Array.IndexOf(headers, "catalogtypename")].Trim('"').Trim(); + if (!catalogTypeIdLookup.ContainsKey(catalogTypeName)) + { + throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); + } + + string catalogBrandName = column[Array.IndexOf(headers, "catalogbrandname")].Trim('"').Trim(); + if (!catalogBrandIdLookup.ContainsKey(catalogBrandName)) + { + throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); + } + + string priceString = column[Array.IndexOf(headers, "price")].Trim('"').Trim(); + if (!Decimal.TryParse(priceString, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out Decimal price)) + { + throw new Exception($"price={priceString}is not a valid decimal number"); + } + + var catalogItem = new CatalogItem() + { + CatalogTypeId = catalogTypeIdLookup[catalogTypeName], + CatalogBrandId = catalogBrandIdLookup[catalogBrandName], + Description = column[Array.IndexOf(headers, "description")].Trim('"').Trim(), + Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(), + Price = price, + PictureUri = column[Array.IndexOf(headers, "pictureuri")].Trim('"').Trim(), + }; + + int availableStockIndex = Array.IndexOf(headers, "availablestock"); + if (availableStockIndex != -1) + { + string availableStockString = column[availableStockIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(availableStockString)) + { + if ( int.TryParse(availableStockString, out int availableStock)) + { + catalogItem.AvailableStock = availableStock; + } + else + { + throw new Exception($"availableStock={availableStockString} is not a valid integer"); + } + } + } + + int restockThresholdIndex = Array.IndexOf(headers, "restockthreshold"); + if (restockThresholdIndex != -1) + { + string restockThresholdString = column[restockThresholdIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(restockThresholdString)) + { + if (int.TryParse(restockThresholdString, out int restockThreshold)) + { + catalogItem.RestockThreshold = restockThreshold; + } + else + { + throw new Exception($"restockThreshold={restockThreshold} is not a valid integer"); + } + } + } + + int maxStockThresholdIndex = Array.IndexOf(headers, "maxstockthreshold"); + if (maxStockThresholdIndex != -1) + { + string maxStockThresholdString = column[maxStockThresholdIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(maxStockThresholdString)) + { + if (int.TryParse(maxStockThresholdString, out int maxStockThreshold)) + { + catalogItem.MaxStockThreshold = maxStockThreshold; + } + else + { + throw new Exception($"maxStockThreshold={maxStockThreshold} is not a valid integer"); + } + } + } + + int onReorderIndex = Array.IndexOf(headers, "onreorder"); + if (onReorderIndex != -1) + { + string onReorderString = column[onReorderIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(onReorderString)) + { + if (bool.TryParse(onReorderString, out bool onReorder)) + { + catalogItem.OnReorder = onReorder; + } + else + { + throw new Exception($"onReorder={onReorderString} is not a valid boolean"); + } + } + } + + return catalogItem; + } + static IEnumerable GetPreconfiguredItems() { return new List() { - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1", AvailableStock = 100}, - new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup White Mug", Name = "Cup White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup Sheet", Name = "Cup Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11", AvailableStock = 100 }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/12", AvailableStock = 100 } + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "1.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "2.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "3.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "4.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "5.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "6.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "7.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "8.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup White Mug", Name = "Cup White Mug", Price = 12, PictureUri = "9.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "10.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup Sheet", Name = "Cup Sheet", Price = 8.5M, PictureUri = "11.png", AvailableStock = 100 }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "12.png", AvailableStock = 100 } }; } + + static string[] GetHeaders(string csvfile, string[] requiredHeaders, string[] optionalHeaders = null) + { + string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); + + if (csvheaders.Count() < requiredHeaders.Count()) + { + throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is bigger then csv header count '{csvheaders.Count()}' "); + } + + if (optionalHeaders != null) + { + if (csvheaders.Count() > (requiredHeaders.Count() + optionalHeaders.Count())) + { + throw new Exception($"csv header count '{csvheaders.Count()}' is larger then required '{requiredHeaders.Count()}' and optional '{optionalHeaders.Count()}' headers count"); + } + } + + foreach (var requiredHeader in requiredHeaders) + { + if (!csvheaders.Contains(requiredHeader)) + { + throw new Exception($"does not contain required header '{requiredHeader}'"); + } + } + + return csvheaders; + } + + static void GetCatalogItemPictures(string contentRootPath, string picturePath) + { + DirectoryInfo directory = new DirectoryInfo(picturePath); + foreach (FileInfo file in directory.GetFiles()) + { + file.Delete(); + } + + string zipFileCatalogItemPictures = Path.Combine(contentRootPath, "Setup", "CatalogItems.zip"); + ZipFile.ExtractToDirectory(zipFileCatalogItemPictures, picturePath); + } } + + } diff --git a/src/Services/Catalog/Catalog.API/Setup/CatalogBrands.csv b/src/Services/Catalog/Catalog.API/Setup/CatalogBrands.csv new file mode 100644 index 000000000..95c9ee64c --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Setup/CatalogBrands.csv @@ -0,0 +1,8 @@ +CatalogBrand +Azure +.NET +Visual Studio +SQL Server +Other +CatalogBrandTestOne +CatalogBrandTestTwo \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Setup/CatalogItems.csv b/src/Services/Catalog/Catalog.API/Setup/CatalogItems.csv new file mode 100644 index 000000000..da0cd297f --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Setup/CatalogItems.csv @@ -0,0 +1,14 @@ +CatalogTypeName,CatalogBrandName,Description,Name,Price,PictureUri,availablestock,onreorder +T-Shirt,.NET,".NET Bot Black Hoodie, and more",.NET Bot Black Hoodie,19.5,1.png,100,false +Mug,.NET,.NET Black & White Mug,.NET Black & White Mug,8.50,2.png,89,true +T-Shirt,Other,Prism White T-Shirt,Prism White T-Shirt,12,3.png,56,false +T-Shirt,.NET,.NET Foundation T-shirt,.NET Foundation T-shirt,12,4.png,120,false +Sheet,Other,Roslyn Red Sheet,Roslyn Red Sheet,8.5,5.png,55,false +T-Shirt,.NET,.NET Blue Hoodie,.NET Blue Hoodie,12,6.png,17,false +T-Shirt,Other,Roslyn Red T-Shirt,Roslyn Red T-Shirt",12,7.png,8,false +T-Shirt,Other,Kudu Purple Hoodie,Kudu Purple Hoodie,8.5,8.png,34,false +Mug,Other,Cup White Mug,Cup White Mug,12,9.png,76,false +Sheet,.NET,.NET Foundation Sheet,.NET Foundation Sheet,12,10.png,11,false +Sheet,.NET,Cup Sheet,Cup Sheet,8.5,11.png,3,false +T-Shirt,Other,Prism White TShirt,Prism White TShirt,12,12.png,0,false +Mug, Other, De los Palotes, pepito, 12, 12.png, 0, false \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Setup/CatalogTypes.csv b/src/Services/Catalog/Catalog.API/Setup/CatalogTypes.csv new file mode 100644 index 000000000..ae636c265 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Setup/CatalogTypes.csv @@ -0,0 +1,7 @@ +CatalogType +Mug +T-Shirt +Sheet +USB Memory Stick +CatalogTypeTestOne +CatalogTypeTestTwo \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Setup/Catalogitems.zip b/src/Services/Catalog/Catalog.API/Setup/Catalogitems.zip new file mode 100644 index 000000000..6d3edfb3c Binary files /dev/null and b/src/Services/Catalog/Catalog.API/Setup/Catalogitems.zip differ diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index c5784b6a4..8f2102f8b 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -153,7 +153,7 @@ var context = (CatalogContext)app .ApplicationServices.GetService(typeof(CatalogContext)); - WaitForSqlAvailabilityAsync(context, loggerFactory, app).Wait(); + WaitForSqlAvailabilityAsync(context, loggerFactory, app, env).Wait(); ConfigureEventBus(app); @@ -165,13 +165,13 @@ integrationEventLogContext.Database.Migrate(); } - private async Task WaitForSqlAvailabilityAsync(CatalogContext ctx, ILoggerFactory loggerFactory, IApplicationBuilder app, int retries = 0) + private async Task WaitForSqlAvailabilityAsync(CatalogContext ctx, ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env, int retries = 0) { var logger = loggerFactory.CreateLogger(nameof(Startup)); var policy = CreatePolicy(retries, logger, nameof (WaitForSqlAvailabilityAsync)); await policy.ExecuteAsync(async () => { - await CatalogContextSeed.SeedAsync(app, loggerFactory); + await CatalogContextSeed.SeedAsync(app, env, loggerFactory); }); } diff --git a/src/Services/Catalog/Catalog.API/settings.json b/src/Services/Catalog/Catalog.API/settings.json index e67fec713..c60969ab7 100644 --- a/src/Services/Catalog/Catalog.API/settings.json +++ b/src/Services/Catalog/Catalog.API/settings.json @@ -1,6 +1,7 @@ { "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", "ExternalCatalogBaseUrl": "http://localhost:5101", + "UseCustomizationData": true, "Logging": { "IncludeScopes": false, "LogLevel": { diff --git a/src/Services/Identity/Identity.API/AppSettings.cs b/src/Services/Identity/Identity.API/AppSettings.cs index bc81ecf36..78d848d41 100644 --- a/src/Services/Identity/Identity.API/AppSettings.cs +++ b/src/Services/Identity/Identity.API/AppSettings.cs @@ -8,5 +8,6 @@ namespace eShopOnContainers.Identity public class AppSettings { public string MvcClient { get; set; } + public bool UseCustomizationData { get; set; } } } diff --git a/src/Services/Identity/Identity.API/Data/ApplicationContextSeed.cs b/src/Services/Identity/Identity.API/Data/ApplicationContextSeed.cs index cf8ae612f..0f81bcf66 100644 --- a/src/Services/Identity/Identity.API/Data/ApplicationContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ApplicationContextSeed.cs @@ -3,13 +3,21 @@ using AspNetCore.Identity; using EntityFrameworkCore; using Extensions.Logging; + using global::eShopOnContainers.Identity; using global::Identity.API.Data; using global::Identity.API.Models; + using Identity.API.Extensions; using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; using System; using System.Collections.Generic; + using System.IO; + using System.IO.Compression; using System.Linq; using System.Security.Cryptography; + using System.Text.RegularExpressions; using System.Threading.Tasks; public class ApplicationContextSeed @@ -21,23 +29,38 @@ _passwordHasher = passwordHasher; } - public async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0) + public async Task SeedAsync(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory, int? retry = 0) { int retryForAvaiability = retry.Value; try { + var log = loggerFactory.CreateLogger("application seed"); + var context = (ApplicationDbContext)applicationBuilder .ApplicationServices.GetService(typeof(ApplicationDbContext)); context.Database.Migrate(); + var settings = (AppSettings)applicationBuilder + .ApplicationServices.GetRequiredService>().Value; + + var useCustomizationData = settings.UseCustomizationData; + var contentRootPath = env.ContentRootPath; + var webroot = env.WebRootPath; + if (!context.Users.Any()) { - context.Users.AddRange( - GetDefaultUser()); + context.Users.AddRange(useCustomizationData + ? GetUsersFromFile(contentRootPath, log) + : GetDefaultUser()); await context.SaveChangesAsync(); } + + if (useCustomizationData) + { + GetPreconfiguredImages(contentRootPath, webroot, log); + } } catch (Exception ex) { @@ -46,14 +69,93 @@ retryForAvaiability++; var log = loggerFactory.CreateLogger("catalog seed"); log.LogError(ex.Message); - await SeedAsync(applicationBuilder, loggerFactory, retryForAvaiability); + await SeedAsync(applicationBuilder, env, loggerFactory, retryForAvaiability); } } } - private ApplicationUser GetDefaultUser() + private IEnumerable GetUsersFromFile(string contentRootPath, ILogger log) { - var user = + string csvFileUsers = Path.Combine(contentRootPath, "Setup", "Users.csv"); + + if (!File.Exists(csvFileUsers)) + { + return GetDefaultUser(); + } + + string[] csvheaders; + try + { + string[] requiredHeaders = { + "cardholdername", "cardnumber", "cardtype", "city", "country", + "email", "expiration", "lastname", "name", "phonenumber", + "username", "zipcode", "state", "street", "securitynumber", + "normalizedemail", "normalizedusername", "password" + }; + csvheaders = GetHeaders(requiredHeaders, csvFileUsers); + } + catch (Exception ex) + { + log.LogError(ex.Message); + return GetDefaultUser(); + } + + List users = File.ReadAllLines(csvFileUsers) + .Skip(1) // skip header column + .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") ) + .SelectTry(column => CreateApplicationUser(column, csvheaders)) + .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .Where(x => x != null) + .ToList(); + + return users; + } + + private ApplicationUser CreateApplicationUser(string[] column, string[] headers) + { + if (column.Count() != headers.Count()) + { + throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); + } + + string cardtypeString = column[Array.IndexOf(headers, "cardtype")].Trim('"').Trim(); + if (!int.TryParse(cardtypeString, out int cardtype)) + { + throw new Exception($"cardtype='{cardtypeString}' is not a number"); + } + + var user = new ApplicationUser + { + CardHolderName = column[Array.IndexOf(headers, "cardholdername")].Trim('"').Trim(), + CardNumber = column[Array.IndexOf(headers, "cardnumber")].Trim('"').Trim(), + CardType = cardtype, + City = column[Array.IndexOf(headers, "city")].Trim('"').Trim(), + Country = column[Array.IndexOf(headers, "country")].Trim('"').Trim(), + Email = column[Array.IndexOf(headers, "email")].Trim('"').Trim(), + Expiration = column[Array.IndexOf(headers, "expiration")].Trim('"').Trim(), + Id = Guid.NewGuid().ToString(), + LastName = column[Array.IndexOf(headers, "lastname")].Trim('"').Trim(), + Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(), + PhoneNumber = column[Array.IndexOf(headers, "phonenumber")].Trim('"').Trim(), + UserName = column[Array.IndexOf(headers, "username")].Trim('"').Trim(), + ZipCode = column[Array.IndexOf(headers, "zipcode")].Trim('"').Trim(), + State = column[Array.IndexOf(headers, "state")].Trim('"').Trim(), + Street = column[Array.IndexOf(headers, "street")].Trim('"').Trim(), + SecurityNumber = column[Array.IndexOf(headers, "securitynumber")].Trim('"').Trim(), + NormalizedEmail = column[Array.IndexOf(headers, "normalizedemail")].Trim('"').Trim(), + NormalizedUserName = column[Array.IndexOf(headers, "normalizedusername")].Trim('"').Trim(), + SecurityStamp = Guid.NewGuid().ToString("D"), + PasswordHash = column[Array.IndexOf(headers, "password")].Trim('"').Trim(), // Note: This is the password + }; + + user.PasswordHash = _passwordHasher.HashPassword(user, user.PasswordHash); + + return user; + } + + private IEnumerable GetDefaultUser() + { + var user = new ApplicationUser() { CardHolderName = "DemoUser", @@ -63,23 +165,86 @@ Country = "U.S.", Email = "demouser@microsoft.com", Expiration = "12/20", - Id = Guid.NewGuid().ToString(), - LastName = "DemoLastName", - Name = "DemoUser", - PhoneNumber = "1234567890", - UserName = "demouser@microsoft.com", - ZipCode = "98052", - State = "WA", - Street = "15703 NE 61st Ct", - SecurityNumber = "535", - NormalizedEmail = "DEMOUSER@MICROSOFT.COM", - NormalizedUserName = "DEMOUSER@MICROSOFT.COM", - SecurityStamp = Guid.NewGuid().ToString("D") + Id = Guid.NewGuid().ToString(), + LastName = "DemoLastName", + Name = "DemoUser", + PhoneNumber = "1234567890", + UserName = "demouser@microsoft.com", + ZipCode = "98052", + State = "WA", + Street = "15703 NE 61st Ct", + SecurityNumber = "535", + NormalizedEmail = "DEMOUSER@MICROSOFT.COM", + NormalizedUserName = "DEMOUSER@MICROSOFT.COM", + SecurityStamp = Guid.NewGuid().ToString("D"), }; user.PasswordHash = _passwordHasher.HashPassword(user, "Pass@word1"); - return user; + return new List() + { + user + }; + } + + static string[] GetHeaders(string[] requiredHeaders, string csvfile) + { + string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); + + if (csvheaders.Count() != requiredHeaders.Count()) + { + throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); + } + + foreach (var requiredHeader in requiredHeaders) + { + if (!csvheaders.Contains(requiredHeader)) + { + throw new Exception($"does not contain required header '{requiredHeader}'"); + } + } + + return csvheaders; + } + + static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log) + { + try + { + string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); + if (!File.Exists(imagesZipFile)) + { + log.LogError($" zip file '{imagesZipFile}' does not exists."); + return; + } + + string imagePath = Path.Combine(webroot, "images"); + string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); + + using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in zip.Entries) + { + if (imageFiles.Contains(entry.Name)) + { + string destinationFilename = Path.Combine(imagePath, entry.Name); + if (File.Exists(destinationFilename)) + { + File.Delete(destinationFilename); + } + entry.ExtractToFile(destinationFilename); + } + else + { + log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); + } + } + } + } + catch (Exception ex) + { + log.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}"); + } } } } diff --git a/src/Services/Identity/Identity.API/Extensions/LinqSelectExtensions.cs b/src/Services/Identity/Identity.API/Extensions/LinqSelectExtensions.cs new file mode 100644 index 000000000..e3ff0a3c0 --- /dev/null +++ b/src/Services/Identity/Identity.API/Extensions/LinqSelectExtensions.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Identity.API.Extensions +{ + public static class LinqSelectExtensions + { + public static IEnumerable> SelectTry(this IEnumerable enumerable, Func selector) + { + foreach (TSource element in enumerable) + { + SelectTryResult returnedValue; + try + { + returnedValue = new SelectTryResult(element, selector(element), null); + } + catch (Exception ex) + { + returnedValue = new SelectTryResult(element, default(TResult), ex); + } + yield return returnedValue; + } + } + + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); + } + + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); + } + + public class SelectTryResult + { + internal SelectTryResult(TSource source, TResult result, Exception exception) + { + Source = source; + Result = result; + CaughtException = exception; + } + + public TSource Source { get; private set; } + public TResult Result { get; private set; } + public Exception CaughtException { get; private set; } + } + } +} diff --git a/src/Services/Identity/Identity.API/Identity.API.csproj b/src/Services/Identity/Identity.API/Identity.API.csproj index d31664a2b..1fd2a7fa8 100644 --- a/src/Services/Identity/Identity.API/Identity.API.csproj +++ b/src/Services/Identity/Identity.API/Identity.API.csproj @@ -8,7 +8,13 @@ $(PackageTargetFallback);dotnet5.6;portable-net45+win8 ..\..\..\..\docker-compose.dcproj - + + + + PreserveNewest + + + @@ -70,6 +76,9 @@ Always + + PreserveNewest + diff --git a/src/Services/Identity/Identity.API/Setup/Users.csv b/src/Services/Identity/Identity.API/Setup/Users.csv new file mode 100644 index 000000000..3e5081078 --- /dev/null +++ b/src/Services/Identity/Identity.API/Setup/Users.csv @@ -0,0 +1,2 @@ +CardHolderName,CardNumber,CardType,City,Country,Email,Expiration,LastName,Name,PhoneNumber,UserName,ZipCode,State,Street,SecurityNumber,NormalizedEmail,NormalizedUserName,Password +DemoUser,4012888888881881,1,Redmond,U.S.,demouser@microsoft.com,12/20,DemoLastName,DemoUser,1234567890,demouser@microsoft.com,98052,WA,15703 NE 61st Ct,535,DEMOUSER@MICROSOFT.COM,DEMOUSER@MICROSOFT.COM,Pass@word1 \ No newline at end of file diff --git a/src/Services/Identity/Identity.API/Setup/images.zip b/src/Services/Identity/Identity.API/Setup/images.zip new file mode 100644 index 000000000..1b901d3af Binary files /dev/null and b/src/Services/Identity/Identity.API/Setup/images.zip differ diff --git a/src/Services/Identity/Identity.API/Startup.cs b/src/Services/Identity/Identity.API/Startup.cs index 584aefb35..bc8ca3cac 100644 --- a/src/Services/Identity/Identity.API/Startup.cs +++ b/src/Services/Identity/Identity.API/Startup.cs @@ -153,7 +153,7 @@ namespace eShopOnContainers.Identity //Seed Data var hasher = new PasswordHasher(); - new ApplicationContextSeed(hasher).SeedAsync(app, loggerFactory).Wait(); + new ApplicationContextSeed(hasher).SeedAsync(app, env, loggerFactory).Wait(); } private async Task InitializeGrantStoreAndConfiguration(IApplicationBuilder app) diff --git a/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml b/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml index 9d0a5ce8a..163d2606a 100644 --- a/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml +++ b/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml @@ -40,10 +40,7 @@
-
-
-
- +
diff --git a/src/Services/Identity/Identity.API/appsettings.json b/src/Services/Identity/Identity.API/appsettings.json index 557bac0c3..f1f885bd8 100644 --- a/src/Services/Identity/Identity.API/appsettings.json +++ b/src/Services/Identity/Identity.API/appsettings.json @@ -6,6 +6,7 @@ "MvcClient": "http://localhost:5100", "SpaClient": "http://localhost:5104", "XamarinCallback": "http://localhost:5105/xamarincallback", + "UseCustomizationData": true, "Logging": { "IncludeScopes": false, "LogLevel": { diff --git a/src/Services/Identity/Identity.API/wwwroot/css/site.css b/src/Services/Identity/Identity.API/wwwroot/css/site.css index c8dcea80a..c29d2090d 100644 --- a/src/Services/Identity/Identity.API/wwwroot/css/site.css +++ b/src/Services/Identity/Identity.API/wwwroot/css/site.css @@ -476,11 +476,7 @@ footer { } footer .text { - text-align: right; - width: 100%; - height: 100%; - color: #83D01B; - margin-top: 10px; + margin-top: 55px; } .text { diff --git a/src/Services/Identity/Identity.API/wwwroot/css/site.min.css b/src/Services/Identity/Identity.API/wwwroot/css/site.min.css index f11b8614b..228970b59 100644 --- a/src/Services/Identity/Identity.API/wwwroot/css/site.min.css +++ b/src/Services/Identity/Identity.API/wwwroot/css/site.min.css @@ -1 +1 @@ -body{margin-top:65px}.navbar-header{position:relative;top:-4px}.navbar-brand>.icon-banner{position:relative;top:-2px;display:inline}.icon{position:relative;top:-10px}.page-consent .client-logo{float:left}.page-consent .client-logo img{width:80px;height:80px}.page-consent .consent-buttons{margin-top:25px}.page-consent .consent-form .consent-scopecheck{display:inline-block;margin-right:5px}.page-consent .consent-form .consent-description{margin-left:25px}.page-consent .consent-form .consent-description label{font-weight:normal}.page-consent .consent-form .consent-remember{padding-left:16px}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}body{font-family:Montserrat,sans-serif;min-width:480px}.mt-15{margin-top:15px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.select-filter{background-color:transparent;padding:10px;margin:10px;margin-right:20px;color:#fff;padding-top:20px;padding-bottom:3px;min-width:140px;border-color:#37c7ca;max-height:43px;-webkit-appearance:none}.select-filter option{background-color:#00a69c}select::-ms-expand{display:none}.select-filter-wrapper{z-index:0;display:inline-block;margin-left:-10px}.select-filter-wrapper::before{content:attr(data-name);opacity:.5;z-index:1;text-transform:uppercase;position:absolute;font-size:10px;margin-top:15px;margin-left:21px;color:#fff}.select-filter-arrow{position:absolute;margin-left:130px;margin-top:40px}.btn-brand-small-filter{margin-top:10px;position:absolute;margin-left:15px}.carousel-caption p{font-size:20px;line-height:1.4}.layout-cart-image{height:36px;margin-top:5px}.layout-cart-badge{position:absolute;margin-top:2px;margin-left:14px;background-color:#83d01b;padding:1px;color:#fff;border-radius:50%;width:18px;height:18px;font-size:12px;cursor:pointer}.btn-bracketed:hover:before{display:inline-block;content:"[";padding-right:.5em;color:#7fff00}.btn-bracketed:hover:after{display:inline-block;content:"]";padding-left:.5em;color:#7fff00}.btn-brand{background-color:#83d01b;color:#fff;padding:10px 20px 10px 20px;border-radius:0;border:none;width:255px;display:inline-block;text-align:center;text-transform:uppercase;height:45px;font-size:16px;font-weight:normal}.btn-brand::before{content:'['}.btn-brand::after{content:']'}.btn-brand:hover:before{padding-right:5px}.btn-brand:hover:after{padding-left:5px}.btn-brand-big{width:360px;margin-top:20px}.btn-brand-small{width:45px}.btn-brand-small::before{content:''}.btn-brand-small::after{content:''}.btn-brand-small:hover:before{content:'';padding:0}.btn-brand-small:hover:after{content:'';padding:0}.btn-brand-dark{background-color:#00a69c}.btn-brand:hover{color:#fff;background-color:#83d01b;text-decoration:none}.btn-brand-dark:hover{background-color:#00a69c}.btn-cart{float:right;margin-top:40px;margin-bottom:40px}.btn-catalog-apply{padding:0}.form-label{text-transform:uppercase;font-weight:normal!important;text-align:left;margin-bottom:10px !important;color:#404040}.form-input{border-radius:0;padding:10px;height:45px}.form-input-small{max-width:100px!important}.form-select{border-radius:0;padding:10px;height:45px;width:150px}.carousel-inner .item img[src$=".svg"]{width:100%}.navbar-inverse{background-color:#fff;border-color:#fff}.btn-login{border:1px solid #00a69c;height:36px!important;margin-right:10px;margin-top:10px;background-color:#fff;color:#00a69c;text-transform:uppercase;max-width:140px;width:140px;padding-top:8px!important}.btn-login{font-weight:normal!important}.btn-login::before{content:'['}.btn-login::after{content:']'}.btn-login:hover:before{content:'[ '}.btn-login:hover:after{content:' ]'}.navbar-inverse li a{height:30px;padding:5px 20px;color:#00a69c !important}.navbar-brand{margin-top:20px;background-image:url(../images/brand.PNG);width:201px;height:44px;margin-left:0 !important}.nav>li>a{color:#fff}.nav>li>a:hover,.nav>li>a:focus{background-color:#00a69c;font-weight:bolder}.container-fluid{padding-left:0;padding-right:0}.home-banner{width:100%;margin-right:0;margin-left:0;background-image:url(../images/main_banner.png);background-size:cover;height:258px;background-position:center}.home-banner-text{margin-top:70px}.home-catalog-container{min-height:400px;margin-bottom:20px}.home-catalog-filter-container{background-color:#00a69c;height:63px;line-height:76px}.home-catalog-filter-container li a{padding-top:5px !important}.home-catalog-filter-brands::before{content:'BRAND';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-filter-types::before{content:'TYPES';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-item{margin-top:10px;margin-bottom:10px}.home-catalog-item-image{width:100%;object-fit:cover;text-align:center}.home-catalog-item-image-addCart{background-color:#83d01b;color:#fff;display:inline-block;height:43px;padding:10px 20px 10px 20px;font-weight:bold;text-align:center;margin-top:10px;margin-left:60px;margin-right:60px;font-size:16px;font-weight:normal}.home-catalog-item-image-addCart:hover{color:#fff;text-decoration:none}.home-catalog-item-image:hover:after{cursor:pointer}.home-catalog-item-title{text-align:center;text-transform:uppercase;font-weight:300;font-size:16px;margin-top:20px}.home-catalog-item-price{text-align:center;font-weight:900;font-size:28px}.home-catalog-item-price::before{content:'$'}.home-catalog-noResults{text-align:center;margin-top:100px}.container .nav .navbar-nav .col-sm-6 ::before{content:'BRAND'}.validation-summary-errors li{list-style:none}footer{background-color:#000;height:150px;vertical-align:middle}footer .brand{margin-top:25px;background-image:url(../images/brand_dark.PNG);max-width:231px;height:52px;margin-left:0 !important}footer .text{text-align:right;width:100%;height:100%;color:#83d01b;margin-top:10px}.text{color:#83d01b}.text:hover{color:#83d01b}form .col-md-4{text-align:right}.brand-header-block{background-color:#00a69c;height:63px}.brand-header-block li{list-style:none;display:inline;opacity:.5;margin-top:25px;margin-left:10px;cursor:pointer;color:#fff}.brand-header-block li a{color:#fff}.brand-header-block li a:hover{text-decoration:none}.brand-header-block .active{opacity:1}.brand-header-block .active::before{content:'[ ';color:#adff2f}.brand-header-block .active::after{content:' ]';color:#adff2f}.brand-header-back{float:left!important;margin-top:20px!important;text-transform:uppercase}.account-login-container{min-height:70vh;text-align:center;padding-top:40px}.account-register-container{min-height:70vh;text-align:center !important;align-content:center}.cart-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px;min-width:992px}.register-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px}.order-create-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px;min-width:995px}.cart-product-column{max-width:120px;text-transform:uppercase;vertical-align:middle!important}.order-create-container .cart-product-column{max-width:130px}.cart-product-column-name{width:220px}.cart-subtotal-label{font-size:12px;color:#404040;margin-top:10px}.cart-subtotal-value{font-size:20px;color:#00a69c}.cart-total-label{font-size:14px;color:#404040;margin-top:10px}.cart-total-value{font-size:28px;color:#00a69c;text-align:left}.cart-product-image{max-width:210px}.cart-section-total{margin-bottom:5px;margin-left:175px;text-align:left}.cart-product-column input{width:70px;text-align:center}.cart-refresh-button{margin-top:0;background-image:url('../images/refresh.svg');color:#fff;font-size:8px;width:40px;height:40px;background-color:transparent;border:none;margin-top:25px;margin-left:15px}.cart-refresh-button:hover{background-color:transparent}.cart-totals{border-bottom:none!important}.input-validation-error{border:1px solid #fb0d0d}.text-danger{color:#fb0d0d;font-size:12px}.cart{border:none !important}.form-horizontal h4{margin-top:30px}.form-horizontal .form-group{margin-right:0!important}.form-control:focus{border-color:#83d01b}.form-input-center{margin:auto}.order-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-index-container .table tbody tr{border-bottom:none}.order-index-container .table tbody tr td{border-top:none;padding-top:10px;padding-bottom:10px}.order-index-container .table tbody tr:nth-child(even){background-color:#f5f5f5}.order-create-section-title{margin-left:-15px;text-transform:uppercase}.order-create-section-items{margin-left:-45px;width:102%}.order-detail-button a{color:#83d01b}.order-detail-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-detail-container .table tbody tr:first-child td{border-top:none}.order-detail-container .table tr{border-bottom:none}.order-detail-section{margin-top:50px}.order-detail-container .table{margin-left:-7px}.order-section-total{margin-bottom:5px;margin-left:40px;text-align:left}.fr{float:right!important}.down-arrow{background-image:url('../images/arrow-down.png');height:7px;width:10px;display:inline-block;margin-left:20px}.logout-icon{background-image:url('../images/logout.PNG');display:inline-block;height:19px;width:19px;margin-left:15px}.myorders-icon{background-image:url('../images/my_orders.PNG');display:inline-block;height:20px;width:20px;margin-left:15px}.login-user{position:absolute!important;top:30px;right:65px;cursor:pointer}.login-user-dropdown{position:relative;display:inline-block}.login-user-dropdown-content{display:none;position:absolute;background-color:#fff;min-width:160px;box-shadow:0 8px 16px 0 rgba(0,0,0,.2);right:0}.login-user-dropdown-content a{color:#000;padding:12px 16px;text-decoration:none;display:block;text-align:right;text-transform:uppercase}.login-user:hover .login-user-dropdown-content{display:block}.down-arrow:hover>.login-user-dropdown-content{display:block}.login-user-dropdown-content a:hover{color:#83d01b}.es-header{min-height:80px!important}.es-pager-bottom{margin-top:40px}.es-pager-top{margin-bottom:20px;margin-top:20px}.es-pager-top ul{list-style:none}.es-pager-bottom ul{list-style:none}.page-item{cursor:pointer}.next{position:absolute;right:15px;top:0}.previous{position:absolute;left:0;top:0}.is-disabled{cursor:not-allowed;opacity:.5;pointer-events:none}.table tr{border-bottom:1px solid #ddd}.table th{text-transform:uppercase}.navbar-nav{margin-top:10px;margin-bottom:7.5px;margin-right:-10px;float:right}@media screen and (max-width:1195px){.cart-product-column-name{display:none}}@media screen and (max-width:767px){.carousel-caption{display:none}footer .text{text-align:left;margin-top:-15px}.cart-product-column-brand{display:none}}@media screen and (min-width:992px){.form-input{width:360px;max-width:360px}}@media screen and (max-width:415px){.account-login-container{margin-left:-50px}.page-consent{margin-left:10px;margin-right:80px;padding-right:0;padding-left:0}} \ No newline at end of file +body{margin-top:65px}.navbar-header{position:relative;top:-4px}.navbar-brand>.icon-banner{position:relative;top:-2px;display:inline}.icon{position:relative;top:-10px}.page-consent .client-logo{float:left}.page-consent .client-logo img{width:80px;height:80px}.page-consent .consent-buttons{margin-top:25px}.page-consent .consent-form .consent-scopecheck{display:inline-block;margin-right:5px}.page-consent .consent-form .consent-description{margin-left:25px}.page-consent .consent-form .consent-description label{font-weight:normal}.page-consent .consent-form .consent-remember{padding-left:16px}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}body{font-family:Montserrat,sans-serif;min-width:480px}.mt-15{margin-top:15px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.select-filter{background-color:transparent;padding:10px;margin:10px;margin-right:20px;color:#fff;padding-top:20px;padding-bottom:3px;min-width:140px;border-color:#37c7ca;max-height:43px;-webkit-appearance:none}.select-filter option{background-color:#00a69c}select::-ms-expand{display:none}.select-filter-wrapper{z-index:0;display:inline-block;margin-left:-10px}.select-filter-wrapper::before{content:attr(data-name);opacity:.5;z-index:1;text-transform:uppercase;position:absolute;font-size:10px;margin-top:15px;margin-left:21px;color:#fff}.select-filter-arrow{position:absolute;margin-left:130px;margin-top:40px}.btn-brand-small-filter{margin-top:10px;position:absolute;margin-left:15px}.carousel-caption p{font-size:20px;line-height:1.4}.layout-cart-image{height:36px;margin-top:5px}.layout-cart-badge{position:absolute;margin-top:2px;margin-left:14px;background-color:#83d01b;padding:1px;color:#fff;border-radius:50%;width:18px;height:18px;font-size:12px;cursor:pointer}.btn-bracketed:hover:before{display:inline-block;content:"[";padding-right:.5em;color:#7fff00}.btn-bracketed:hover:after{display:inline-block;content:"]";padding-left:.5em;color:#7fff00}.btn-brand{background-color:#83d01b;color:#fff;padding:10px 20px 10px 20px;border-radius:0;border:none;width:255px;display:inline-block;text-align:center;text-transform:uppercase;height:45px;font-size:16px;font-weight:normal}.btn-brand::before{content:'['}.btn-brand::after{content:']'}.btn-brand:hover:before{padding-right:5px}.btn-brand:hover:after{padding-left:5px}.btn-brand-big{width:360px;margin-top:20px}.btn-brand-small{width:45px}.btn-brand-small::before{content:''}.btn-brand-small::after{content:''}.btn-brand-small:hover:before{content:'';padding:0}.btn-brand-small:hover:after{content:'';padding:0}.btn-brand-dark{background-color:#00a69c}.btn-brand:hover{color:#fff;background-color:#83d01b;text-decoration:none}.btn-brand-dark:hover{background-color:#00a69c}.btn-cart{float:right;margin-top:40px;margin-bottom:40px}.btn-catalog-apply{padding:0}.form-label{text-transform:uppercase;font-weight:normal!important;text-align:left;margin-bottom:10px !important;color:#404040}.form-input{border-radius:0;padding:10px;height:45px}.form-input-small{max-width:100px!important}.form-select{border-radius:0;padding:10px;height:45px;width:150px}.carousel-inner .item img[src$=".svg"]{width:100%}.navbar-inverse{background-color:#fff;border-color:#fff}.btn-login{border:1px solid #00a69c;height:36px!important;margin-right:10px;margin-top:10px;background-color:#fff;color:#00a69c;text-transform:uppercase;max-width:140px;width:140px;padding-top:8px!important}.btn-login{font-weight:normal!important}.btn-login::before{content:'['}.btn-login::after{content:']'}.btn-login:hover:before{content:'[ '}.btn-login:hover:after{content:' ]'}.navbar-inverse li a{height:30px;padding:5px 20px;color:#00a69c !important}.navbar-brand{margin-top:20px;background-image:url(../images/brand.PNG);width:201px;height:44px;margin-left:0 !important}.nav>li>a{color:#fff}.nav>li>a:hover,.nav>li>a:focus{background-color:#00a69c;font-weight:bolder}.container-fluid{padding-left:0;padding-right:0}.home-banner{width:100%;margin-right:0;margin-left:0;background-image:url(../images/main_banner.png);background-size:cover;height:258px;background-position:center}.home-banner-text{margin-top:70px}.home-catalog-container{min-height:400px;margin-bottom:20px}.home-catalog-filter-container{background-color:#00a69c;height:63px;line-height:76px}.home-catalog-filter-container li a{padding-top:5px !important}.home-catalog-filter-brands::before{content:'BRAND';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-filter-types::before{content:'TYPES';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-item{margin-top:10px;margin-bottom:10px}.home-catalog-item-image{width:100%;object-fit:cover;text-align:center}.home-catalog-item-image-addCart{background-color:#83d01b;color:#fff;display:inline-block;height:43px;padding:10px 20px 10px 20px;font-weight:bold;text-align:center;margin-top:10px;margin-left:60px;margin-right:60px;font-size:16px;font-weight:normal}.home-catalog-item-image-addCart:hover{color:#fff;text-decoration:none}.home-catalog-item-image:hover:after{cursor:pointer}.home-catalog-item-title{text-align:center;text-transform:uppercase;font-weight:300;font-size:16px;margin-top:20px}.home-catalog-item-price{text-align:center;font-weight:900;font-size:28px}.home-catalog-item-price::before{content:'$'}.home-catalog-noResults{text-align:center;margin-top:100px}.container .nav .navbar-nav .col-sm-6 ::before{content:'BRAND'}.validation-summary-errors li{list-style:none}footer{background-color:#000;height:150px;vertical-align:middle}footer .brand{margin-top:25px;background-image:url(../images/brand_dark.PNG);max-width:231px;height:52px;margin-left:0 !important}footer .text{margin-top:55px}.text{color:#83d01b}.text:hover{color:#83d01b}form .col-md-4{text-align:right}.brand-header-block{background-color:#00a69c;height:63px}.brand-header-block li{list-style:none;display:inline;opacity:.5;margin-top:25px;margin-left:10px;cursor:pointer;color:#fff}.brand-header-block li a{color:#fff}.brand-header-block li a:hover{text-decoration:none}.brand-header-block .active{opacity:1}.brand-header-block .active::before{content:'[ ';color:#adff2f}.brand-header-block .active::after{content:' ]';color:#adff2f}.brand-header-back{float:left!important;margin-top:20px!important;text-transform:uppercase}.account-login-container{min-height:70vh;text-align:center;padding-top:40px}.account-register-container{min-height:70vh;text-align:center !important;align-content:center}.cart-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px;min-width:992px}.register-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px}.order-create-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px;min-width:995px}.cart-product-column{max-width:120px;text-transform:uppercase;vertical-align:middle!important}.order-create-container .cart-product-column{max-width:130px}.cart-product-column-name{width:220px}.cart-subtotal-label{font-size:12px;color:#404040;margin-top:10px}.cart-subtotal-value{font-size:20px;color:#00a69c}.cart-total-label{font-size:14px;color:#404040;margin-top:10px}.cart-total-value{font-size:28px;color:#00a69c;text-align:left}.cart-product-image{max-width:210px}.cart-section-total{margin-bottom:5px;margin-left:175px;text-align:left}.cart-product-column input{width:70px;text-align:center}.cart-refresh-button{margin-top:0;background-image:url('../images/refresh.svg');color:#fff;font-size:8px;width:40px;height:40px;background-color:transparent;border:none;margin-top:25px;margin-left:15px}.cart-refresh-button:hover{background-color:transparent}.cart-totals{border-bottom:none!important}.input-validation-error{border:1px solid #fb0d0d}.text-danger{color:#fb0d0d;font-size:12px}.cart{border:none !important}.form-horizontal h4{margin-top:30px}.form-horizontal .form-group{margin-right:0!important}.form-control:focus{border-color:#83d01b}.form-input-center{margin:auto}.order-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-index-container .table tbody tr{border-bottom:none}.order-index-container .table tbody tr td{border-top:none;padding-top:10px;padding-bottom:10px}.order-index-container .table tbody tr:nth-child(even){background-color:#f5f5f5}.order-create-section-title{margin-left:-15px;text-transform:uppercase}.order-create-section-items{margin-left:-45px;width:102%}.order-detail-button a{color:#83d01b}.order-detail-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-detail-container .table tbody tr:first-child td{border-top:none}.order-detail-container .table tr{border-bottom:none}.order-detail-section{margin-top:50px}.order-detail-container .table{margin-left:-7px}.order-section-total{margin-bottom:5px;margin-left:40px;text-align:left}.fr{float:right!important}.down-arrow{background-image:url('../images/arrow-down.png');height:7px;width:10px;display:inline-block;margin-left:20px}.logout-icon{background-image:url('../images/logout.PNG');display:inline-block;height:19px;width:19px;margin-left:15px}.myorders-icon{background-image:url('../images/my_orders.PNG');display:inline-block;height:20px;width:20px;margin-left:15px}.login-user{position:absolute!important;top:30px;right:65px;cursor:pointer}.login-user-dropdown{position:relative;display:inline-block}.login-user-dropdown-content{display:none;position:absolute;background-color:#fff;min-width:160px;box-shadow:0 8px 16px 0 rgba(0,0,0,.2);right:0}.login-user-dropdown-content a{color:#000;padding:12px 16px;text-decoration:none;display:block;text-align:right;text-transform:uppercase}.login-user:hover .login-user-dropdown-content{display:block}.down-arrow:hover>.login-user-dropdown-content{display:block}.login-user-dropdown-content a:hover{color:#83d01b}.es-header{min-height:80px!important}.es-pager-bottom{margin-top:40px}.es-pager-top{margin-bottom:20px;margin-top:20px}.es-pager-top ul{list-style:none}.es-pager-bottom ul{list-style:none}.page-item{cursor:pointer}.next{position:absolute;right:15px;top:0}.previous{position:absolute;left:0;top:0}.is-disabled{cursor:not-allowed;opacity:.5;pointer-events:none}.table tr{border-bottom:1px solid #ddd}.table th{text-transform:uppercase}.navbar-nav{margin-top:10px;margin-bottom:7.5px;margin-right:-10px;float:right}@media screen and (max-width:1195px){.cart-product-column-name{display:none}}@media screen and (max-width:767px){.carousel-caption{display:none}footer .text{text-align:left;margin-top:-15px}.cart-product-column-brand{display:none}}@media screen and (min-width:992px){.form-input{width:360px;max-width:360px}}@media screen and (max-width:415px){.account-login-container{margin-left:-50px}.page-consent{margin-left:10px;margin-right:80px;padding-right:0;padding-left:0}} \ No newline at end of file diff --git a/src/Services/Identity/Identity.API/wwwroot/images/brand.PNG b/src/Services/Identity/Identity.API/wwwroot/images/brand.PNG index 2afd3dccf..4f7b8a84b 100644 Binary files a/src/Services/Identity/Identity.API/wwwroot/images/brand.PNG and b/src/Services/Identity/Identity.API/wwwroot/images/brand.PNG differ diff --git a/src/Services/Identity/Identity.API/wwwroot/images/brand_dark.PNG b/src/Services/Identity/Identity.API/wwwroot/images/brand_dark.PNG index 44a65364f..04626dcf4 100644 Binary files a/src/Services/Identity/Identity.API/wwwroot/images/brand_dark.PNG and b/src/Services/Identity/Identity.API/wwwroot/images/brand_dark.PNG differ diff --git a/src/Services/Identity/Identity.API/wwwroot/images/logout.PNG b/src/Services/Identity/Identity.API/wwwroot/images/logout.PNG index 9915b9862..d7c4ef140 100644 Binary files a/src/Services/Identity/Identity.API/wwwroot/images/logout.PNG and b/src/Services/Identity/Identity.API/wwwroot/images/logout.PNG differ diff --git a/src/Services/Identity/Identity.API/wwwroot/images/main_footer_text.PNG b/src/Services/Identity/Identity.API/wwwroot/images/main_footer_text.PNG new file mode 100644 index 000000000..d8f90a201 Binary files /dev/null and b/src/Services/Identity/Identity.API/wwwroot/images/main_footer_text.PNG differ diff --git a/src/Services/Identity/Identity.API/wwwroot/images/my_orders.PNG b/src/Services/Identity/Identity.API/wwwroot/images/my_orders.PNG index 145be925b..9ee5f60e7 100644 Binary files a/src/Services/Identity/Identity.API/wwwroot/images/my_orders.PNG and b/src/Services/Identity/Identity.API/wwwroot/images/my_orders.PNG differ diff --git a/src/Services/Ordering/Ordering.API/Extensions/LinqSelectExtensions.cs b/src/Services/Ordering/Ordering.API/Extensions/LinqSelectExtensions.cs new file mode 100644 index 000000000..4efe08c07 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Extensions/LinqSelectExtensions.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Ordering.API.Extensions +{ + public static class LinqSelectExtensions + { + public static IEnumerable> SelectTry(this IEnumerable enumerable, Func selector) + { + foreach (TSource element in enumerable) + { + SelectTryResult returnedValue; + try + { + returnedValue = new SelectTryResult(element, selector(element), null); + } + catch (Exception ex) + { + returnedValue = new SelectTryResult(element, default(TResult), ex); + } + yield return returnedValue; + } + } + + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); + } + + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); + } + + public class SelectTryResult + { + internal SelectTryResult(TSource source, TResult result, Exception exception) + { + Source = source; + Result = result; + CaughtException = exception; + } + + public TSource Source { get; private set; } + public TResult Result { get; private set; } + public Exception CaughtException { get; private set; } + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs b/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs index ebc4778d1..7443024ee 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs @@ -8,40 +8,175 @@ using System.Threading.Tasks; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + using System.Collections.Generic; + using Microsoft.AspNetCore.Hosting; + using System.IO; + using Microsoft.Extensions.Options; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using System; + using global::Ordering.API.Extensions; public class OrderingContextSeed { - public static async Task SeedAsync(IApplicationBuilder applicationBuilder) + public static async Task SeedAsync(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory) { + var log = loggerFactory.CreateLogger("ordering seed"); + var context = (OrderingContext)applicationBuilder .ApplicationServices.GetService(typeof(OrderingContext)); + var settings = applicationBuilder + .ApplicationServices.GetRequiredService>().Value; + + var useCustomizationData = settings.UseCustomizationData; + var contentRootPath = env.ContentRootPath; + + using (context) { context.Database.Migrate(); if (!context.CardTypes.Any()) { - context.CardTypes.Add(CardType.Amex); - context.CardTypes.Add(CardType.Visa); - context.CardTypes.Add(CardType.MasterCard); + context.CardTypes.AddRange(useCustomizationData + ? GetCardTypesFromFile(contentRootPath, log) + : GetPredefinedCardTypes()); await context.SaveChangesAsync(); } if (!context.OrderStatus.Any()) { - context.OrderStatus.Add(OrderStatus.Submitted); - context.OrderStatus.Add(OrderStatus.AwaitingValidation); - context.OrderStatus.Add(OrderStatus.StockConfirmed); - context.OrderStatus.Add(OrderStatus.Paid); - context.OrderStatus.Add(OrderStatus.Shipped); - context.OrderStatus.Add(OrderStatus.Cancelled); + context.OrderStatus.AddRange(useCustomizationData + ? GetOrderStatusFromFile(contentRootPath, log) + : GetPredefinedOrderStatus()); } await context.SaveChangesAsync(); } } + static IEnumerable GetCardTypesFromFile(string contentRootPath, ILogger log) + { + string csvFileCardTypes = Path.Combine(contentRootPath, "Setup", "CardTypes.csv"); + + if (!File.Exists(csvFileCardTypes)) + { + return GetPredefinedCardTypes(); + } + + string[] csvheaders; + try + { + string[] requiredHeaders = { "CardType" }; + csvheaders = GetHeaders(requiredHeaders, csvFileCardTypes); + } + catch (Exception ex) + { + log.LogError(ex.Message); + return GetPredefinedCardTypes(); + } + + int id = 1; + return File.ReadAllLines(csvFileCardTypes) + .Skip(1) // skip header column + .SelectTry(x => CreateCardType(x, ref id)) + .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .Where(x => x != null); + } + + static CardType CreateCardType(string value, ref int id) + { + if (String.IsNullOrEmpty(value)) + { + throw new Exception("Orderstatus is null or empty"); + } + + return new CardType(id++, value.Trim('"').Trim()); + } + + + private static IEnumerable GetPredefinedCardTypes() + { + return new List() + { + CardType.Amex, + CardType.Visa, + CardType.MasterCard + }; + } + + static IEnumerable GetOrderStatusFromFile(string contentRootPath, ILogger log) + { + string csvFileOrderStatus = Path.Combine(contentRootPath, "Setup", "OrderStatus.csv"); + + if (!File.Exists(csvFileOrderStatus)) + { + return GetPredefinedOrderStatus(); + } + + string[] csvheaders; + try + { + string[] requiredHeaders = { "OrderStatus" }; + csvheaders = GetHeaders(requiredHeaders, csvFileOrderStatus); + } + catch (Exception ex) + { + log.LogError(ex.Message); + return GetPredefinedOrderStatus(); + } + + int id = 1; + return File.ReadAllLines(csvFileOrderStatus) + .Skip(1) // skip header row + .SelectTry(x => CreateOrderStatus(x, ref id)) + .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .Where(x => x != null); + } + + static OrderStatus CreateOrderStatus(string value, ref int id) + { + if (String.IsNullOrEmpty(value)) + { + throw new Exception("Orderstatus is null or empty"); + } + + return new OrderStatus(id++, value.Trim('"').Trim().ToLowerInvariant()); + } + + static IEnumerable GetPredefinedOrderStatus() + { + return new List() + { + OrderStatus.Submitted, + OrderStatus.AwaitingValidation, + OrderStatus.StockConfirmed, + OrderStatus.Paid, + OrderStatus.Shipped, + OrderStatus.Cancelled + }; + } + + static string[] GetHeaders(string[] requiredHeaders, string csvfile) + { + string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); + + if (csvheaders.Count() != requiredHeaders.Count()) + { + throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); + } + + foreach (var requiredHeader in requiredHeaders) + { + if (!csvheaders.Contains(requiredHeader)) + { + throw new Exception($"does not contain required header '{requiredHeader}'"); + } + } + + return csvheaders; + } } } diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index 96b4f6627..b59dcbd1a 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -16,6 +16,9 @@ PreserveNewest + + PreserveNewest +
@@ -73,6 +76,9 @@ Always + + PreserveNewest + diff --git a/src/Services/Ordering/Ordering.API/OrderingSettings.cs b/src/Services/Ordering/Ordering.API/OrderingSettings.cs new file mode 100644 index 000000000..8c75ba62d --- /dev/null +++ b/src/Services/Ordering/Ordering.API/OrderingSettings.cs @@ -0,0 +1,7 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API +{ + public class OrderingSettings + { + public bool UseCustomizationData { get; set; } + } +} diff --git a/src/Services/Ordering/Ordering.API/Setup/CardTypes.csv b/src/Services/Ordering/Ordering.API/Setup/CardTypes.csv new file mode 100644 index 000000000..5a610b3e8 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Setup/CardTypes.csv @@ -0,0 +1,5 @@ +CardType +Amex +Visa +MasterCard +Capital One \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv b/src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv new file mode 100644 index 000000000..8a3109887 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv @@ -0,0 +1,7 @@ +OrderStatus +Submitted +AwaitingValidation +StockConfirmed +Paid +Shipped +Cancelled \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 80b7b2abe..d9989f07f 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -87,6 +87,8 @@ ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) ); + services.Configure(Configuration); + services.AddSwaggerGen(options => { options.DescribeAllEnumsAsStrings(); @@ -159,7 +161,7 @@ c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); - WaitForSqlAvailabilityAsync(loggerFactory, app).Wait(); + WaitForSqlAvailabilityAsync(loggerFactory, app, env).Wait(); ConfigureEventBus(app); var integrationEventLogContext = new IntegrationEventLogContext( @@ -200,13 +202,13 @@ } - private async Task WaitForSqlAvailabilityAsync(ILoggerFactory loggerFactory, IApplicationBuilder app, int retries = 0) + private async Task WaitForSqlAvailabilityAsync(ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env, int retries = 0) { var logger = loggerFactory.CreateLogger(nameof(Startup)); var policy = CreatePolicy(retries, logger, nameof(WaitForSqlAvailabilityAsync)); await policy.ExecuteAsync(async () => { - await OrderingContextSeed.SeedAsync(app); + await OrderingContextSeed.SeedAsync(app, env, loggerFactory); }); } diff --git a/src/Services/Ordering/Ordering.API/settings.json b/src/Services/Ordering/Ordering.API/settings.json index 09552377a..d6736bfc2 100644 --- a/src/Services/Ordering/Ordering.API/settings.json +++ b/src/Services/Ordering/Ordering.API/settings.json @@ -1,6 +1,7 @@ { "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", "IdentityUrl": "http://localhost:5105", + "UseCustomizationData": true, "Logging": { "IncludeScopes": false, "LogLevel": { diff --git a/src/Web/WebMVC/AppSettings.cs b/src/Web/WebMVC/AppSettings.cs index b15167647..19c8b9a93 100644 --- a/src/Web/WebMVC/AppSettings.cs +++ b/src/Web/WebMVC/AppSettings.cs @@ -13,6 +13,7 @@ namespace Microsoft.eShopOnContainers.WebMVC public string BasketUrl { get; set; } public string MarketingUrl { get; set; } public Logging Logging { get; set; } + public bool UseCustomizationData { get; set; } } public class Connectionstrings diff --git a/src/Web/WebMVC/Infrastructure/WebContextSeed.cs b/src/Web/WebMVC/Infrastructure/WebContextSeed.cs new file mode 100644 index 000000000..65063e34f --- /dev/null +++ b/src/Web/WebMVC/Infrastructure/WebContextSeed.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.eShopOnContainers.WebMVC; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace WebMVC.Infrastructure +{ + public class WebContextSeed + { + public static void Seed(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + var log = loggerFactory.CreateLogger("WebMVC seed"); + + var settings = (AppSettings)applicationBuilder + .ApplicationServices.GetRequiredService>().Value; + + var useCustomizationData = settings.UseCustomizationData; + var contentRootPath = env.ContentRootPath; + var webroot = env.WebRootPath; + + if (useCustomizationData) + { + GetPreconfiguredImages(contentRootPath, webroot, log); + + GetPreconfiguredCSS(contentRootPath, webroot, log); + } + } + + static void GetPreconfiguredCSS(string contentRootPath, string webroot, ILogger log) + { + try + { + string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css"); + if (!File.Exists(overrideCssFile)) + { + log.LogError($" override css file '{overrideCssFile}' does not exists."); + return; + } + + string destinationFilename = Path.Combine(webroot, "css", "override.css"); + File.Copy(overrideCssFile, destinationFilename, true ); + } + catch (Exception ex) + { + log.LogError($"Exception in method GetPreconfiguredCSS WebMVC. Exception Message={ex.Message}"); + } + } + + static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log) + { + try + { + string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); + if (!File.Exists(imagesZipFile)) + { + log.LogError($" zip file '{imagesZipFile}' does not exists."); + return; + } + + string imagePath = Path.Combine(webroot, "images"); + string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); + + using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in zip.Entries) + { + if (imageFiles.Contains(entry.Name)) + { + string destinationFilename = Path.Combine(imagePath, entry.Name); + if (File.Exists(destinationFilename)) + { + File.Delete(destinationFilename); + } + entry.ExtractToFile(destinationFilename); + } + else + { + log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); + } + } + } + } + catch ( Exception ex ) + { + log.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}"); + } + } + + } + +} diff --git a/src/Web/WebMVC/Setup/images.zip b/src/Web/WebMVC/Setup/images.zip new file mode 100644 index 000000000..531ed26f2 Binary files /dev/null and b/src/Web/WebMVC/Setup/images.zip differ diff --git a/src/Web/WebMVC/Setup/override.css b/src/Web/WebMVC/Setup/override.css new file mode 100644 index 000000000..e8b5bb50f --- /dev/null +++ b/src/Web/WebMVC/Setup/override.css @@ -0,0 +1,3 @@ +.esh-catalog-button { + background-color: #83D01B; /* to override the style of this button ie. to make it red, use background-color: #FF001b; */ +} diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 8147f6720..ef0228604 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -12,6 +12,7 @@ using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using System; using System.IdentityModel.Tokens.Jwt; +using WebMVC.Infrastructure; namespace Microsoft.eShopOnContainers.WebMVC { @@ -129,6 +130,9 @@ namespace Microsoft.eShopOnContainers.WebMVC Scope = { "openid", "profile", "orders", "basket", "marketing" } }; + //Seed Data + WebContextSeed.Seed(app, env, loggerFactory); + //Wait untill identity service is ready on compose. app.UseOpenIdConnectAuthentication(oidcOptions); diff --git a/src/Web/WebMVC/Views/Order/Detail.cshtml b/src/Web/WebMVC/Views/Order/Detail.cshtml index c17ffb2a9..810a59559 100644 --- a/src/Web/WebMVC/Views/Order/Detail.cshtml +++ b/src/Web/WebMVC/Views/Order/Detail.cshtml @@ -68,9 +68,9 @@
@item.ProductName
-
$ @Math.Round(item.UnitPrice, 2)
+
$ @item.UnitPrice.ToString("N2")
@item.Units
-
$ @Math.Round(item.Units * item.UnitPrice, 2)
+
$ @Math.Round(item.Units * item.UnitPrice, 2).ToString("N2")
} diff --git a/src/Web/WebMVC/Views/Order/_OrderItems.cshtml b/src/Web/WebMVC/Views/Order/_OrderItems.cshtml index 8b9cdd534..6feaa9838 100644 --- a/src/Web/WebMVC/Views/Order/_OrderItems.cshtml +++ b/src/Web/WebMVC/Views/Order/_OrderItems.cshtml @@ -20,14 +20,14 @@
- $ @item.UnitPrice + $ @item.UnitPrice.ToString("N2")
@item.Units
-
$ @Math.Round(item.Units * item.UnitPrice, 2)
+
$ @Math.Round(item.Units * item.UnitPrice, 2).ToString("N2")
} @@ -41,7 +41,7 @@
- $ @Model.Total + $ @Model.Total.ToString("N2")
diff --git a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml index 5885291ef..1f0d05320 100644 --- a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml +++ b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml @@ -23,12 +23,12 @@
@item.ProductName
-
$ @item.UnitPrice
+
$ @item.UnitPrice.ToString("N2")
-
$ @Math.Round(item.Quantity * item.UnitPrice, 2)
+
$ @Math.Round(item.Quantity * item.UnitPrice, 2).ToString("N2")
diff --git a/src/Web/WebMVC/Views/Shared/_Layout.cshtml b/src/Web/WebMVC/Views/Shared/_Layout.cshtml index ab1a4e542..678c962e1 100644 --- a/src/Web/WebMVC/Views/Shared/_Layout.cshtml +++ b/src/Web/WebMVC/Views/Shared/_Layout.cshtml @@ -19,12 +19,14 @@ + + @@ -56,7 +58,7 @@
- +
diff --git a/src/Web/WebMVC/WebMVC.csproj b/src/Web/WebMVC/WebMVC.csproj index 2c8e47175..c0cc08da0 100644 --- a/src/Web/WebMVC/WebMVC.csproj +++ b/src/Web/WebMVC/WebMVC.csproj @@ -9,6 +9,24 @@ ..\..\..\docker-compose.dcproj + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + +