Javier Suárez Ruiz 8 years ago
parent
commit
99b26d5387
57 changed files with 10345 additions and 191 deletions
  1. +23
    -0
      add-firewall-docker.ps1
  2. +14
    -11
      docker-compose.override.yml
  3. +2
    -0
      docker-compose.yml
  4. +50
    -50
      eShopOnContainers.sln
  5. +28
    -0
      src/Services/Basket/Basket.API/Auth/Client/enable-token-client.js
  6. +8896
    -0
      src/Services/Basket/Basket.API/Auth/Client/oidc-token-manager.js
  7. +13
    -0
      src/Services/Basket/Basket.API/Auth/Client/popup.html
  8. +35
    -0
      src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs
  9. +23
    -0
      src/Services/Basket/Basket.API/Auth/Server/IdentitySecurityScheme.cs
  10. +7
    -0
      src/Services/Basket/Basket.API/Startup.cs
  11. +1
    -0
      src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
  12. +5
    -3
      src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs
  13. +11
    -16
      src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs
  14. +12
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs
  15. +29
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs
  16. +6
    -4
      src/Services/Ordering/Ordering.API/Startup.cs
  17. +28
    -1
      src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs
  18. +1
    -7
      src/Web/WebMVC/Startup.cs
  19. +1
    -1
      src/Web/WebMVC/wwwroot/css/site.min.css
  20. +32
    -29
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html
  21. +3
    -8
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss
  22. +16
    -12
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.ts
  23. +2
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts
  24. +3
    -1
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts
  25. +8
    -3
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts
  26. +1
    -1
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts
  27. +0
    -9
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts
  28. +8
    -4
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts
  29. +1
    -1
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts
  30. +0
    -9
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts
  31. +43
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html
  32. +85
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss
  33. +27
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts
  34. +14
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts
  35. +27
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts
  36. +2
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html
  37. +3
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.scss
  38. +43
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts
  39. +3
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/identity.model.ts
  40. +6
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/order.model.ts
  41. +19
    -10
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/data.service.ts
  42. +237
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts
  43. +8
    -4
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts
  44. +1
    -1
      src/Web/WebSPA/eShopOnContainers.WebSPA/Program.cs
  45. +5
    -1
      src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js
  46. +3
    -3
      src/Web/WebSPA/eShopOnContainers.WebSPA/package.json
  47. +2
    -2
      src/Web/WebSPA/eShopOnContainers.WebSPA/project.json
  48. +34
    -0
      test/Services/UnitTest/Catalog/CatalogControllertest.cs
  49. +92
    -0
      test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs
  50. +152
    -0
      test/Services/UnitTest/Ordering/Controllers/OrderControllerTest.cs
  51. +19
    -0
      test/Services/UnitTest/Properties/AssemblyInfo.cs
  52. +22
    -0
      test/Services/UnitTest/UnitTest.xproj
  53. +24
    -0
      test/Services/UnitTest/project.json
  54. +50
    -0
      test/Services/UnitTests__/UnitTests/Ordering/OrderControllerTest.cs
  55. +36
    -0
      test/Services/UnitTests__/UnitTests/Properties/AssemblyInfo.cs
  56. +117
    -0
      test/Services/UnitTests__/UnitTests/UnitTests.csproj
  57. +12
    -0
      test/Services/UnitTests__/UnitTests/packages.config

+ 23
- 0
add-firewall-docker.ps1 View File

@ -0,0 +1,23 @@
param([switch]$Elevated)
function Check-Admin {
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Check-Admin) -eq $false) {
if ($elevated)
{
# could not elevate, quit
}
else {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
$reglas = Get-NetFirewallRule -DisplayName 'EshopDocker'
if ($reglas.Length -gt 0)
{
New-NetFirewallRule -DisplayName EshopDocker -Confirm -Description "Eshop on Containers" -LocalAddress Any -LocalPort Any -Protocol tcp -RemoteAddress Any -RemotePort 5100-5105 -Direction Inbound
New-NetFirewallRule -DisplayName EshopDocker -Confirm -Description "Eshop on Containers" -LocalAddress Any -LocalPort Any -Protocol tcp -RemoteAddress Any -RemotePort 5100-5105 -Direction Outbound
}

+ 14
- 11
docker-compose.override.yml View File

@ -14,8 +14,8 @@ services:
- CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api:5102
#- IdentityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105.
#- IdentityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105.
- IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker.
- IdentityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105.
#- IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker.
- BasketUrl=http://basket.api:5103
ports:
- "5100:5100"
@ -24,9 +24,10 @@ services:
environment:
- CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api
#- IdentityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105.
- IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker.
- BasketUrl=http://basket.api:5103
#- IdentityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105.
#- IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker.
- IdentityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105.
- BasketUrl=http://basket.api:5103
ports:
- "5104:80"
@ -34,7 +35,8 @@ services:
environment:
- ConnectionString=basket.data
#- identityUrl=http://13.88.8.119:5105 #Remote
- identityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker.
#- identityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker.
- identityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105.
ports:
- "5103:5103"
@ -47,8 +49,9 @@ services:
ordering.api:
environment:
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.service:5105 #local
#- identityUrl=http://13.88.8.119:5105 #remote
#- identityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105.
#- identityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker.
- identityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105.
ports:
- "5102:5102"
@ -56,9 +59,9 @@ services:
environment:
- SpaClient=http://localhost:5104
- ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word
#- MvcClient=http://13.88.8.119:5100 #Remote: VM Needs to have public access at 5105.
- MvcClient=http://localhost:5100 #Local: You need a entry in windows host file to run identity in local docker.
#10.0.75.1:5105 CCE/TODO: try to avoid host entry.
#- MvcClient=http://13.88.8.119:5100 #Remote: VM Needs to have public access at 5105.
#- MvcClient=http://localhost:5100 #Local: You need a entry in windows host file to run identity in local docker.
- MvcClient=http://10.0.75.1:5100 #Local: You need to open windows firewall at range 5100-5105.
ports:
- "5105:5105"


+ 2
- 0
docker-compose.yml View File

@ -47,3 +47,5 @@ services:
basket.data:
image: redis
ports:
- "6379:6379"

+ 50
- 50
eShopOnContainers.sln View File

@ -73,7 +73,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.UITests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.Core", "src\Mobile\eShopOnContainers\eShopOnContainers.Core\eShopOnContainers.Core.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ordering.Application", "src\Services\Ordering\Ordering.Application\Ordering.Application.xproj", "{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UnitTest", "test\Services\UnitTest\UnitTest.xproj", "{7796F5D8-31FC-45A4-B673-19DE5BA194CF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -908,54 +908,54 @@ Global
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x64.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x86.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x86.Build.0 = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|ARM.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhone.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x64.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x64.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x86.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x86.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|ARM.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhone.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x64.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x64.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x86.ActiveCfg = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x86.Build.0 = Debug|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|Any CPU.Build.0 = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|ARM.ActiveCfg = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|ARM.Build.0 = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhone.ActiveCfg = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhone.Build.0 = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x64.ActiveCfg = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x64.Build.0 = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x86.ActiveCfg = Release|Any CPU
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x86.Build.0 = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.ActiveCfg = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.Build.0 = Debug|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.Build.0 = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.ActiveCfg = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.Build.0 = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.ActiveCfg = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.Build.0 = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.ActiveCfg = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.Build.0 = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.ActiveCfg = Release|Any CPU
{7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -990,6 +990,6 @@ Global
{621E7211-58D0-45FD-9600-1CB490BD930E} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
{E3B18084-842C-4B80-8E4A-A7E588EC3137} = {B7B1D395-4E06-4036-BE86-C216756B9367}
{67F9D3A8-F71E-4428-913F-C37AE82CDB24} = {778289CA-31F7-4464-8C2A-612EE846F8A7}
{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B}
{7796F5D8-31FC-45A4-B673-19DE5BA194CF} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
EndGlobalSection
EndGlobal

+ 28
- 0
src/Services/Basket/Basket.API/Auth/Client/enable-token-client.js View File

@ -0,0 +1,28 @@
(function ($, swaggerUi) {
$(function () {
var settings = {
authority: 'https://localhost:5105',
client_id: 'js',
popup_redirect_uri: window.location.protocol
+ '//'
+ window.location.host
+ '/tokenclient/popup.html',
response_type: 'id_token token',
scope: 'openid profile basket',
filter_protocol_claims: true
},
manager = new OidcTokenManager(settings),
$inputApiKey = $('#input_apiKey');
$inputApiKey.on('dblclick', function () {
manager.openPopupForTokenAsync()
.then(function () {
$inputApiKey.val(manager.access_token).change();
}, function (error) {
console.error(error);
});
});
});
})(jQuery, window.swaggerUi);

+ 8896
- 0
src/Services/Basket/Basket.API/Auth/Client/oidc-token-manager.js
File diff suppressed because it is too large
View File


+ 13
- 0
src/Services/Basket/Basket.API/Auth/Client/popup.html View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
</head>
<body>
<script type="text/javascript" src="oidc-token-manager.min.js"></script>
<script type="text/javascript">
new OidcTokenManager().processTokenPopup();
</script>
</body>
</html>

+ 35
- 0
src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs View File

@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc.Authorization;
using Swashbuckle.Swagger.Model;
using Swashbuckle.SwaggerGen.Generator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server
{
public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
if (isAuthorized && !allowAnonymous)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
operation.Parameters.Add(new NonBodyParameter
{
Name = "Authorization",
In = "header",
Description = "access token",
Required = true,
Type = "string"
});
}
}
}
}

+ 23
- 0
src/Services/Basket/Basket.API/Auth/Server/IdentitySecurityScheme.cs View File

@ -0,0 +1,23 @@
using Swashbuckle.Swagger.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server
{
public class IdentitySecurityScheme:SecurityScheme
{
public IdentitySecurityScheme()
{
Type = "IdentitySecurityScheme";
Description = "Security definition that provides to the user of Swagger a mechanism to obtain a token from the identity service that secures the api";
Extensions.Add("authorizationUrl", "http://localhost:5103/Auth/Client/popup.html");
Extensions.Add("flow", "implicit");
Extensions.Add("scopes", new List<string>
{
"basket"
});
}
}
}

+ 7
- 0
src/Services/Basket/Basket.API/Startup.cs View File

@ -11,6 +11,8 @@ using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using StackExchange.Redis;
using Microsoft.Extensions.Options;
using System.Net;
using Swashbuckle.Swagger.Model;
using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server;
namespace Microsoft.eShopOnContainers.Services.Basket.API
{
@ -48,8 +50,11 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
});
services.AddSwaggerGen();
//var sch = new IdentitySecurityScheme();
services.ConfigureSwaggerGen(options =>
{
//options.AddSecurityDefinition("IdentityServer", sch);
options.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
options.DescribeAllEnumsAsStrings();
options.SingleApiVersion(new Swashbuckle.Swagger.Model.Info()
{
@ -79,6 +84,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
// Use frameworks
app.UseCors("CorsPolicy");


+ 1
- 0
src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs View File

@ -4,6 +4,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Model;
using System;
using System.Linq;
using System.Threading.Tasks;


+ 5
- 3
src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs View File

@ -41,8 +41,8 @@ namespace eShopOnContainers.Identity.Configuration
ClientName = "eShop SPA OpenId Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["Spa"]}/callback.html" },
PostLogoutRedirectUris = { $"{clientsUrl["Spa"]}/index.html" },
RedirectUris = { $"{clientsUrl["Spa"]}/" },
PostLogoutRedirectUris = { $"{clientsUrl["Spa"]}/" },
AllowedCorsOrigins = { $"{clientsUrl["Spa"]}" },
AllowedScopes =
{
@ -82,7 +82,9 @@ namespace eShopOnContainers.Identity.Configuration
RedirectUris = new List<string>
{
$"{clientsUrl["Mvc"]}/signin-oidc",
"http://104.40.62.65:5100/signin-oidc"
"http://104.40.62.65:5100/signin-oidc",
"http://localhost:5100/signin-oidc",
"http://13.88.8.119:5100/signin-oidc"
},
PostLogoutRedirectUris = new List<string>
{


+ 11
- 16
src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs View File

@ -3,6 +3,7 @@
using Api.Application.Commands;
using Api.Application.Queries;
using AspNetCore.Authorization;
using Infrastructure.Services;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Models;
@ -11,13 +12,14 @@
using System.Threading.Tasks;
[Route("api/v1/[controller]")]
//[Authorize]
[Authorize]
public class OrdersController : Controller
{
private readonly IMediator _mediator;
private readonly IOrderQueries _orderQueries;
private readonly IIdentityService _identityService;
public OrdersController(IMediator mediator, IOrderQueries orderQueries)
public OrdersController(IMediator mediator, IOrderQueries orderQueries, IIdentityService identityService)
{
if (mediator == null)
{
@ -29,21 +31,24 @@
throw new ArgumentNullException(nameof(orderQueries));
}
if (identityService == null)
{
throw new ArgumentException(nameof(identityService));
}
_mediator = mediator;
_orderQueries = orderQueries;
_identityService = identityService;
}
[Route("new")]
[HttpPost]
public async Task<IActionResult> AddOrder([FromBody]NewOrderRequest order)
{
if (order.CardExpiration == DateTime.MinValue)
order.CardExpiration = DateTime.Now.AddYears(5);
if (order.CardTypeId == 0)
order.CardTypeId = 1;
order.Buyer = GetUserName();
order.Buyer = _identityService.GetUserIdentity();
var added = await _mediator.SendAsync(order);
if (added)
@ -86,17 +91,7 @@
return Ok(cardTypes);
}
/// <summary>
/// Returns the GUID corresponding to the Id of the authenticated user.
/// </summary>
/// <returns>GUID (string)</returns>
string GetUserName()
{
return HttpContext.User.FindFirst("sub").Value;
}
}
}

+ 12
- 0
src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services
{
public interface IIdentityService
{
string GetUserIdentity();
}
}

+ 29
- 0
src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs View File

@ -0,0 +1,29 @@

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services
{
public class IdentityService : IIdentityService
{
private IHttpContextAccessor _context;
public IdentityService(IHttpContextAccessor context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_context = context;
}
public string GetUserIdentity()
{
return _context.HttpContext.User.FindFirst("sub").Value;
}
}
}

+ 6
- 4
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -1,11 +1,12 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API
{
using AspNetCore.Http;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Infrastructure;
using Infrastructure.AutofacModules;
using Infrastructure.Filters;
using MediatR;
using Infrastructure.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
@ -15,8 +16,6 @@
using Ordering.Infrastructure;
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
public class Startup
{
@ -64,7 +63,7 @@
Title = "Ordering HTTP API",
Version = "v1",
Description = "The Ordering Service HTTP API",
TermsOfService = "Terms Of Service"
TermsOfService = "Terms Of Service"
});
});
@ -77,7 +76,10 @@
.AllowCredentials());
});
// Add application services.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IConfiguration>(this.Configuration);
services.AddTransient<IIdentityService,IdentityService>();
services.AddOptions();


+ 28
- 1
src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs View File

@ -7,9 +7,10 @@
using System;
using System.Dynamic;
using System.Collections.Generic;
using System.Linq;
public class OrderQueries
:IOrderQueries
: IOrderQueries
{
private string _connectionString = string.Empty;
@ -97,5 +98,31 @@
return order;
}
//TODO/CCE: try to use this method instead actual.
//private object MapOrderItems(dynamic result)
//{
// IEnumerable<dynamic> items = (result as System.Collections.IEnumerable).Cast<dynamic>();
// var order = new
// {
// ordernumber = result[0].ordernumbe,
// date = result[0].date,
// status = result[0].status,
// street = result[0].street,
// city = result[0].city,
// zipcode = result[0].zipcode,
// country = result[0].country,
// total = items.Select(r => (int)r.units * (int)r.unitprice).Sum(),
// orderItems = items.Select(r => new
// {
// productname = r.productname,
// units = r.units,
// unitprice = r.unitprice,
// pictureurl = r.pictureurl
// })
// };
// return order;
//}
}
}

+ 1
- 7
src/Web/WebMVC/Startup.cs View File

@ -33,9 +33,6 @@ namespace Microsoft.eShopOnContainers.WebMVC
// builder.AddUserSecrets();
//}
//builder.AddJsonFile("appsettings.override.json");
//builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
@ -51,7 +48,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<ICatalogService, CatalogService>();
services.AddSingleton<IOrderingService, OrderingService>(); //CCE: Once services are integrated, a singleton is not needed we can left transient.
services.AddTransient<IOrderingService, OrderingService>();
services.AddTransient<IBasketService, BasketService>();
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
}
@ -86,9 +83,6 @@ namespace Microsoft.eShopOnContainers.WebMVC
var callBackUrl = Configuration.GetValue<string>("CallBackUrl");
var log = loggerFactory.CreateLogger("identity");
log.LogDebug(identityUrl.ToString());
log.LogDebug(callBackUrl.ToString());
var oidcOptions = new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",


+ 1
- 1
src/Web/WebMVC/wwwroot/css/site.min.css
File diff suppressed because it is too large
View File


+ 32
- 29
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html View File

@ -1,41 +1,44 @@
<header class="navbar navbar-light navbar-static-top">
<div class="container">
<div class="row">
<div class="col-sm-10">
<a class="navbar-brand" routerLink="catalog">
<img src="../images/brand.png" />
</a>
</div>
<div class="col-sm-2">
<esh-basket-status></esh-basket-status>
<div class="app">
<header class="navbar navbar-light navbar-static-top">
<div class="container">
<div class="row">
<div class="col-sm-10">
<a class="navbar-brand" routerLink="catalog">
<img src="../images/brand.png" />
</a>
</div>
<div class="col-sm-2">
<esh-basket-status *ngIf="Authenticated"></esh-basket-status>
<esh-identity *ngIf="!Authenticated"></esh-identity>
</div>
</div>
</div>
</div>
</header>
</header>
<!-- component routing placeholder -->
<router-outlet></router-outlet>
<!-- component routing placeholder -->
<router-outlet></router-outlet>
<footer class="app-footer">
<div class="container">
<div class="row">
<div class="col-sm-6">
<br>
<div class="app-footer-brand">
<img src="../images/brand_dark.png" />
<footer class="app-footer">
<div class="container">
<div class="row">
<div class="col-sm-6">
<br>
<div class="app-footer-brand">
<img src="../images/brand_dark.png" />
</div>
</div>
<div class="col-sm-6">
<br>
<br>
<br>
<div class="app-footer-text hidden-xs">e-ShoponContainers. All right reserved</div>
</div>
</div>
<div class="col-sm-6">
<br>
<br>
<br>
<div class="app-footer-text hidden-xs">e-ShoponContainers. All right reserved</div>
</div>
</div>
</div>
</footer>
</footer>
</div>

+ 3
- 8
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss View File

@ -3,23 +3,18 @@
@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")
src: url("../dist/fonts/Montserrat-Regular.eot?") format("eot"),url("../dist/fonts/Montserrat-Regular.woff") format("woff"),url("../dist/fonts/Montserrat-Regular.ttf") format("truetype"),url("../dist/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")
src: url("../dist/fonts/Montserrat-Bold.eot?") format("eot"),url("../dist/fonts/Montserrat-Bold.woff") format("woff"),url("../dist/fonts/Montserrat-Bold.ttf") format("truetype"),url("../dist/fonts/Montserrat-Bold.svg#Montserrat") format("svg")
}
body {
padding-top: 80px;
/*padding-bottom: 20px;*/
.app {
font-family: Montserrat,sans-serif;
min-width:480px;
}
.app {
&-footer {
padding-top: 40px;
padding-bottom: 40px;


+ 16
- 12
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.ts View File

@ -1,8 +1,10 @@
import { Title } from '@angular/platform-browser';
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { RouterModule } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { DataService } from './shared/services/data.service';
import { SecurityService } from './shared/services/security.service';
/*
* App Component
@ -10,22 +12,24 @@ import { DataService } from './shared/services/data.service';
*/
@Component({
selector: 'appc-app',
styleUrls: ['./app.component.scss'],
templateUrl: './app.component.html'
selector: 'appc-app',
styleUrls: ['./app.component.scss'],
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
private Authenticated: boolean = false;
subscription: Subscription;
constructor(private titleService: Title, private securityService: SecurityService) {
constructor(private titleService: Title) {
}
}
ngOnInit() {
console.log('app on init');
this.subscription = this.securityService.authenticationChallenge$.subscribe(res => this.Authenticated = res);
}
ngOnInit() {
}
public setTitle(newTitle: string) {
this.titleService.setTitle('eShopOnContainers');
}
public setTitle(newTitle: string) {
this.titleService.setTitle('eShopOnContainers');
}
}

+ 2
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts View File

@ -9,6 +9,7 @@ import { AppService } from './app.service';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';
import { CatalogModule } from './catalog/catalog.module';
import { OrdersModule } from './orders/orders.module';
import { BasketModule } from './basket/basket.module';
@NgModule({
@ -20,6 +21,7 @@ import { BasketModule } from './basket/basket.module';
// Only module that app module loads
SharedModule.forRoot(),
CatalogModule,
OrdersModule,
BasketModule
],
providers: [


+ 3
- 1
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts View File

@ -2,11 +2,13 @@ import { Routes, RouterModule } from '@angular/router';
import { BasketComponent } from './basket/basket.component';
import { CatalogComponent } from './catalog/catalog.component';
import { OrdersComponent } from './orders/orders.component';
export const routes: Routes = [
{ path: '', redirectTo: 'catalog', pathMatch: 'full' },
{ path: 'basket', component: BasketComponent },
{ path: 'catalog', component: CatalogComponent }
{ path: 'catalog', component: CatalogComponent },
{ path: 'orders', component: OrdersComponent }
//Lazy async modules (angular-loader-router) and enable a router in each module.
//{
// path: 'basket', loadChildren: '/basket/basket.module' });


+ 8
- 3
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts View File

@ -19,10 +19,15 @@ export class BasketStatusComponent implements OnInit {
ngOnInit() {
this.subscription = this.basketEvents.addItemToBasket$.subscribe(
item => {
console.log('element received in basket');
console.log(item);
this.service.setBasket(item);
this.service.getBasket().subscribe(basket => {
this.badge = basket.items.length;
this.service.setBasket(item).subscribe(res => {
console.log(res);
this.service.getBasket().subscribe(basket => {
this.badge = basket.items.length;
console.log('response from basket api');
console.log(basket.items.length);
});
});
});
}


+ 1
- 1
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts View File

@ -5,7 +5,7 @@ import { IBasket } from '../shared/models/basket.model';
import { IBasketItem } from '../shared/models/basketItem.model';
@Component({
selector: 'esh-basket',
selector: 'esh-basket .esh-basket',
styleUrls: ['./basket.component.scss'],
templateUrl: './basket.component.html'
})


+ 0
- 9
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts View File

@ -1,9 +0,0 @@
//import { Routes, RouterModule } from '@angular/router';
//import { BasketComponent } from './basket.component';
//const routes: Routes = [
// { path: '', component: BasketComponent }
//];
//export const routing = RouterModule.forChild(routes);

+ 8
- 4
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts View File

@ -1,7 +1,8 @@
import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { Response, Headers } from '@angular/http';
import { DataService } from '../shared/services/data.service';
import { SecurityService } from '../shared/services/security.service';
import { IBasket } from '../shared/models/basket.model';
import { IBasketItem } from '../shared/models/basketItem.model';
@ -19,13 +20,16 @@ export class BasketService {
items: []
};
constructor(private service: DataService) {
constructor(private service: DataService, private authService: SecurityService) {
this.basket.items = [];
}
setBasket(item) {
setBasket(item): Observable<boolean> {
console.log('set basket');
this.basket.items.push(item);
this.service.post(this.basket.buyerId, this.basket.items);
return this.service.post(this.basketUrl + '/', this.basket).map((response: Response) => {
return true;
});
}
getBasket(): Observable<IBasket> {


+ 1
- 1
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts View File

@ -8,7 +8,7 @@ import { IPager } from '../shared/models/pager.model';
import { BasketWrapperService} from '../shared/services/basket.wrapper.service';
@Component({
selector: 'esh-catalog',
selector: 'esh-catalog .catalog',
styleUrls: ['./catalog.component.scss'],
templateUrl: './catalog.component.html'
})


+ 0
- 9
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts View File

@ -1,9 +0,0 @@
//import { Routes, RouterModule } from '@angular/router';
//import { CatalogComponent } from './catalog.component';
//const routes: Routes = [
// { path: '', component: CatalogComponent }
//];
//export const routing = RouterModule.forChild(routes);

+ 43
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html View File

@ -0,0 +1,43 @@
<div class="esh-orders-header">
<ul class="container">
<li class="esh-orders-header-back" routerLink="/catalog">Back to list</li>
</ul>
</div>
<div class="container esh-orders-container">
<div class="row">
<div class="col-md-12">
<section>
<table class="table">
<thead>
<tr>
<th class="esh-orders-order-column">
ORDER NUMBER
</th>
<th>
DATE
</th>
<th>
TOTAL
</th>
<th>
STATUS
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let order of orders">
<td class="esh-orders-order-column">{{order.ordernumber}}</td>
<td class="esh-orders-order-column">{{order.date | date:'short'}}</td>
<td class="esh-orders-order-column">$ {{order.total}}</td>
<td class="esh-orders-order-column">{{order.status}}</td>
<td class="esh-orders-order-column">
<a class="esh-orders-order-link" href="#">Detail</a>
</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
</div>

+ 85
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss View File

@ -0,0 +1,85 @@
@import '../_variables.scss';
.esh-orders {
min-height: 80vh;
&-header {
background-color: #00A69C;
height: 63px;
li {
list-style: none;
display: inline;
opacity: 0.5;
margin-top: 25px;
margin-left: 10px;
float: right;
cursor: pointer;
color: white;
}
li a {
color: white;
}
&-back {
float: left !important;
margin-top: 20px !important;
text-transform: uppercase;
}
li a:hover {
text-decoration: none;
}
}
&-container {
min-height: 70vh;
padding-top: 40px;
margin-bottom: 30px;
min-width: 992px;
}
&-order-column {
max-width: 120px;
vertical-align: middle !important;
}
&-order-link {
color: #83d01b;
}
&-order-image {
max-width: 210px;
}
&-total-value {
font-size: 20px;
color: #00a69c;
}
&-total-label {
font-size: 14px;
color: #404040;
margin-top: 10px;
}
&-totals {
border-bottom:none!important;
}
}
.table td {
border-top: solid 1px #ddd;
}
.table thead th {
border: none !important;
}

+ 27
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts View File

@ -0,0 +1,27 @@
import { Component, OnInit } from '@angular/core';
import { OrdersService } from './orders.service';
import { IOrder } from '../shared/models/order.model';
@Component({
selector: 'esh-orders .orders',
styleUrls: ['./orders.component.scss'],
templateUrl: './orders.component.html'
})
export class OrdersComponent implements OnInit {
orders: IOrder[];
constructor(private service: OrdersService) { }
ngOnInit() {
this.getOrders();
}
getOrders() {
this.service.getOrders().subscribe(orders => {
this.orders = orders;
console.log('orders items retrieved: ' + orders.length);
});
}
}

+ 14
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts View File

@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SharedModule } from '../shared/shared.module';
import { OrdersComponent } from './orders.component';
import { OrdersService } from './orders.service';
import { Pager } from '../shared/components/pager/pager';
@NgModule({
imports: [BrowserModule, SharedModule],
declarations: [OrdersComponent],
providers: [OrdersService]
})
export class OrdersModule { }

+ 27
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts View File

@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { DataService } from '../shared/services/data.service';
import { IOrder } from '../shared/models/order.model';
import 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import { Observer } from 'rxjs/Observer';
import 'rxjs/add/operator/map';
@Injectable()
export class OrdersService {
private ordersUrl: string = 'http://eshopcontainers:5102/api/v1/orders';
constructor(private service: DataService) {
}
getOrders(): Observable<any> {
var url = this.ordersUrl;
return this.service.get(url).map((response: Response) => {
return response.json();
});
}
}

+ 2
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html View File

@ -0,0 +1,2 @@
<button *ngIf="!authenticated" (click)="login()">Login</button>
<div *ngIf="authenticated">userName: {{userName}}</div>

+ 3
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.scss View File

@ -0,0 +1,3 @@
.identity {
}

+ 43
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts View File

@ -0,0 +1,43 @@
import { Component, OnInit, OnChanges, Output, Input, EventEmitter } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { IIdentity } from '../../models/identity.model';
import { SecurityService } from '../../services/security.service';
@Component({
selector: 'esh-identity',
templateUrl: './identity.html',
styleUrls: ['./identity.scss']
})
export class Identity implements OnInit {
private authenticated: boolean = false;
private subscription: Subscription;
private userName: string = "";
constructor(private service: SecurityService) {
}
ngOnInit() {
this.subscription = this.service.authenticationChallenge$.subscribe(res =>
{
//console.log(res);
//console.log(this.service.UserData);
//console.log(this.service);
this.authenticated = res;
this.userName = this.service.UserData.email;
});
if (window.location.hash) {
this.service.AuthorizedCallback();
}
}
login() {
this.service.Authorize();
}
logout() {
this.service.Logoff();
}
}

+ 3
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/identity.model.ts View File

@ -0,0 +1,3 @@
export interface IIdentity {
}

+ 6
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/order.model.ts View File

@ -0,0 +1,6 @@
export interface IOrder {
ordernumber: number,
date: Date,
status: string,
total: number
}

+ 19
- 10
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/data.service.ts View File

@ -8,15 +8,19 @@ import { Observer } from 'rxjs/Observer';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { SecurityService } from './security.service';
@Injectable()
export class DataService {
constructor(private http: Http) { }
constructor(private http: Http, private securityService: SecurityService) { }
get(url: string, params?: any): Observable<Response> {
let options: RequestOptionsArgs = {};
options.headers = new Headers();
this.addCors(options);
if (this.securityService) {
options.headers = new Headers();
options.headers.append("Authorization", "Bearer " + this.securityService.GetToken());
}
return this.http.get(url, options).map(
(res: Response) => {
@ -24,13 +28,18 @@ export class DataService {
}).catch(this.handleError);
}
post(url: string, data: any, params?: any) {
return this.http.post(url, data, params);
}
post(url: string, data: any, params?: any): Observable<Response> {
let options: RequestOptionsArgs = {};
if (this.securityService) {
options.headers = new Headers();
options.headers.append("Authorization", "Bearer " + this.securityService.GetToken());
}
private addCors(options: RequestOptionsArgs): RequestOptionsArgs {
options.headers.append('Access-Control-Allow-Origin', '*');
return options;
return this.http.post(url, data, options).map(
(res: Response) => {
return res;
}).catch(this.handleError);
}
private handleError(error: any) {


+ 237
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts View File

@ -0,0 +1,237 @@
import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
//import { Configuration } from '../app.constants';
import { Router } from '@angular/router';
@Injectable()
export class SecurityService {
private actionUrl: string;
private headers: Headers;
private storage: any;
private authenticationSource = new Subject<boolean>();
authenticationChallenge$ = this.authenticationSource.asObservable();
constructor(private _http: Http, private _router: Router) {
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept', 'application/json');
this.storage = sessionStorage; //localStorage;
if (this.retrieve("IsAuthorized") !== "") {
this.IsAuthorized = this.retrieve("IsAuthorized");
}
}
public IsAuthorized: boolean;
public GetToken(): any {
return this.retrieve("authorizationData");
}
public ResetAuthorizationData() {
this.store("authorizationData", "");
this.store("authorizationDataIdToken", "");
this.IsAuthorized = false;
this.store("IsAuthorized", false);
}
public UserData: any;
public SetAuthorizationData(token: any, id_token:any) {
if (this.retrieve("authorizationData") !== "") {
this.store("authorizationData", "");
}
this.store("authorizationData", token);
this.store("authorizationDataIdToken", id_token);
this.IsAuthorized = true;
this.store("IsAuthorized", true);
this.getUserData()
.subscribe(data => {
this.UserData = data;
//emit observable
this.authenticationSource.next(true);
},
error => this.HandleError(error),
() => {
console.log(this.UserData);
});
}
public Authorize() {
this.ResetAuthorizationData();
var authorizationUrl = 'http://localhost:5105/connect/authorize';
var client_id = 'js';
var redirect_uri = 'http://localhost:5104/';
var response_type = "id_token token";
var scope = "openid profile orders basket";
var nonce = "N" + Math.random() + "" + Date.now();
var state = Date.now() + "" + Math.random();
this.store("authStateControl", state);
this.store("authNonce", nonce);
var url =
authorizationUrl + "?" +
"response_type=" + encodeURI(response_type) + "&" +
"client_id=" + encodeURI(client_id) + "&" +
"redirect_uri=" + encodeURI(redirect_uri) + "&" +
"scope=" + encodeURI(scope) + "&" +
"nonce=" + encodeURI(nonce) + "&" +
"state=" + encodeURI(state);
window.location.href = url;
}
public AuthorizedCallback() {
this.ResetAuthorizationData();
var hash = window.location.hash.substr(1);
var result: any = hash.split('&').reduce(function (result : any, item: string) {
var parts = item.split('=');
result[parts[0]] = parts[1];
return result;
}, {});
console.log(result);
var token = "";
var id_token = "";
var authResponseIsValid = false;
if (!result.error) {
if (result.state !== this.retrieve("authStateControl")) {
console.log("AuthorizedCallback incorrect state");
} else {
token = result.access_token;
id_token = result.id_token
var dataIdToken: any = this.getDataFromToken(id_token);
console.log(dataIdToken);
// validate nonce
if (dataIdToken.nonce !== this.retrieve("authNonce")) {
console.log("AuthorizedCallback incorrect nonce");
} else {
this.store("authNonce", "");
this.store("authStateControl", "");
authResponseIsValid = true;
console.log("AuthorizedCallback state and nonce validated, returning access token");
}
}
}
if (authResponseIsValid) {
this.SetAuthorizationData(token, id_token);
console.log(this.retrieve("authorizationData"));
// router navigate to DataEventRecordsList
this._router.navigate(['/dataeventrecords/list']);
}
else {
this.ResetAuthorizationData();
this._router.navigate(['/Unauthorized']);
}
}
public Logoff() {
// /connect/endsession?id_token_hint=...&post_logout_redirect_uri=https://myapp.com
console.log("BEGIN Authorize, no auth data");
var authorizationUrl = 'http://localhost:5105/connect/endsession';
console.log(this.retrieve("authorizationDataIdToken"));
var id_token_hint = this.retrieve("authorizationDataIdToken");
var post_logout_redirect_uri = 'http://localhost:5104/';
var url =
authorizationUrl + "?" +
"id_token_hint=" + encodeURI(id_token_hint) + "&" +
"post_logout_redirect_uri=" + encodeURI(post_logout_redirect_uri);
this.ResetAuthorizationData();
window.location.href = url;
}
public HandleError(error: any) {
console.log(error);
if (error.status == 403) {
this._router.navigate(['/Forbidden'])
}
else if (error.status == 401) {
this.ResetAuthorizationData();
this._router.navigate(['/Unauthorized'])
}
}
private urlBase64Decode(str: string) {
var output = str.replace('-', '+').replace('_', '/');
switch (output.length % 4) {
case 0:
break;
case 2:
output += '==';
break;
case 3:
output += '=';
break;
default:
throw 'Illegal base64url string!';
}
return window.atob(output);
}
private getDataFromToken(token: any) {
var data = {};
if (typeof token !== 'undefined') {
var encoded = token.split('.')[1];
data = JSON.parse(this.urlBase64Decode(encoded));
}
return data;
}
private retrieve(key: string): any {
var item = this.storage.getItem(key);
if (item && item !== 'undefined') {
return JSON.parse(this.storage.getItem(key));
}
return;
}
private store(key: string, value: any) {
this.storage.setItem(key, JSON.stringify(value));
}
private getUserData = (): Observable<string[]> => {
this.setHeaders();
return this._http.get('http://localhost:5105/connect/userinfo', {
headers: this.headers,
body: ''
}).map(res => res.json());
}
private setHeaders() {
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept', 'application/json');
var token = this.GetToken();
if (token !== "") {
this.headers.append('Authorization', 'Bearer ' + token);
}
}
}

+ 8
- 4
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts View File

@ -17,9 +17,11 @@ import { DataService } from './services/data.service';
import { UtilityService } from './services/utility.service';
import { UppercasePipe } from './pipes/uppercase.pipe';
import { BasketWrapperService} from './services/basket.wrapper.service';
import { SecurityService } from './services/security.service';
//Components:
import {Pager } from './components/pager/pager';
import {Identity } from './components/identity/identity';
@NgModule({
imports: [
@ -39,7 +41,8 @@ import {Pager } from './components/pager/pager';
ErrorSummaryComponent,
PageHeadingComponent,
UppercasePipe,
Pager
Pager,
Identity
],
exports: [
// Modules
@ -57,9 +60,9 @@ import {Pager } from './components/pager/pager';
//HeaderComponent,
PageHeadingComponent,
UppercasePipe,
Pager
Pager,
Identity
]
})
export class SharedModule {
static forRoot(): ModuleWithProviders {
@ -70,7 +73,8 @@ export class SharedModule {
DataService,
FormControlService,
UtilityService,
BasketWrapperService
BasketWrapperService,
SecurityService
]
};
}


+ 1
- 1
src/Web/WebSPA/eShopOnContainers.WebSPA/Program.cs View File

@ -17,8 +17,8 @@ namespace eShopConContainers.WebSPA
.UseKestrel()
.UseConfiguration(config)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseUrls("http://localhost:5104/")
.UseIISIntegration()
//.UseUrls("http://localhost:5104/")
.UseStartup<Startup>()
.Build();


+ 5
- 1
src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js View File

@ -6,6 +6,7 @@ var extractCSS = new ExtractTextPlugin('styles.css');
var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;
var devConfig = require('./webpack.config.dev');
var prodConfig = require('./webpack.config.prod');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Development';
console.log("==========Dev Mode = " + isDevelopment + " ============" )
@ -74,6 +75,9 @@ module.exports = merge({
'process.env': {
'ENV': JSON.stringify(process.env.ASPNETCORE_ENVIRONMENT)
}
})
}),
new CopyWebpackPlugin([
{ from: 'Client/fonts', to: 'fonts' }
])
]
}, isDevelopment ? devConfig : prodConfig);

+ 3
- 3
src/Web/WebSPA/eShopOnContainers.WebSPA/package.json View File

@ -8,7 +8,7 @@
"angular2",
"webpack2",
"typescript2",
"bootstrap4",
"bootstrap4",
"docker"
],
"author": {
@ -71,11 +71,11 @@
"@types/source-map": "0.1.28",
"@types/uglify-js": "2.6.28",
"@types/webpack": "1.12.35",
"angular2-template-loader": "0.6.0",
"angular2-router-loader": "0.3.4",
"angular2-template-loader": "0.6.0",
"awesome-typescript-loader": "2.2.4",
"codelyzer": "1.0.0-beta.3",
"copy-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^4.0.1",
"css": "2.2.1",
"css-loader": "0.25.0",
"es6-promise": "3.2.1",


+ 2
- 2
src/Web/WebSPA/eShopOnContainers.WebSPA/project.json View File

@ -39,8 +39,8 @@
},
"tools": {
"Microsoft.DotNet.Watcher.Tools": {
"version": "1.0.0-*",
"imports": "portable-net451+win8"
"version": "1.0.0-preview2-final",
"imports": "portable-net451+win8+dnxcore50"
},
"Microsoft.AspNetCore.Razor.Tools": {
"version": "1.0.0-preview2-final",


+ 34
- 0
test/Services/UnitTest/Catalog/CatalogControllertest.cs View File

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Catalog.API.Controllers;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.eShopOnContainers.Services.Catalog.API.Model;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace UnitTest.Catalog
{
public class CatalogControllerTest
{
private readonly Mock<CatalogContext> _mockContext;
private readonly Mock<IQueryable<CatalogItem>> _mockItems;
public CatalogControllerTest()
{
_mockContext = new Mock<CatalogContext>();
_mockItems = new Mock<IQueryable<CatalogItem>>();
}
[Fact]
public async Task Items_ReturnsOKObject_WhenItemsFound()
{
//CCE: TODO
Assert.True(true);
}
}
}

+ 92
- 0
test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs View File

@ -0,0 +1,92 @@
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Api.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Repositories;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace UnitTest.Ordering.Application
{
public class NewOrderRequestHandlerTest
{
private readonly Mock<IBuyerRepository> _buyerRepositoryMock;
private readonly Mock<IOrderRepository> _orderRepositoryMock;
public NewOrderRequestHandlerTest()
{
//Mocks;
_buyerRepositoryMock = new Mock<IBuyerRepository>();
_orderRepositoryMock = new Mock<IOrderRepository>();
}
[Fact]
public async Task Handle_ReturnsTrue_WhenOrderIsPersistedSuccesfully()
{
// Arrange
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().Buyer))
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken)))
.Returns(Task.FromResult(1));
_orderRepositoryMock.Setup(or => or.Add(FakeOrder())).Returns(FakeOrder());
_orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))).Returns(Task.FromResult(1));
//Act
var handler = new NewOrderRequestHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object);
var result = await handler.Handle(FakeOrderRequestWithBuyer());
//Assert
Assert.True(result);
}
[Fact]
public async Task Handle_ReturnsFalse_WhenOrderIsNotPersisted()
{
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().Buyer))
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken)))
.Returns(Task.FromResult(1));
_orderRepositoryMock.Setup(or => or.Add(FakeOrder())).Returns(FakeOrder());
_orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))).Returns(Task.FromResult(0));
//Act
var handler = new NewOrderRequestHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object);
var result = await handler.Handle(FakeOrderRequestWithBuyer());
//Assert
Assert.False(result);
}
private Buyer FakeBuyer()
{
return new Buyer(Guid.NewGuid().ToString());
}
private Order FakeOrder()
{
return new Order(1, 1)
{
};
}
private NewOrderRequest FakeOrderRequestWithBuyer()
{
return new NewOrderRequest
{
Buyer = "1234",
CardNumber = "1234",
CardExpiration = DateTime.Now.AddYears(1),
CardSecurityNumber = "123",
CardHolderName = "XXX"
};
}
}
}

+ 152
- 0
test/Services/UnitTest/Ordering/Controllers/OrderControllerTest.cs View File

@ -0,0 +1,152 @@
using System;
using Xunit;
using System.Threading.Tasks;
using Moq;
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers;
using Microsoft.eShopOnContainers.Services.Ordering.Api.Application.Commands;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
using Microsoft.eShopOnContainers.Services.Ordering.Api.Application.Queries;
using System.Collections.Generic;
namespace UnitTest.Ordering.Controllers
{
public class OrderControllerTest
{
private readonly Mock<IMediator> _mediatorMock;
private readonly Mock<IIdentityService> _identityMock;
private readonly Mock<IOrderQueries> _queriesMock;
public OrderControllerTest()
{
//Mocks;
_mediatorMock = new Mock<IMediator>();
_identityMock = new Mock<IIdentityService>();
_queriesMock = new Mock<IOrderQueries>();
}
[Fact]
public async Task AddOrder_ReturnsBadRequestResult_WhenPersitenceOperationFails()
{
// Arrange
var orderRequest = new object() as IAsyncRequest<bool>;
_mediatorMock.Setup(mediator => mediator.SendAsync(OrderFakeNotExpired()))
.Returns(Task.FromResult(false));
_identityMock.Setup(identity => identity.GetUserIdentity())
.Returns(Guid.NewGuid().ToString());
var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object);
// Act
var badRequestResult = await controller.AddOrder(OrderFakeNotExpired());
// Assert
Assert.IsType<BadRequestResult>(badRequestResult);
}
[Fact]
public async Task AddOrder_ReturnsOK_WhenPersistenceOperationSucceed()
{
// Arrange
_mediatorMock.Setup(mediator => mediator.SendAsync(OrderFakeNotExpired()))
.Returns(Task.FromResult(true));
_identityMock.Setup(identity => identity.GetUserIdentity())
.Returns(Guid.NewGuid().ToString());
var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object);
// Act
var badRequestResult = await controller.AddOrder(OrderFakeNotExpired());
// Assert
Assert.IsType<BadRequestResult>(badRequestResult);
}
[Fact]
public async Task GetOrder_ReturnsNotFound_WhenItemNotFound()
{
// Arrange
_queriesMock.Setup(queries => queries.GetOrder(1))
.Throws(new KeyNotFoundException());
var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object);
// Act
var notFoundResult = await controller.GetOrder(1);
// Assert
Assert.IsType<NotFoundResult>(notFoundResult);
}
[Fact]
public async Task GetOrder_ReturnsOkObjecResult_WheItemFound()
{
// Arrange
_queriesMock.Setup(queries => queries.GetOrder(1))
.Returns(Task.FromResult(new object()));
var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object);
// Act
var OkObjectResult = await controller.GetOrder(1);
// Assert
Assert.IsType<OkObjectResult>(OkObjectResult);
}
[Fact]
public async Task GetOrders_ReturnsOkObjectResult()
{
// Arrange
_queriesMock.Setup(queries => queries.GetOrders())
.Returns(Task.FromResult(new object()));
var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object);
// Act
var OkObjectResult = await controller.GetOrders();
// Assert
Assert.IsType<OkObjectResult>(OkObjectResult);
}
[Fact]
public async Task GetCardTypes()
{
// Arrange
_queriesMock.Setup(queries => queries.GetCardTypes())
.Returns(Task.FromResult(new object()));
var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object);
// Act
var OkObjectResult = await controller.GetCardTypes();
// Assert
Assert.IsType<OkObjectResult>(OkObjectResult);
}
//Fakes
private NewOrderRequest OrderFakeNotExpired()
{
return new NewOrderRequest()
{
CardTypeId = 1,
CardExpiration = DateTime.Now.AddYears(1)
};
}
private NewOrderRequest OrderFakeExpired()
{
return new NewOrderRequest()
{
CardTypeId = 1,
CardExpiration = DateTime.Now.AddYears(-1)
};
}
}
}

+ 19
- 0
test/Services/UnitTest/Properties/AssemblyInfo.cs View File

@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UnitTest")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7796f5d8-31fc-45a4-b673-19de5ba194cf")]

+ 22
- 0
test/Services/UnitTest/UnitTest.xproj View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>7796f5d8-31fc-45a4-b673-19de5ba194cf</ProjectGuid>
<RootNamespace>UnitTest</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

+ 24
- 0
test/Services/UnitTest/project.json View File

@ -0,0 +1,24 @@
{
"version": "1.0.0-*",
"dependencies": {
"MediatR": "2.1.0",
"Moq": "4.6.38-alpha",
"Microsoft.NETCore.App": "1.1.0",
"xunit": "2.2.0-beta4-build3444",
"Ordering.API": "1.0.0-*",
"Catalog.API": "1.0.0-*",
"Microsoft.AspNetCore.TestHost": "1.1.0",
"dotnet-test-xunit": "2.2.0-preview2-build1029"
},
"testRunner": "xunit",
"runtimes": {
"win10-x64": {}
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
}
}
}
}

+ 50
- 0
test/Services/UnitTests__/UnitTests/Ordering/OrderControllerTest.cs View File

@ -0,0 +1,50 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Xunit;
using System.Threading.Tasks;
using Moq;
using MediatR;
namespace UnitTests
{
public class OrderControllerTest
{
private readonly Mock<IMediator> _mock;
public OrderControllerTest()
{
//config mock;
_mock = new Mock<IMediator>();
}
[Fact]
public async Task AddOrder_ReturnsBadRequestResult_WhenPersitenceOperationFails()
{
//Add order:
var orderRequest = new object() as IAsyncRequest<bool>;
_mock.Setup(mediator => mediator.SendAsync(orderRequest))
.Returns(Task.FromResult(false));
// Arrange
var controller = new OrdersController(mockRepo.Object);
controller.ModelState.AddModelError("SessionName", "Required");
var newSession = new HomeController.NewSessionModel();
// Act
var result = await controller.Index(newSession);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
// Implement Fake method for mock.
private MediatorMockForAddOrder()
{
}
}
}

+ 36
- 0
test/Services/UnitTests__/UnitTests/Properties/AssemblyInfo.cs View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("UnitTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ecbb8dc1-22ea-42d2-a45a-4ae800c73356")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

+ 117
- 0
test/Services/UnitTests__/UnitTests/UnitTests.csproj View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{ECBB8DC1-22EA-42D2-A45A-4AE800C73356}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UnitTests</RootNamespace>
<AssemblyName>UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MediatR, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\MediatR.2.1.0\lib\net45\MediatR.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Moq, Version=4.6.38.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Moq.4.6.38-alpha\lib\net45\Moq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.assert, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.core, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.execution.desktop, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="Ordering\OrderControllerTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Catalog\" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

+ 12
- 0
test/Services/UnitTests__/UnitTests/packages.config View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Castle.Core" version="3.3.3" targetFramework="net452" />
<package id="MediatR" version="2.1.0" targetFramework="net452" />
<package id="Moq" version="4.6.38-alpha" targetFramework="net452" />
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="net452" />
<package id="xunit.abstractions" version="2.0.1" targetFramework="net452" />
<package id="xunit.assert" version="2.2.0-beta4-build3444" targetFramework="net452" />
<package id="xunit.core" version="2.2.0-beta4-build3444" targetFramework="net452" />
<package id="xunit.extensibility.core" version="2.2.0-beta4-build3444" targetFramework="net452" />
<package id="xunit.extensibility.execution" version="2.2.0-beta4-build3444" targetFramework="net452" />
</packages>

Loading…
Cancel
Save