commit
						a7394be2bc
					
				
							
								
								
									
										2
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								.env
									
									
									
									
									
								
							| @ -5,7 +5,7 @@ | ||||
| # The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices | ||||
| 
 | ||||
| ESHOP_EXTERNAL_DNS_NAME_OR_IP=localhost | ||||
| ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.92 | ||||
| ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.162 | ||||
| 
 | ||||
| #ESHOP_AZURE_REDIS_BASKET_DB=<YourAzureRedisBasketInfo> | ||||
| #ESHOP_AZURE_STORAGE_CATALOG_URL=<YourAzureStorage_Catalog_BLOB_URL> | ||||
|  | ||||
| @ -8,7 +8,7 @@ version: '3' | ||||
| # | ||||
| # IMPORTANT: Note that this compose file uses ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP instead of ESHOP_EXTERNAL_DNS_NAME_OR_IP | ||||
| 
 | ||||
| # Set ASPNETCORE_ENVIRONMENT=Development, instead Production, if you want to show up errors while testing.  | ||||
| # Set ASPNETCORE_ENVIRONMENT= Development or Production, depending if you want to show up errors while testing.  | ||||
| # | ||||
| # You need to start it with the following CLI command: | ||||
| # docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d | ||||
| @ -17,7 +17,7 @@ services: | ||||
|        | ||||
|   basket.api: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - ConnectionString=${ESHOP_AZURE_REDIS_BASKET_DB:-basket.data} | ||||
|       - identityUrl=http://identity.api              #Local: You need to open your local dev-machine firewall at range 5100-5110. | ||||
| @ -35,7 +35,7 @@ services: | ||||
| 
 | ||||
|   catalog.api: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word} | ||||
|       - PicBaseUrl=${ESHOP_AZURE_STORAGE_CATALOG_URL:-http://localhost:5101/api/v1/catalog/items/[0]/pic/}   #Local: You need to open your local dev-machine firewall at range 5100-5110.   | ||||
| @ -54,7 +54,7 @@ services: | ||||
| 
 | ||||
|   identity.api: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104 | ||||
|       - 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 | ||||
| @ -72,7 +72,7 @@ services: | ||||
| 
 | ||||
|   ordering.api: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-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-5110. | ||||
| @ -92,7 +92,7 @@ services: | ||||
| 
 | ||||
|   marketing.api: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - ConnectionString=${ESHOP_AZURE_MARKETING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word} | ||||
|       - MongoConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} | ||||
| @ -116,7 +116,7 @@ services: | ||||
| 
 | ||||
|   webspa: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 | ||||
|       - OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102 | ||||
| @ -126,7 +126,7 @@ services: | ||||
|       - LocationsUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5109 | ||||
|       - CatalogUrlHC=http://catalog.api/hc | ||||
|       - 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.           | ||||
|       - IdentityUrlHC=http://identity.api/hc            | ||||
|       - BasketUrlHC=http://basket.api/hc | ||||
|       - MarketingUrlHC=http://marketing.api/hc | ||||
|       - PaymentUrlHC=http://payment.api/hc | ||||
| @ -138,17 +138,17 @@ services: | ||||
| 
 | ||||
|   webmvc: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - CatalogUrl=http://catalog.api | ||||
|       - OrderingUrl=http://ordering.api | ||||
|       - BasketUrl=http://basket.api | ||||
|       - LocationsUrl=http://locations.api | ||||
|       - 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_PROD_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.                                                   | ||||
|       - IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105   #Local:  Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. #Remote: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.   | ||||
|       - MarketingUrl=http://marketing.api                                                   | ||||
|       - CatalogUrlHC=http://catalog.api/hc | ||||
|       - 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.           | ||||
|       - IdentityUrlHC=http://identity.api/hc              | ||||
|       - BasketUrlHC=http://basket.api/hc | ||||
|       - MarketingUrlHC=http://marketing.api/hc | ||||
|       - PaymentUrlHC=http://payment.api/hc | ||||
| @ -161,7 +161,7 @@ services: | ||||
| 
 | ||||
|   webstatus: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - CatalogUrl=http://catalog.api/hc | ||||
|       - OrderingUrl=http://ordering.api/hc | ||||
| @ -179,7 +179,7 @@ services: | ||||
| 
 | ||||
|   payment.api: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} | ||||
|       - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} | ||||
| @ -192,7 +192,7 @@ services: | ||||
| 
 | ||||
|   locations.api: | ||||
|     environment: | ||||
|       - ASPNETCORE_ENVIRONMENT=Production | ||||
|       - ASPNETCORE_ENVIRONMENT=Development | ||||
|       - ASPNETCORE_URLS=http://0.0.0.0:80 | ||||
|       - ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} | ||||
|       - Database=LocationsDb | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								img/loadtests/k8ssettings.PNG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/loadtests/k8ssettings.PNG
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 66 KiB | 
							
								
								
									
										
											BIN
										
									
								
								img/loadtests/loadtestproj_dir.PNG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/loadtests/loadtestproj_dir.PNG
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								img/loadtests/runloadtest.PNG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/loadtests/runloadtest.PNG
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 10 KiB | 
							
								
								
									
										
											BIN
										
									
								
								img/loadtests/sfmanifestsettings.PNG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/loadtests/sfmanifestsettings.PNG
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 66 KiB | 
| @ -5,9 +5,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions | ||||
| { | ||||
|     public interface IEventBus | ||||
|     { | ||||
|         void Publish(IntegrationEvent @event); | ||||
| 
 | ||||
|         void Subscribe<T, TH>() | ||||
|             where T : IntegrationEvent | ||||
|             where TH : IIntegrationEventHandler<T>; | ||||
| 
 | ||||
|         void SubscribeDynamic<TH>(string eventName) | ||||
|             where TH : IDynamicIntegrationEventHandler; | ||||
| 
 | ||||
| @ -17,7 +20,5 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions | ||||
|         void Unsubscribe<T, TH>() | ||||
|             where TH : IIntegrationEventHandler<T> | ||||
|             where T : IntegrationEvent; | ||||
| 
 | ||||
|         void Publish(IntegrationEvent @event); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,145 +0,0 @@ | ||||
| //using Microsoft.eShopOnContainers.BuildingBlocks.CommandBus; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Newtonsoft.Json; | ||||
| using Polly; | ||||
| using Polly.Retry; | ||||
| using RabbitMQ.Client; | ||||
| using RabbitMQ.Client.Events; | ||||
| using RabbitMQ.Client.Exceptions; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Net.Sockets; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| /* | ||||
| namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ | ||||
| { | ||||
|     public class CommandBusRabbitMQ : ICommandBus, IDisposable | ||||
|     { | ||||
|         const string BROKER_NAME = "eshop_command_bus"; | ||||
| 
 | ||||
|         private readonly IRabbitMQPersistentConnection _persistentConnection; | ||||
|         private readonly ILogger<CommandBusRabbitMQ> _logger; | ||||
| 
 | ||||
|         private IModel _consumerChannel; | ||||
|         private string _queueName; | ||||
| 
 | ||||
|         private readonly Dictionary<string, IIntegrationCommandHandler> _handlers; | ||||
|         private readonly Dictionary<string, Type> _typeMappings; | ||||
| 
 | ||||
|         public CommandBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection,  | ||||
|             ILogger<CommandBusRabbitMQ> logger) | ||||
|         { | ||||
|             _logger = logger; | ||||
|             _persistentConnection = persistentConnection; | ||||
|             _handlers = new Dictionary<string, IIntegrationCommandHandler>(); | ||||
|             _typeMappings = new Dictionary<string, Type>(); | ||||
|         } | ||||
| 
 | ||||
|         public void Send<T>(string name, T data) | ||||
|         { | ||||
|             Send(new IntegrationCommand<T>(name, data)); | ||||
|         } | ||||
| 
 | ||||
|         public void Handle<TC>(string name, IIntegrationCommandHandler<TC> handler) | ||||
|         { | ||||
|             _handlers.Add(name, handler); | ||||
|             _typeMappings.Add(name, typeof(TC)); | ||||
|         } | ||||
| 
 | ||||
|         public void Handle(string name, IIntegrationCommandHandler handler) | ||||
|         { | ||||
|             _handlers.Add(name, handler); | ||||
|         } | ||||
|         public void Handle<TI, TC>(TI handler) where TI : IIntegrationCommandHandler<TC> | ||||
|         { | ||||
|             var name = typeof(TI).Name; | ||||
|             _handlers.Add(name, handler); | ||||
|             _typeMappings.Add(name, typeof(TC)); | ||||
|         } | ||||
| 
 | ||||
|         private void Send<T>(IntegrationCommand<T> command) | ||||
|         { | ||||
|             if (!_persistentConnection.IsConnected) | ||||
|             { | ||||
|                 _persistentConnection.TryConnect(); | ||||
|             } | ||||
| 
 | ||||
|             var policy = RetryPolicy.Handle<BrokerUnreachableException>() | ||||
|                 .Or<SocketException>() | ||||
|                 .WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => | ||||
|                     { | ||||
|                         _logger.LogWarning(ex.ToString()); | ||||
|                     }); | ||||
| 
 | ||||
|             using (var channel = _persistentConnection.CreateModel()) | ||||
|             { | ||||
|                 var commandName = command.Name; | ||||
|                 channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct"); | ||||
|                 var message = JsonConvert.SerializeObject(command); | ||||
|                 var body = Encoding.UTF8.GetBytes(message); | ||||
|                 policy.Execute(() => | ||||
|                 { | ||||
|                     channel.BasicPublish(exchange: BROKER_NAME, | ||||
|                                      routingKey: commandName, | ||||
|                                      basicProperties: null, | ||||
|                                      body: body); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private IModel CreateConsumerChannel() | ||||
|         { | ||||
|             if (!_persistentConnection.IsConnected) | ||||
|             { | ||||
|                 _persistentConnection.TryConnect(); | ||||
|             } | ||||
| 
 | ||||
|             var channel = _persistentConnection.CreateModel(); | ||||
| 
 | ||||
|             channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct"); | ||||
|             _queueName = channel.QueueDeclare().QueueName; | ||||
|             var consumer = new EventingBasicConsumer(channel); | ||||
|             consumer.Received += async (model, ea) => | ||||
|             { | ||||
|                 var commandName = ea.RoutingKey; | ||||
|                 var message = Encoding.UTF8.GetString(ea.Body); | ||||
|                 await InvokeHandler(commandName, message); | ||||
|             }; | ||||
| 
 | ||||
|             channel.BasicConsume(queue: _queueName, | ||||
|                                  noAck: true, | ||||
|                                  consumer: consumer); | ||||
| 
 | ||||
|             channel.CallbackException += (sender, ea) => | ||||
|             { | ||||
|                 _consumerChannel.Dispose(); | ||||
|                 _consumerChannel = CreateConsumerChannel(); | ||||
|             }; | ||||
| 
 | ||||
|             return channel; | ||||
|         } | ||||
| 
 | ||||
|         private Task InvokeHandler(string commandName, string message) | ||||
|         { | ||||
|             if (_handlers.ContainsKey(commandName)) | ||||
|             { | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public void Dispose() | ||||
|         { | ||||
|             if (_consumerChannel != null) | ||||
|             { | ||||
|                 _consumerChannel.Dispose(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| */ | ||||
| @ -32,11 +32,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ | ||||
|         private string _queueName; | ||||
| 
 | ||||
|         public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger, | ||||
|             ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, int retryCount = 5) | ||||
|             ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, string queueName = null, int retryCount = 5) | ||||
|         { | ||||
|             _persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|             _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); | ||||
|             _queueName = queueName; | ||||
|             _consumerChannel = CreateConsumerChannel(); | ||||
|             _autofac = autofac; | ||||
|             _retryCount = retryCount; | ||||
| @ -147,7 +148,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ | ||||
|             _subsManager.RemoveDynamicSubscription<TH>(eventName); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public void Dispose() | ||||
|         { | ||||
|             if (_consumerChannel != null) | ||||
| @ -170,7 +170,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ | ||||
|             channel.ExchangeDeclare(exchange: BROKER_NAME, | ||||
|                                  type: "direct"); | ||||
| 
 | ||||
|             _queueName = channel.QueueDeclare().QueueName; | ||||
|             channel.QueueDeclare(queue: _queueName, | ||||
|                                  durable: true, | ||||
|                                  exclusive: false, | ||||
|                                  autoDelete: false, | ||||
|                                  arguments: null); | ||||
| 
 | ||||
| 
 | ||||
|             var consumer = new EventingBasicConsumer(channel); | ||||
|             consumer.Received += async (model, ea) => | ||||
| @ -196,8 +201,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ | ||||
| 
 | ||||
|         private async Task ProcessEvent(string eventName, string message) | ||||
|         { | ||||
| 
 | ||||
| 
 | ||||
|             if (_subsManager.HasSubscriptionsForEvent(eventName)) | ||||
|             { | ||||
|                 using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> | ||||
|     <PackageReference Include="Polly" Version="5.3.1" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
|  | ||||
| @ -1,7 +1,10 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Polly; | ||||
| using Polly.Retry; | ||||
| using System; | ||||
| using System.Data.SqlClient; | ||||
| 
 | ||||
| namespace Microsoft.AspNetCore.Hosting | ||||
| { | ||||
| @ -21,10 +24,26 @@ namespace Microsoft.AspNetCore.Hosting | ||||
|                 { | ||||
|                     logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}"); | ||||
| 
 | ||||
|                     context.Database | ||||
|                     var retry = Policy.Handle<SqlException>() | ||||
|                          .WaitAndRetry(new TimeSpan[] | ||||
|                          { | ||||
|                              TimeSpan.FromSeconds(5), | ||||
|                              TimeSpan.FromSeconds(10), | ||||
|                              TimeSpan.FromSeconds(15), | ||||
|                          }); | ||||
| 
 | ||||
|                     retry.Execute(() => | ||||
|                     { | ||||
|                         //if the sql server container is not created on run docker compose this | ||||
|                         //migration can't fail for network related exception. The retry options for DbContext only  | ||||
|                         //apply to transient exceptions. | ||||
| 
 | ||||
|                         context.Database | ||||
|                         .Migrate(); | ||||
| 
 | ||||
|                     seeder(context,services); | ||||
|                         seeder(context, services); | ||||
|                     }); | ||||
|                    | ||||
| 
 | ||||
|                     logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}"); | ||||
|                 } | ||||
|  | ||||
| @ -34,148 +34,139 @@ | ||||
|             <Color x:Key="AndroidListViewBackgroundColor">Transparent</Color> | ||||
|             <Color x:Key="iOSListViewBackgroundColor">Transparent</Color> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:TypeArguments="Color" | ||||
|                 x:Key="ActivityIndicatorColor" | ||||
|                 iOS="{ StaticResource iOSDefaultTintColor }" /> | ||||
|             <OnPlatform x:TypeArguments="Color" | ||||
|                         x:Key="ActivityIndicatorColor"> | ||||
|                 <On Platform="iOS" Value="{ StaticResource iOSDefaultTintColor }" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="Color" | ||||
|                 x:Key="DefaultButtonClassBackgroundColor" | ||||
|                 Android="{ StaticResource AndroidDefaultButtonClassBackgroundColor }" | ||||
|                 iOS="{ StaticResource iOSDefaultButtonClassBackgroundColor }" /> | ||||
|             <OnPlatform x:TypeArguments="Color" | ||||
|                         x:Key="DefaultButtonClassBackgroundColor"> | ||||
|                 <On Platform="iOS" Value="{ StaticResource iOSDefaultButtonClassBackgroundColor }" /> | ||||
|                 <On Platform="Android" Value="{ StaticResource AndroidDefaultButtonClassBackgroundColor }" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="Color" | ||||
|                 x:Key="DefaultButtonClassBorderColor" | ||||
|                 Android="{ StaticResource AndroidDefaultButtonClassBorderColor }" | ||||
|                 iOS="{ StaticResource iOSDefaultButtonClassBorderColor }" /> | ||||
|             <OnPlatform x:TypeArguments="Color" | ||||
|                         x:Key="DefaultButtonClassBorderColor"> | ||||
|                 <On Platform="iOS" Value="{ StaticResource iOSDefaultButtonClassBorderColor }" /> | ||||
|                 <On Platform="Android" Value="{ StaticResource AndroidDefaultButtonClassBorderColor }" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="Color" | ||||
|                 x:Key="DefaultButtonClassTextColor" | ||||
|                 Android="{ StaticResource AndroidDefaultButtonClassTextColor }" | ||||
|                 iOS="{ StaticResource iOSDefaultButtonClassTextColor }" /> | ||||
|             <OnPlatform x:TypeArguments="Color" | ||||
|                         x:Key="DefaultButtonClassTextColor"> | ||||
|                 <On Platform="iOS" Value="{ StaticResource iOSDefaultButtonClassTextColor }" /> | ||||
|                 <On Platform="Android" Value="{ StaticResource AndroidDefaultButtonClassTextColor }" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="Color" | ||||
|                 x:Key="EntryBackgroundColor" | ||||
|                 Android="{ StaticResource AndroidEntryBackgroundColor }" | ||||
|                 iOS="{ StaticResource iOSEntryBackgroundColor }" /> | ||||
|             <OnPlatform x:TypeArguments="Color" | ||||
|                         x:Key="EntryBackgroundColor"> | ||||
|                 <On Platform="iOS" Value="{ StaticResource iOSEntryBackgroundColor }" /> | ||||
|                 <On Platform="Android" Value="{ StaticResource AndroidEntryBackgroundColor }" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="Color" | ||||
|                 x:Key="ThemeListViewBackgroundColor" | ||||
|                 Android="{ StaticResource AndroidListViewBackgroundColor }" | ||||
|                 iOS="{ StaticResource iOSListViewBackgroundColor }" /> | ||||
|             <OnPlatform x:TypeArguments="Color" | ||||
|                         x:Key="ThemeListViewBackgroundColor"> | ||||
|                 <On Platform="iOS" Value="{ StaticResource iOSListViewBackgroundColor }" /> | ||||
|                 <On Platform="Android" Value="{ StaticResource AndroidListViewBackgroundColor }" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <!-- SIZES --> | ||||
|             <OnPlatform  | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 x:Key="BaseButtonBorderRadius" | ||||
|                 iOS="6" /> | ||||
|             <OnPlatform x:TypeArguments="x:Double" | ||||
|                         x:Key="BaseButtonBorderRadius"> | ||||
|                 <On Platform="iOS" Value="6" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 x:Key="BaseButtonBorderWidth" | ||||
|                 Android="0" | ||||
|                 iOS="0" /> | ||||
|             <OnPlatform x:TypeArguments="x:Double" | ||||
|                         x:Key="BaseButtonBorderWidth"> | ||||
|                 <On Platform="iOS, Android" Value="0" /> | ||||
|             </OnPlatform> | ||||
|              | ||||
|             <!-- FONTS --> | ||||
|             <OnPlatform  | ||||
|                 x:Key="MontserratRegular" | ||||
|                 x:TypeArguments="x:String" | ||||
|                 iOS="Montserrat-Regular" | ||||
|                 Android="Montserrat-Regular.ttf#Montserrat" | ||||
|                 WinPhone="Assets/Fonts/Montserrat-Regular.ttf#Montserrat"/> | ||||
|             <OnPlatform x:Key="MontserratRegular" | ||||
|                         x:TypeArguments="x:String"> | ||||
|                 <On Platform="iOS" Value="Montserrat-Regular" /> | ||||
|                 <On Platform="Android" Value="Montserrat-Regular.ttf#Montserrat" /> | ||||
|                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/Fonts/Montserrat-Regular.ttf#Montserrat" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="MontserratBold" | ||||
|                 x:TypeArguments="x:String" | ||||
|                 iOS="Montserrat-Bold" | ||||
|                 Android="Montserrat-Bold.ttf#Montserrat" | ||||
|                 WinPhone="Assets/Fonts/Montserrat-Bold.ttf#Montserrat"/> | ||||
|             <OnPlatform x:Key="MontserratBold" | ||||
|                         x:TypeArguments="x:String"> | ||||
|                 <On Platform="iOS" Value="Montserrat-Bold" /> | ||||
|                 <On Platform="Android" Value="Montserrat-Bold.ttf#Montserrat" /> | ||||
|                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/Fonts/Montserrat-Bold.ttf#Montserrat" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="SourceSansProRegular" | ||||
|                 x:TypeArguments="x:String" | ||||
|                 iOS="SourceSansPro-Regular" | ||||
|                 Android="SourceSansPro-Regular.ttf#Source Sans Pro" | ||||
|                 WinPhone="Assets/Fonts/SourceSansPro-Regular.ttf#Source Sans Pro"/> | ||||
|             <OnPlatform x:Key="SourceSansProRegular" | ||||
|                         x:TypeArguments="x:String"> | ||||
|                 <On Platform="iOS" Value="SourceSansPro-Regular" /> | ||||
|                 <On Platform="Android" Value="SourceSansPro-Regular.ttf#Source Sans Pro" /> | ||||
|                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/Fonts/SourceSansPro-Regular.ttf#Source Sans Pro" /> | ||||
|             </OnPlatform> | ||||
|              | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 x:Key="BaseButtonFontSize" | ||||
|                 Android="16" | ||||
|                 iOS="18" /> | ||||
|             <OnPlatform x:TypeArguments="x:Double" | ||||
|                         x:Key="BaseButtonFontSize"> | ||||
|                 <On Platform="iOS" Value="18" /> | ||||
|                 <On Platform="Android" Value="16" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 x:Key="BaseFontSize" | ||||
|                 Android="15" | ||||
|                 iOS="16" /> | ||||
|             <OnPlatform x:TypeArguments="x:Double" | ||||
|                         x:Key="BaseFontSize"> | ||||
|                 <On Platform="iOS" Value="16" /> | ||||
|                 <On Platform="Android" Value="15" /> | ||||
|             </OnPlatform> | ||||
|              | ||||
|             <OnPlatform  | ||||
|                 x:Key="LittleSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="11" | ||||
|                 Android="12" | ||||
|                 WinPhone="12"/> | ||||
|             <OnPlatform x:Key="LittleSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="11" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="12" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="MidMediumSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="12" | ||||
|                 Android="14" | ||||
|                 WinPhone="14"/> | ||||
|             <OnPlatform x:Key="MidMediumSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="12" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="14" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="MediumSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="14" | ||||
|                 Android="16" | ||||
|                 WinPhone="16"/> | ||||
|             <OnPlatform x:Key="MediumSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="14" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="16" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="LargeSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="16" | ||||
|                 Android="18" | ||||
|                 WinPhone="18"/> | ||||
|             <OnPlatform x:Key="LargeSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="16" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="18" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="LargerSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="18" | ||||
|                 Android="20" | ||||
|                 WinPhone="20"/> | ||||
|             <OnPlatform x:Key="LargerSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="18" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="20" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="BigSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="20" | ||||
|                 Android="24" | ||||
|                 WinPhone="24"/> | ||||
|             <OnPlatform x:Key="BigSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="20" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="24" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="ExtraBigSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="24" | ||||
|                 Android="32" | ||||
|                 WinPhone="32"/> | ||||
|             <OnPlatform x:Key="ExtraBigSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="24" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="32" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform  | ||||
|                 x:Key="HugeSize" | ||||
|                 x:TypeArguments="x:Double" | ||||
|                 iOS="32" | ||||
|                 Android="48" | ||||
|                 WinPhone="48"/> | ||||
|             <OnPlatform x:Key="HugeSize" | ||||
|                         x:TypeArguments="x:Double"> | ||||
|                 <On Platform="iOS" Value="32" /> | ||||
|                 <On Platform="Android, UWP, WinRT, WinPhone" Value="48" /> | ||||
|             </OnPlatform> | ||||
| 
 | ||||
|             <OnPlatform | ||||
|                 x:TypeArguments="FontAttributes" | ||||
|                 x:Key="BaseButtonFontAttributes" | ||||
|                 Android="None" | ||||
|                 iOS="Bold" /> | ||||
|             <OnPlatform x:TypeArguments="FontAttributes" | ||||
|                         x:Key="BaseButtonFontAttributes"> | ||||
|                 <On Platform="iOS" Value="Bold" /> | ||||
|                 <On Platform="Android" Value="None" /> | ||||
|             </OnPlatform> | ||||
|              | ||||
|             <!-- CONVERTERS --> | ||||
|             <converters:CountToBoolConverter x:Key="CountToBoolConverter" /> | ||||
|  | ||||
| @ -44,11 +44,10 @@ | ||||
|             <!-- ANDROID --> | ||||
|             <Grid> | ||||
|                 <Grid.IsVisible> | ||||
|                     <OnPlatform          | ||||
|                         x:TypeArguments="x:Boolean" | ||||
|                         Android="True" | ||||
|                         iOS="True" | ||||
|                         WinPhone="False"/> | ||||
|                     <OnPlatform x:TypeArguments="x:Boolean"> | ||||
|                         <On Platform="iOS, Android" Value="True" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="False" /> | ||||
|                     </OnPlatform> | ||||
|                 </Grid.IsVisible> | ||||
|                 <BoxView   | ||||
|                     BackgroundColor="{StaticResource LightGreenColor}" | ||||
| @ -64,11 +63,10 @@ | ||||
|             <!-- IOS & UWP --> | ||||
|             <Grid> | ||||
|                 <Grid.IsVisible> | ||||
|                     <OnPlatform          | ||||
|                         x:TypeArguments="x:Boolean" | ||||
|                         Android="False" | ||||
|                         iOS="False" | ||||
|                         WinPhone="True"/> | ||||
|                     <OnPlatform x:TypeArguments="x:Boolean"> | ||||
|                         <On Platform="iOS, Android" Value="False" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="True" /> | ||||
|                     </OnPlatform> | ||||
|                 </Grid.IsVisible> | ||||
|                 <Image | ||||
|                     Source="Assets\circle_button_background.png" | ||||
|  | ||||
| @ -68,6 +68,7 @@ namespace eShopOnContainers.Core.Services.Order | ||||
|                 CardSecurityNumber = order.CardSecurityNumber, | ||||
|                 CardTypeId = order.CardTypeId, | ||||
|                 City = order.ShippingCity, | ||||
|                 State = order.ShippingState, | ||||
|                 Country = order.ShippingCountry, | ||||
|                 ZipCode = order.ShippingZipCode, | ||||
|                 Street = order.ShippingStreet | ||||
|  | ||||
| @ -165,7 +165,7 @@ namespace eShopOnContainers.Core.ViewModels | ||||
|                 await NavigationService.RemoveLastFromBackStackAsync(); | ||||
| 
 | ||||
|                 // Show Dialog | ||||
|                 await DialogService.ShowAlertAsync("Order sent successfully!", string.Format("Order {0}", Order.OrderNumber), "Ok"); | ||||
|                 await DialogService.ShowAlertAsync("Order sent successfully!", "Checkout", "Ok"); | ||||
|                 await NavigationService.RemoveLastFromBackStackAsync(); | ||||
|             } | ||||
|             catch | ||||
|  | ||||
| @ -171,11 +171,10 @@ | ||||
|             VerticalOptions="Center" | ||||
|             HorizontalOptions="Center"> | ||||
|             <ActivityIndicator.WidthRequest> | ||||
|                 <OnPlatform  | ||||
|                     x:TypeArguments="x:Double"  | ||||
|                     iOS="100"  | ||||
|                     Android="100" | ||||
|                     WinPhone="400" /> | ||||
|                 <OnPlatform x:TypeArguments="x:Double"> | ||||
|                     <On Platform="iOS, Android" Value="100" /> | ||||
|                     <On Platform="UWP, WinRT, WinPhone" Value="400" /> | ||||
|                 </OnPlatform> | ||||
|             </ActivityIndicator.WidthRequest> | ||||
|         </ActivityIndicator> | ||||
|     </Grid> | ||||
|  | ||||
| @ -90,11 +90,10 @@ | ||||
|             VerticalOptions="Center" | ||||
|             HorizontalOptions="Center"> | ||||
|             <ActivityIndicator.WidthRequest> | ||||
|                 <OnPlatform  | ||||
|                     x:TypeArguments="x:Double"  | ||||
|                     iOS="100"  | ||||
|                     Android="100" | ||||
|                     WinPhone="400" /> | ||||
|                 <OnPlatform x:TypeArguments="x:Double"> | ||||
|                     <On Platform="iOS, Android" Value="100" /> | ||||
|                     <On Platform="UWP, WinRT, WinPhone" Value="400" /> | ||||
|                 </OnPlatform> | ||||
|             </ActivityIndicator.WidthRequest> | ||||
|         </ActivityIndicator> | ||||
|     </Grid> | ||||
|  | ||||
| @ -114,11 +114,10 @@ | ||||
|             VerticalOptions="Center" | ||||
|             HorizontalOptions="Center"> | ||||
|             <ActivityIndicator.WidthRequest> | ||||
|                 <OnPlatform  | ||||
|                     x:TypeArguments="x:Double"  | ||||
|                     iOS="100"  | ||||
|                     Android="100" | ||||
|                     WinPhone="400" /> | ||||
|                 <OnPlatform x:TypeArguments="x:Double"> | ||||
|                     <On Platform="iOS, Android" Value="100" /> | ||||
|                     <On Platform="UWP, WinRT, WinPhone" Value="400" /> | ||||
|                 </OnPlatform> | ||||
|             </ActivityIndicator.WidthRequest> | ||||
|         </ActivityIndicator> | ||||
|     </Grid> | ||||
|  | ||||
| @ -231,11 +231,10 @@ | ||||
|             VerticalOptions="Center" | ||||
|             HorizontalOptions="Center"> | ||||
|             <ActivityIndicator.WidthRequest> | ||||
|                 <OnPlatform  | ||||
|                     x:TypeArguments="x:Double"  | ||||
|                     iOS="100"  | ||||
|                     Android="100" | ||||
|                     WinPhone="400" /> | ||||
|                 <OnPlatform x:TypeArguments="x:Double"> | ||||
|                     <On Platform="iOS, Android" Value="100" /> | ||||
|                     <On Platform="UWP, WinRT, WinPhone" Value="400" /> | ||||
|                 </OnPlatform> | ||||
|             </ActivityIndicator.WidthRequest> | ||||
|         </ActivityIndicator> | ||||
|     </Grid> | ||||
|  | ||||
| @ -76,11 +76,10 @@ | ||||
|       SelectedItem="{Binding Brand, Mode=TwoWay}" | ||||
|       Style="{StaticResource FilterPickerStyle}"> | ||||
|       <Picker.HeightRequest>    | ||||
|         <OnPlatform  | ||||
|           x:TypeArguments="x:Double"        | ||||
|           Android="48" | ||||
|           iOS="48" | ||||
|           WinPhone="36"/> | ||||
|         <OnPlatform x:TypeArguments="x:Double"> | ||||
|             <On Platform="iOS, Android" Value="48" /> | ||||
|             <On Platform="UWP, WinRT, WinPhone" Value="36" /> | ||||
|         </OnPlatform> | ||||
|       </Picker.HeightRequest>  | ||||
|     </Picker> | ||||
|     <!-- TYPE --> | ||||
| @ -91,11 +90,10 @@ | ||||
|       SelectedItem="{Binding Type, Mode=TwoWay}" | ||||
|       Style="{StaticResource FilterPickerStyle}">     | ||||
|       <Picker.HeightRequest>    | ||||
|         <OnPlatform  | ||||
|           x:TypeArguments="x:Double"        | ||||
|           Android="48" | ||||
|           iOS="48" | ||||
|           WinPhone="36"/> | ||||
|         <OnPlatform x:TypeArguments="x:Double"> | ||||
|             <On Platform="iOS, Android" Value="48" /> | ||||
|             <On Platform="UWP, WinRT, WinPhone" Value="36" /> | ||||
|         </OnPlatform> | ||||
|       </Picker.HeightRequest>  | ||||
|     </Picker> | ||||
|     <Button  | ||||
|  | ||||
| @ -9,10 +9,9 @@ | ||||
|              xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core" | ||||
| 			 viewModelBase:ViewModelLocator.AutoWireViewModel="true"> | ||||
|     <ContentPage.Title> | ||||
|         <OnPlatform | ||||
|             x:TypeArguments="x:String" | ||||
|             iOS="eShop on Containers" | ||||
|             WinPhone="eShop on Containers"/> | ||||
|         <OnPlatform x:TypeArguments="x:String"> | ||||
|             <On Platform="iOS, UWP, WinRT, WinPhone" Value="eShop on Containers" /> | ||||
|         </OnPlatform> | ||||
|     </ContentPage.Title> | ||||
|     <ContentPage.Resources> | ||||
|         <ResourceDictionary> | ||||
| @ -179,10 +178,10 @@ | ||||
|                   Style="{StaticResource HeaderLabelStyle}" /> | ||||
|                 <Entry Text="{Binding UserName.Value, Mode=TwoWay}"> | ||||
| 					<Entry.Style> | ||||
|                         <OnPlatform x:TypeArguments="Style" | ||||
|                           iOS="{StaticResource EntryStyle}" | ||||
|                           Android="{StaticResource EntryStyle}" | ||||
|                           WinPhone="{StaticResource UwpEntryStyle}"/> | ||||
|                         <OnPlatform x:TypeArguments="Style"> | ||||
|                             <On Platform="iOS, Android" Value="{StaticResource EntryStyle}" /> | ||||
|                             <On Platform="UWP, WinRT, WinPhone" Value="{StaticResource UwpEntryStyle}" /> | ||||
|                         </OnPlatform> | ||||
|                     </Entry.Style> | ||||
|                     <Entry.Behaviors> | ||||
| 						<behaviors:EventToCommandBehavior | ||||
| @ -208,10 +207,10 @@ | ||||
|                   IsPassword="True" | ||||
|                   Text="{Binding Password.Value, Mode=TwoWay}"> | ||||
|                     <Entry.Style> | ||||
|                         <OnPlatform x:TypeArguments="Style" | ||||
|                           iOS="{StaticResource EntryStyle}" | ||||
|                           Android="{StaticResource EntryStyle}" | ||||
|                           WinPhone="{StaticResource UwpEntryStyle}"/> | ||||
|                         <OnPlatform x:TypeArguments="Style"> | ||||
|                             <On Platform="iOS, Android" Value="{StaticResource EntryStyle}" /> | ||||
|                             <On Platform="UWP, WinRT, WinPhone" Value="{StaticResource UwpEntryStyle}" /> | ||||
|                         </OnPlatform> | ||||
|                     </Entry.Style> | ||||
| 					<Entry.Behaviors> | ||||
| 						<behaviors:EventToCommandBehavior | ||||
| @ -270,11 +269,10 @@ | ||||
|                 Grid.ColumnSpan="3" | ||||
|                 Aspect="AspectFill"> | ||||
|                 <Image.Source> | ||||
|                     <OnPlatform | ||||
|                         x:TypeArguments="ImageSource" | ||||
|                         Android="banner.png" | ||||
|                         iOS="banner.png" | ||||
|                         WinPhone="Assets\banner.png"/> | ||||
|                     <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                         <On Platform="iOS, Android" Value="banner.png" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="Assets\banner.png" /> | ||||
|                     </OnPlatform> | ||||
|                 </Image.Source> | ||||
|             </Image> | ||||
|             <Grid | ||||
| @ -319,11 +317,10 @@ | ||||
|                 <Image | ||||
|                     Style="{StaticResource SettingsImageStyle}"> | ||||
|                     <Image.Source> | ||||
|                         <OnPlatform  | ||||
|                             x:TypeArguments="ImageSource" | ||||
|                             WinPhone="Assets/app_settings.png" | ||||
|                             Android="app_settings" | ||||
|                             iOS="app_settings"/> | ||||
|                         <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                             <On Platform="iOS, Android" Value="app_settings" /> | ||||
|                             <On Platform="UWP, WinRT, WinPhone" Value="Assets/app_settings.png" /> | ||||
|                         </OnPlatform> | ||||
|                     </Image.Source> | ||||
|                 </Image> | ||||
|                 <Grid.GestureRecognizers> | ||||
| @ -345,24 +342,22 @@ | ||||
|                   AbsoluteLayout.LayoutFlags="All"> | ||||
|                     <WebView.Behaviors> | ||||
|                         <OnPlatform x:TypeArguments="Behavior"> | ||||
|                             <OnPlatform.Android> | ||||
|                                 <behaviors:EventToCommandBehavior | ||||
| 							        EventName="Navigating" | ||||
| 							        EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}" | ||||
| 							        Command="{Binding NavigateCommand}" /> | ||||
|                             </OnPlatform.Android> | ||||
|                             <OnPlatform.iOS> | ||||
|                                 <behaviors:EventToCommandBehavior | ||||
| 							        EventName="Navigating" | ||||
| 							        EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}" | ||||
| 							        Command="{Binding NavigateCommand}" /> | ||||
|                             </OnPlatform.iOS> | ||||
|                             <OnPlatform.WinPhone> | ||||
|                                 <behaviors:EventToCommandBehavior | ||||
| 							        EventName="Navigated" | ||||
| 							        EventArgsConverter="{StaticResource WebNavigatedEventArgsConverter}" | ||||
| 							        Command="{Binding NavigateCommand}" /> | ||||
|                             </OnPlatform.WinPhone> | ||||
|                             <On Platform="iOS, Android"> | ||||
|                                 <On.Value> | ||||
|                                     <behaviors:EventToCommandBehavior  | ||||
|                                         EventName="Navigating" | ||||
|                                         EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}" | ||||
|                                         Command="{Binding NavigateCommand}" /> | ||||
|                                 </On.Value> | ||||
|                             </On> | ||||
|                             <On Platform="UWP"> | ||||
|                                 <On.Value> | ||||
|                                     <behaviors:EventToCommandBehavior  | ||||
|                                         EventName="Navigated" | ||||
|                                         EventArgsConverter="{StaticResource WebNavigatedEventArgsConverter}" | ||||
|                                         Command="{Binding NavigateCommand}" /> | ||||
|                                 </On.Value> | ||||
|                             </On> | ||||
|                         </OnPlatform> | ||||
|                     </WebView.Behaviors> | ||||
|                 </WebView> | ||||
| @ -376,11 +371,10 @@ | ||||
|           VerticalOptions="Center" | ||||
|           HorizontalOptions="Center"> | ||||
|             <ActivityIndicator.WidthRequest> | ||||
|                 <OnPlatform    | ||||
|                   x:TypeArguments="x:Double" | ||||
|                   iOS="100" | ||||
|                   Android="100" | ||||
|                   WinPhone="400" /> | ||||
|                 <OnPlatform x:TypeArguments="x:Double"> | ||||
|                     <On Platform="iOS, Android" Value="100" /> | ||||
|                     <On Platform="UWP, WinRT, WinPhone" Value="400" /> | ||||
|                 </OnPlatform> | ||||
|             </ActivityIndicator.WidthRequest> | ||||
|         </ActivityIndicator> | ||||
|     </Grid> | ||||
|  | ||||
| @ -10,21 +10,19 @@ | ||||
|             BarTextColor="{StaticResource WhiteColor}" | ||||
| 			viewModelBase:ViewModelLocator.AutoWireViewModel="true"> | ||||
|     <TabbedPage.Title> | ||||
|         <OnPlatform       | ||||
|             x:TypeArguments="x:String" | ||||
|             iOS="eShop on Containers" | ||||
|             WinPhone="eShop on Containers"/> | ||||
|         <OnPlatform x:TypeArguments="x:String"> | ||||
|             <On Platform="iOS, UWP, WinRT, WinPhone" Value="eShop on Containers" /> | ||||
|         </OnPlatform> | ||||
|     </TabbedPage.Title> | ||||
|     <ContentPage.ToolbarItems> | ||||
|         <ToolbarItem  | ||||
|             Command="{Binding SettingsCommand}" | ||||
|             Text="Settings"> | ||||
|             <ToolbarItem.Icon> | ||||
|                 <OnPlatform  | ||||
|                     x:TypeArguments="FileImageSource" | ||||
|                     WinPhone="Assets/app_settings.png" | ||||
|                     Android="app_settings" | ||||
|                     iOS="app_settings"/> | ||||
|                 <OnPlatform x:TypeArguments="FileImageSource"> | ||||
|                     <On Platform="iOS, Android" Value="app_settings" /> | ||||
|                     <On Platform="UWP, WinRT, WinPhone" Value="Assets/app_settings.png" /> | ||||
|                 </OnPlatform> | ||||
|             </ToolbarItem.Icon> | ||||
|         </ToolbarItem> | ||||
|     </ContentPage.ToolbarItems> | ||||
| @ -32,22 +30,20 @@ | ||||
|     <views:CatalogView   | ||||
|         x:Name="HomeView"> | ||||
|         <views:CatalogView.Icon> | ||||
|             <OnPlatform         | ||||
|                 x:TypeArguments="FileImageSource" | ||||
|                 Android="menu_filter" | ||||
|                 iOS="menu_filter" | ||||
|                 WinPhone="Assets\menu_filter.png"/> | ||||
|             <OnPlatform x:TypeArguments="FileImageSource"> | ||||
|                 <On Platform="iOS, Android" Value="menu_filter" /> | ||||
|                 <On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_filter.png" /> | ||||
|             </OnPlatform> | ||||
|         </views:CatalogView.Icon> | ||||
|     </views:CatalogView> | ||||
|     <!-- PROFILE --> | ||||
|     <views:ProfileView | ||||
|         x:Name="ProfileView"> | ||||
|         <views:ProfileView.Icon> | ||||
|             <OnPlatform         | ||||
|                 x:TypeArguments="FileImageSource" | ||||
|                 Android="menu_profile" | ||||
|                 iOS="menu_profile" | ||||
|                 WinPhone="Assets\menu_profile.png"/> | ||||
|             <OnPlatform x:TypeArguments="FileImageSource"> | ||||
|                 <On Platform="iOS, Android" Value="menu_profile" /> | ||||
|                 <On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_profile.png" /> | ||||
|             </OnPlatform> | ||||
|         </views:ProfileView.Icon> | ||||
|     </views:ProfileView> | ||||
|     <!-- BASKET --> | ||||
| @ -56,22 +52,20 @@ | ||||
|         controls:CustomTabbedPage.BadgeText="{Binding BadgeCount}" | ||||
|         controls:CustomTabbedPage.BadgeColor="{StaticResource LightGreenColor}"> | ||||
|         <views:BasketView.Icon> | ||||
|             <OnPlatform         | ||||
|                 x:TypeArguments="FileImageSource" | ||||
|                 Android="menu_cart" | ||||
|                 iOS="menu_cart" | ||||
|                 WinPhone="Assets\menu_cart.png"/> | ||||
|             <OnPlatform x:TypeArguments="FileImageSource"> | ||||
|                 <On Platform="iOS, Android" Value="menu_cart" /> | ||||
|                 <On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_cart.png" /> | ||||
|             </OnPlatform> | ||||
|         </views:BasketView.Icon> | ||||
|     </views:BasketView> | ||||
|     <!-- CAMPAIGNS --> | ||||
|     <views:CampaignView   | ||||
|         x:Name="CampaignView"> | ||||
|         <views:CampaignView.Icon> | ||||
|             <OnPlatform         | ||||
|                 x:TypeArguments="FileImageSource" | ||||
|                 Android="menu_campaigns" | ||||
|                 iOS="menu_campaigns" | ||||
|                 WinPhone="Assets\menu_campaigns.png"/> | ||||
|             <OnPlatform x:TypeArguments="FileImageSource"> | ||||
|                 <On Platform="iOS, Android" Value="menu_campaigns" /> | ||||
|                 <On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_campaigns.png" /> | ||||
|             </OnPlatform> | ||||
|         </views:CampaignView.Icon> | ||||
|     </views:CampaignView> | ||||
| </TabbedPage> | ||||
| @ -264,11 +264,10 @@ | ||||
|                 VerticalOptions="Center" | ||||
|                 HorizontalOptions="Center"> | ||||
|                 <ActivityIndicator.WidthRequest> | ||||
|                     <OnPlatform  | ||||
|                         x:TypeArguments="x:Double"  | ||||
|                         iOS="100"  | ||||
|                         Android="100" | ||||
|                         WinPhone="400" /> | ||||
|                     <OnPlatform x:TypeArguments="x:Double"> | ||||
|                         <On Platform="iOS, Android" Value="100" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="400" /> | ||||
|                     </OnPlatform> | ||||
|                 </ActivityIndicator.WidthRequest> | ||||
|             </ActivityIndicator> | ||||
|         </Grid> | ||||
|  | ||||
| @ -99,11 +99,10 @@ | ||||
|                 <animations:FadeInAnimation  | ||||
|                     Direction="Up"> | ||||
|                     <animations:FadeInAnimation.Duration> | ||||
|                         <OnPlatform  | ||||
|                             x:TypeArguments="x:String"  | ||||
|                             Android="500" | ||||
|                             iOS="0" | ||||
|                             WinPhone="500"/> | ||||
|                         <OnPlatform x:TypeArguments="x:String"> | ||||
|                             <On Platform="iOS" Value="0" /> | ||||
|                             <On Platform="Android, UWP, WinRT, WinPhone" Value="500" /> | ||||
|                         </OnPlatform> | ||||
|                     </animations:FadeInAnimation.Duration> | ||||
|                 </animations:FadeInAnimation> | ||||
|             </animations:StoryBoard> | ||||
| @ -156,16 +155,18 @@ | ||||
|                         Command="{Binding ToggleMockServicesCommand}" | ||||
|                         Style="{StaticResource SettingsToggleButtonStyle}"> | ||||
|                         <controls:ToggleButton.CheckedImage> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource" | ||||
|                               Android="switch_on.png" | ||||
|                               iOS="switchOn.png" | ||||
|                               WinPhone="Assets/switchOn.png"/> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                                 <On Platform="iOS" Value="switchOn.png" /> | ||||
|                                 <On Platform="Android" Value="switch_on.png" /> | ||||
|                                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOn.png" /> | ||||
|                             </OnPlatform> | ||||
|                         </controls:ToggleButton.CheckedImage> | ||||
|                         <controls:ToggleButton.UnCheckedImage> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource" | ||||
|                               Android="switch_off.png" | ||||
|                               iOS="switchOff.png" | ||||
|                               WinPhone="Assets/switchOff.png"/> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                                 <On Platform="iOS" Value="switchOff.png" /> | ||||
|                                 <On Platform="Android" Value="switch_off.png" /> | ||||
|                                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOff.png" /> | ||||
|                             </OnPlatform> | ||||
|                         </controls:ToggleButton.UnCheckedImage> | ||||
|                     </controls:ToggleButton> | ||||
|                     <!-- ENDPOINT --> | ||||
| @ -181,11 +182,10 @@ | ||||
|                         <Entry | ||||
|                             Text="{Binding Endpoint, Mode=TwoWay}"> | ||||
|                             <Entry.Style> | ||||
|                                 <OnPlatform  | ||||
|                                     x:TypeArguments="Style" | ||||
|                                     iOS="{StaticResource SettingsEntryStyle}" | ||||
|                                     Android="{StaticResource SettingsEntryStyle}" | ||||
|                                     WinPhone="{StaticResource SettingsUwpEntryStyle}"/> | ||||
|                                 <OnPlatform x:TypeArguments="Style"> | ||||
|                                     <On Platform="iOS, Android" Value="{StaticResource SettingsEntryStyle}" /> | ||||
|                                     <On Platform="UWP, WinRT, WinPhone" Value="{StaticResource SettingsUwpEntryStyle}" /> | ||||
|                                 </OnPlatform> | ||||
|                             </Entry.Style> | ||||
|                         </Entry> | ||||
|                     </StackLayout> | ||||
| @ -212,16 +212,18 @@ | ||||
|                         Style="{StaticResource SettingsToggleButtonStyle}" | ||||
|                         IsVisible="{Binding UserIsLogged}"> | ||||
|                         <controls:ToggleButton.CheckedImage> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource" | ||||
|                               Android="switch_on.png" | ||||
|                               iOS="switchOn.png" | ||||
|                               WinPhone="Assets/switchOn.png"/> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                                 <On Platform="iOS" Value="switchOn.png" /> | ||||
|                                 <On Platform="Android" Value="switch_on.png" /> | ||||
|                                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOn.png" /> | ||||
|                             </OnPlatform> | ||||
|                         </controls:ToggleButton.CheckedImage> | ||||
|                         <controls:ToggleButton.UnCheckedImage> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource" | ||||
|                               Android="switch_off.png" | ||||
|                               iOS="switchOff.png" | ||||
|                               WinPhone="Assets/switchOff.png"/> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                                 <On Platform="iOS" Value="switchOff.png" /> | ||||
|                                 <On Platform="Android" Value="switch_off.png" /> | ||||
|                                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOff.png" /> | ||||
|                             </OnPlatform> | ||||
|                         </controls:ToggleButton.UnCheckedImage> | ||||
|                     </controls:ToggleButton> | ||||
|                     <!-- FAKE LOCATIONS --> | ||||
| @ -238,11 +240,10 @@ | ||||
|                             Text="{Binding Latitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" | ||||
|                             Keyboard="Text"> | ||||
|                             <Entry.Style> | ||||
|                                 <OnPlatform  | ||||
|                                     x:TypeArguments="Style" | ||||
|                                     iOS="{StaticResource SettingsEntryStyle}" | ||||
|                                     Android="{StaticResource SettingsEntryStyle}" | ||||
|                                     WinPhone="{StaticResource SettingsUwpEntryStyle}"/> | ||||
|                                 <OnPlatform x:TypeArguments="Style"> | ||||
|                                     <On Platform="iOS, Android" Value="{StaticResource SettingsEntryStyle}" /> | ||||
|                                     <On Platform="UWP, WinRT, WinPhone" Value="{StaticResource SettingsUwpEntryStyle}" /> | ||||
|                                 </OnPlatform> | ||||
|                             </Entry.Style> | ||||
|                         </Entry> | ||||
|                         <Label | ||||
| @ -252,11 +253,10 @@ | ||||
|                             Text="{Binding Longitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" | ||||
|                             Keyboard="Text"> | ||||
|                             <Entry.Style> | ||||
|                                 <OnPlatform  | ||||
|                                     x:TypeArguments="Style" | ||||
|                                     iOS="{StaticResource SettingsEntryStyle}" | ||||
|                                     Android="{StaticResource SettingsEntryStyle}" | ||||
|                                     WinPhone="{StaticResource SettingsUwpEntryStyle}"/> | ||||
|                                 <OnPlatform x:TypeArguments="Style"> | ||||
|                                     <On Platform="iOS, Android" Value="{StaticResource SettingsEntryStyle}" /> | ||||
|                                     <On Platform="UWP, WinRT, WinPhone" Value="{StaticResource SettingsUwpEntryStyle}" /> | ||||
|                                 </OnPlatform> | ||||
|                             </Entry.Style> | ||||
|                         </Entry> | ||||
|                         <Button  | ||||
| @ -289,16 +289,18 @@ | ||||
|                         Style="{StaticResource SettingsToggleButtonStyle}" | ||||
|                         IsVisible="{Binding UseFakeLocation, Converter={StaticResource InverseBoolConverter}}"> | ||||
|                         <controls:ToggleButton.CheckedImage> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource" | ||||
|                                         Android="switch_on.png" | ||||
|                                         iOS="switchOn.png" | ||||
|                                         WinPhone="Assets/switchOn.png"/> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                                 <On Platform="iOS" Value="switchOn.png" /> | ||||
|                                 <On Platform="Android" Value="switch_on.png" /> | ||||
|                                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOn.png" /> | ||||
|                             </OnPlatform> | ||||
|                         </controls:ToggleButton.CheckedImage> | ||||
|                         <controls:ToggleButton.UnCheckedImage> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource" | ||||
|                                         Android="switch_off.png" | ||||
|                                         iOS="switchOff.png" | ||||
|                                         WinPhone="Assets/switchOff.png"/> | ||||
|                             <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                                 <On Platform="iOS" Value="switchOff.png" /> | ||||
|                                 <On Platform="Android" Value="switch_off.png" /> | ||||
|                                 <On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOff.png" /> | ||||
|                             </OnPlatform> | ||||
|                         </controls:ToggleButton.UnCheckedImage> | ||||
|                     </controls:ToggleButton> | ||||
|                 </Grid> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ContentView | ||||
|     xmlns="http://xamarin.com/schemas/2014/forms"      | ||||
|     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"   | ||||
| @ -71,11 +71,10 @@ | ||||
|         <Grid    | ||||
|             BackgroundColor="{StaticResource BackgroundColor}"> | ||||
|             <Grid.Padding> | ||||
|                 <OnPlatform          | ||||
|                     x:TypeArguments="Thickness" | ||||
|                     Android="0" | ||||
|                     iOS="0" | ||||
|                     WinPhone="12, 0"/> | ||||
|                 <OnPlatform x:TypeArguments="Thickness"> | ||||
|                     <On Platform="iOS, Android" Value="0" /> | ||||
|                     <On Platform="UWP, WinRT, WinPhone" Value="12, 0" /> | ||||
|                 </OnPlatform> | ||||
|             </Grid.Padding> | ||||
|             <Grid.ColumnDefinitions> | ||||
|                 <ColumnDefinition Width="Auto" /> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ContentView  | ||||
|     xmlns="http://xamarin.com/schemas/2014/forms"  | ||||
|     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"     | ||||
| @ -59,18 +59,16 @@ | ||||
|                 Source="{Binding PictureUri}"      | ||||
|                 Aspect="AspectFill"> | ||||
|                 <ffimageloading:CachedImage.LoadingPlaceholder> | ||||
|                     <OnPlatform    | ||||
|                         x:TypeArguments="ImageSource" | ||||
|                         iOS="default_campaign" | ||||
|                         Android="default_campaign" | ||||
|                         WinPhone="Assets/default_campaign.png"/> | ||||
|                     <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                         <On Platform="iOS, Android" Value="default_campaign" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="Assets/default_campaign.png" /> | ||||
|                     </OnPlatform> | ||||
|                 </ffimageloading:CachedImage.LoadingPlaceholder> | ||||
|                 <ffimageloading:CachedImage.ErrorPlaceholder> | ||||
|                     <OnPlatform    | ||||
|                         x:TypeArguments="ImageSource" | ||||
|                         iOS="noimage" | ||||
|                         Android="noimage" | ||||
|                         WinPhone="Assets/noimage.png"/> | ||||
|                     <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                         <On Platform="iOS, Android" Value="noimage" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="Assets/noimage.png" /> | ||||
|                     </OnPlatform> | ||||
|                 </ffimageloading:CachedImage.ErrorPlaceholder> | ||||
|             </ffimageloading:CachedImage> | ||||
|             <!-- NAME --> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ContentView  | ||||
|     xmlns="http://xamarin.com/schemas/2014/forms"  | ||||
|     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"     | ||||
| @ -76,18 +76,16 @@ | ||||
|                 Source="{Binding PictureUri}"      | ||||
|                 Aspect="AspectFill"> | ||||
|                 <ffimageloading:CachedImage.LoadingPlaceholder> | ||||
|                     <OnPlatform    | ||||
|                         x:TypeArguments="ImageSource" | ||||
|                         iOS="default_product" | ||||
|                         Android="default_product" | ||||
|                         WinPhone="Assets/default_product.png"/> | ||||
|                     <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                         <On Platform="iOS, Android" Value="default_product" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="Assets/default_product.png" /> | ||||
|                     </OnPlatform> | ||||
|                 </ffimageloading:CachedImage.LoadingPlaceholder> | ||||
|                 <ffimageloading:CachedImage.ErrorPlaceholder> | ||||
|                     <OnPlatform    | ||||
|                         x:TypeArguments="ImageSource" | ||||
|                         iOS="noimage" | ||||
|                         Android="noimage" | ||||
|                         WinPhone="Assets/noimage.png"/> | ||||
|                     <OnPlatform x:TypeArguments="ImageSource"> | ||||
|                         <On Platform="iOS, Android" Value="noimage" /> | ||||
|                         <On Platform="UWP, WinRT, WinPhone" Value="Assets/noimage.png" /> | ||||
|                     </OnPlatform> | ||||
|                 </ffimageloading:CachedImage.ErrorPlaceholder> | ||||
|             </ffimageloading:CachedImage> | ||||
|             <Grid | ||||
|  | ||||
| @ -257,6 +257,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API | ||||
| 
 | ||||
|         private void RegisterEventBus(IServiceCollection services) | ||||
|         { | ||||
|             var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|             if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
| @ -264,8 +266,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API | ||||
|                     var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                     var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();                     | ||||
| 
 | ||||
|                     return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                         eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | ||||
| @ -286,7 +287,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API | ||||
|                         retryCount = int.Parse(Configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
| 
 | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount); | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -180,7 +180,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers | ||||
|             catalogItem = productToUpdate; | ||||
|             _catalogContext.CatalogItems.Update(catalogItem); | ||||
| 
 | ||||
|             if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed | ||||
|             if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed | ||||
|             { | ||||
|                 //Create Integration Event to be published through the Event Bus | ||||
|                 var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); | ||||
| @ -191,7 +191,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers | ||||
|                 // Publish through the Event Bus and mark the saved event as published | ||||
|                 await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); | ||||
|             } | ||||
|             else // Save updated product | ||||
|             else // Just save the updated product because the Product's Price hasn't changed. | ||||
|             { | ||||
|                 await _catalogContext.SaveChangesAsync(); | ||||
|             } | ||||
|  | ||||
| @ -224,6 +224,8 @@ | ||||
| 
 | ||||
|         private void RegisterEventBus(IServiceCollection services) | ||||
|         { | ||||
|             var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|             if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
| @ -232,7 +234,6 @@ | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                     var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                     return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                         eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | ||||
| @ -254,7 +255,7 @@ | ||||
|                         retryCount = int.Parse(Configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
| 
 | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount); | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|       "Default": "Debug", | ||||
|       "Default": "Trace", | ||||
|       "System": "Information", | ||||
|       "Microsoft": "Information" | ||||
|     } | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|       "Default": "Debug", | ||||
|       "Default": "Trace", | ||||
|       "System": "Information", | ||||
|       "Microsoft": "Information" | ||||
|     } | ||||
|  | ||||
| @ -227,6 +227,8 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API | ||||
| 
 | ||||
|         private void RegisterEventBus(IServiceCollection services) | ||||
|         { | ||||
|             var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|             if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
| @ -235,7 +237,6 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                     var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                     return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                         eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | ||||
| @ -256,7 +257,7 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API | ||||
|                         retryCount = int.Parse(Configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
| 
 | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount); | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|       "Default": "Debug", | ||||
|       "Default": "Trace", | ||||
|       "System": "Information", | ||||
|       "Microsoft": "Information" | ||||
|     } | ||||
|  | ||||
| @ -246,8 +246,10 @@ | ||||
|                 }); | ||||
|         } | ||||
| 
 | ||||
|             private void RegisterEventBus(IServiceCollection services) | ||||
|         private void RegisterEventBus(IServiceCollection services) | ||||
|         { | ||||
|             var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|             if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
| @ -255,8 +257,7 @@ | ||||
|                     var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                     var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();                     | ||||
| 
 | ||||
|                     return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                         eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | ||||
| @ -277,7 +278,7 @@ | ||||
|                         retryCount = int.Parse(Configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
| 
 | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount); | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|       "Default": "Warning" | ||||
|       "Default": "Trace" | ||||
|     } | ||||
|   }, | ||||
|   "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word", | ||||
|  | ||||
| @ -9,18 +9,7 @@ using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| { | ||||
|     public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler<CancelOrderCommand, bool> | ||||
|     { | ||||
|         public CancelOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Regular CommandHandler | ||||
|     public class CancelOrderCommandHandler : IAsyncRequestHandler<CancelOrderCommand, bool> | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| @ -43,4 +32,18 @@ namespace Ordering.API.Application.Commands | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool> | ||||
|     { | ||||
|         public CancelOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -7,19 +7,7 @@ | ||||
|     using System; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
| 
 | ||||
|     public class CreateOrderCommandIdentifiedHandler : IdentifierCommandHandler<CreateOrderCommand, bool> | ||||
|     { | ||||
|         public CreateOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for creating order. | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Regular CommandHandler | ||||
|     public class CreateOrderCommandHandler | ||||
|         : IAsyncRequestHandler<CreateOrderCommand, bool> | ||||
|     { | ||||
| @ -55,4 +43,18 @@ | ||||
|                 .SaveEntitiesAsync(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool> | ||||
|     { | ||||
|         public CreateOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for creating order. | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -10,13 +10,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
|     /// </summary> | ||||
|     /// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam> | ||||
|     /// <typeparam name="R">Return value of the inner command handler</typeparam> | ||||
|     public class IdentifierCommandHandler<T, R> : IAsyncRequestHandler<IdentifiedCommand<T, R>, R> | ||||
|     public class IdentifiedCommandHandler<T, R> : IAsyncRequestHandler<IdentifiedCommand<T, R>, R> | ||||
|         where T : IRequest<R> | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly IRequestManager _requestManager; | ||||
| 
 | ||||
|         public IdentifierCommandHandler(IMediator mediator, IRequestManager requestManager) | ||||
|         public IdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) | ||||
|         { | ||||
|             _mediator = mediator; | ||||
|             _requestManager = requestManager; | ||||
| @ -48,6 +48,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
|             { | ||||
|                 await _requestManager.CreateRequestForCommandAsync<T>(message.Id); | ||||
| 
 | ||||
|                 // Send the embeded business command to mediator so it runs its related CommandHandler  | ||||
|                 var result = await _mediator.Send(message.Command); | ||||
|                  | ||||
|                 return result; | ||||
| @ -6,18 +6,7 @@ using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| { | ||||
|     public class ShipOrderCommandIdentifiedHandler : IdentifierCommandHandler<ShipOrderCommand, bool> | ||||
|     { | ||||
|         public ShipOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Regular CommandHandler | ||||
|     public class ShipOrderCommandHandler : IAsyncRequestHandler<ShipOrderCommand, bool> | ||||
|     {         | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| @ -40,4 +29,18 @@ namespace Ordering.API.Application.Commands | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool> | ||||
|     { | ||||
|         public ShipOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -25,7 +25,6 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent | ||||
|         public async Task Handle(OrderStartedDomainEvent orderStartedEvent) | ||||
|         { | ||||
|             var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1; | ||||
| 
 | ||||
|             var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId); | ||||
|             bool buyerOriginallyExisted = (buyer == null) ? false : true; | ||||
| 
 | ||||
|  | ||||
| @ -5,10 +5,10 @@ | ||||
| 
 | ||||
|     public interface IOrderQueries | ||||
|     { | ||||
|         Task<dynamic> GetOrderAsync(int id); | ||||
|         Task<Order> GetOrderAsync(int id); | ||||
| 
 | ||||
|         Task<IEnumerable<dynamic>> GetOrdersAsync(); | ||||
|         Task<IEnumerable<OrderSummary>> GetOrdersAsync(); | ||||
| 
 | ||||
|         Task<IEnumerable<dynamic>> GetCardTypesAsync(); | ||||
|         Task<IEnumerable<CardType>> GetCardTypesAsync(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,9 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries | ||||
| { | ||||
|     using Dapper; | ||||
|     using Microsoft.Extensions.Configuration; | ||||
|     using System.Data.SqlClient; | ||||
|     using System.Threading.Tasks; | ||||
|     using System; | ||||
|     using System.Dynamic; | ||||
|     using System.Collections.Generic; | ||||
| 
 | ||||
|     public class OrderQueries | ||||
| @ -19,7 +17,7 @@ | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public async Task<dynamic> GetOrderAsync(int id) | ||||
|         public async Task<Order> GetOrderAsync(int id) | ||||
|         { | ||||
|             using (var connection = new SqlConnection(_connectionString)) | ||||
|             { | ||||
| @ -44,13 +42,13 @@ | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public async Task<IEnumerable<dynamic>> GetOrdersAsync() | ||||
|         public async Task<IEnumerable<OrderSummary>> GetOrdersAsync() | ||||
|         { | ||||
|             using (var connection = new SqlConnection(_connectionString)) | ||||
|             { | ||||
|                 connection.Open(); | ||||
| 
 | ||||
|                 return await connection.QueryAsync<dynamic>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status],SUM(oi.units*oi.unitprice) as total
 | ||||
|                 return await connection.QueryAsync<OrderSummary>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status],SUM(oi.units*oi.unitprice) as total
 | ||||
|                      FROM [ordering].[Orders] o | ||||
|                      LEFT JOIN[ordering].[orderitems] oi ON  o.Id = oi.orderid  | ||||
|                      LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id                      | ||||
| @ -59,39 +57,41 @@ | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public async Task<IEnumerable<dynamic>> GetCardTypesAsync() | ||||
|         public async Task<IEnumerable<CardType>> GetCardTypesAsync() | ||||
|         { | ||||
|             using (var connection = new SqlConnection(_connectionString)) | ||||
|             { | ||||
|                 connection.Open(); | ||||
| 
 | ||||
|                 return await connection.QueryAsync<dynamic>("SELECT * FROM ordering.cardtypes"); | ||||
|                 return await connection.QueryAsync<CardType>("SELECT * FROM ordering.cardtypes"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private dynamic MapOrderItems(dynamic result) | ||||
|         private Order MapOrderItems(dynamic result) | ||||
|         { | ||||
|             dynamic order = new ExpandoObject(); | ||||
| 
 | ||||
|             order.ordernumber = result[0].ordernumber; | ||||
|             order.date = result[0].date; | ||||
|             order.status = result[0].status; | ||||
|             order.description = result[0].description; | ||||
|             order.street = result[0].street; | ||||
|             order.city = result[0].city; | ||||
|             order.zipcode = result[0].zipcode; | ||||
|             order.country = result[0].country; | ||||
| 
 | ||||
|             order.orderitems = new List<dynamic>(); | ||||
|             order.total = 0; | ||||
|             var order = new Order | ||||
|             { | ||||
|                 ordernumber = result[0].ordernumber, | ||||
|                 date = result[0].date, | ||||
|                 status = result[0].status, | ||||
|                 description = result[0].description, | ||||
|                 street = result[0].street, | ||||
|                 city = result[0].city, | ||||
|                 zipcode = result[0].zipcode, | ||||
|                 country = result[0].country, | ||||
|                 orderitems = new List<Orderitem>(), | ||||
|                 total = 0 | ||||
|             }; | ||||
| 
 | ||||
|             foreach (dynamic item in result) | ||||
|             { | ||||
|                 dynamic orderitem = new ExpandoObject(); | ||||
|                 orderitem.productname = item.productname; | ||||
|                 orderitem.units = item.units; | ||||
|                 orderitem.unitprice = item.unitprice; | ||||
|                 orderitem.pictureurl = item.pictureurl; | ||||
|                 var orderitem = new Orderitem | ||||
|                 { | ||||
|                     productname = item.productname, | ||||
|                     units = item.units, | ||||
|                     unitprice = (double)item.unitprice, | ||||
|                     pictureurl = item.pictureurl | ||||
|                 }; | ||||
| 
 | ||||
|                 order.total += item.units * item.unitprice; | ||||
|                 order.orderitems.Add(orderitem); | ||||
|  | ||||
| @ -0,0 +1,41 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries | ||||
| { | ||||
|     public class Orderitem | ||||
|     { | ||||
|         public string productname { get; set; } | ||||
|         public int units { get; set; } | ||||
|         public double unitprice { get; set; } | ||||
|         public string pictureurl { get; set; } | ||||
|     } | ||||
| 
 | ||||
|     public class Order | ||||
|     { | ||||
|         public int ordernumber { get; set; } | ||||
|         public DateTime date { get; set; } | ||||
|         public string status { get; set; } | ||||
|         public string description { get; set; } | ||||
|         public string street { get; set; } | ||||
|         public string city { get; set; } | ||||
|         public string zipcode { get; set; } | ||||
|         public string country { get; set; } | ||||
|         public List<Orderitem> orderitems { get; set; } | ||||
|         public decimal total { get; set; } | ||||
|     } | ||||
| 
 | ||||
|     public class OrderSummary | ||||
|     { | ||||
|         public int ordernumber { get; set; } | ||||
|         public DateTime date { get; set; } | ||||
|         public string status { get; set; } | ||||
|         public double total { get; set; } | ||||
|     } | ||||
| 
 | ||||
|     public class CardType | ||||
|     { | ||||
|         public int Id { get; set; } | ||||
|         public string Name { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -3,9 +3,9 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Validations | ||||
| { | ||||
|     public class IdentifierCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand,bool>> | ||||
|     public class IdentifiedCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand,bool>> | ||||
|     { | ||||
|         public IdentifierCommandValidator() | ||||
|         public IdentifiedCommandValidator() | ||||
|         { | ||||
|             RuleFor(command => command.Id).NotEmpty();     | ||||
|         } | ||||
| @ -64,7 +64,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers | ||||
| 
 | ||||
|         [Route("{orderId:int}")] | ||||
|         [HttpGet] | ||||
|         [ProducesResponseType((int)HttpStatusCode.OK)] | ||||
|         [ProducesResponseType(typeof(Order),(int)HttpStatusCode.OK)] | ||||
|         [ProducesResponseType((int)HttpStatusCode.NotFound)] | ||||
|         public async Task<IActionResult> GetOrder(int orderId) | ||||
|         { | ||||
| @ -83,6 +83,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers | ||||
| 
 | ||||
|         [Route("")] | ||||
|         [HttpGet] | ||||
|         [ProducesResponseType(typeof(IEnumerable<OrderSummary>), (int)HttpStatusCode.OK)] | ||||
|         public async Task<IActionResult> GetOrders() | ||||
|         { | ||||
|             var orderTask = _orderQueries.GetOrdersAsync(); | ||||
| @ -94,6 +95,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers | ||||
| 
 | ||||
|         [Route("cardtypes")] | ||||
|         [HttpGet] | ||||
|         [ProducesResponseType(typeof(IEnumerable<CardType>), (int)HttpStatusCode.OK)] | ||||
|         public async Task<IActionResult> GetCardTypes() | ||||
|         { | ||||
|             var cardTypes = await _orderQueries | ||||
|  | ||||
| @ -23,11 +23,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof | ||||
|             builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly) | ||||
|                 .AsClosedTypesOf(typeof(IAsyncRequestHandler<,>)); | ||||
| 
 | ||||
|             // Register all the event classes (they implement IAsyncNotificationHandler) in assembly holding the Commands | ||||
|             // Register the DomainEventHandler classes (they implement IAsyncNotificationHandler<>) in assembly holding the Domain Events | ||||
|             builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) | ||||
|                 .AsClosedTypesOf(typeof(IAsyncNotificationHandler<>)); | ||||
| 
 | ||||
| 
 | ||||
|             // Register the Command's Validators (Validators based on FluentValidation library) | ||||
|             builder | ||||
|                 .RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly) | ||||
|                 .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) | ||||
|  | ||||
| @ -294,6 +294,8 @@ | ||||
| 
 | ||||
|         private void RegisterEventBus(IServiceCollection services) | ||||
|         { | ||||
|             var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|             if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
| @ -301,8 +303,7 @@ | ||||
|                     var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                     var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();                     | ||||
| 
 | ||||
|                     return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                         eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | ||||
| @ -323,7 +324,7 @@ | ||||
|                         retryCount = int.Parse(Configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
| 
 | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount); | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|       "Default": "Debug", | ||||
|       "Default": "Trace", | ||||
|       "System": "Information", | ||||
|       "Microsoft": "Information" | ||||
|     } | ||||
|  | ||||
| @ -4,17 +4,12 @@ using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate | ||||
| { | ||||
|     public class Address | ||||
|         :ValueObject | ||||
|     public class Address : ValueObject | ||||
|     { | ||||
|         public String Street { get; private set; } | ||||
| 
 | ||||
|         public String City { get; private set; } | ||||
| 
 | ||||
|         public String State { get; private set; } | ||||
| 
 | ||||
|         public String Country { get; private set; } | ||||
| 
 | ||||
|         public String ZipCode { get; private set; } | ||||
| 
 | ||||
|         private Address() { } | ||||
| @ -30,6 +25,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O | ||||
| 
 | ||||
|         protected override IEnumerable<object> GetAtomicValues() | ||||
|         { | ||||
|             // Using a yield return statement to return each element one at a time | ||||
|             yield return Street; | ||||
|             yield return City; | ||||
|             yield return State; | ||||
|  | ||||
| @ -15,6 +15,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O | ||||
|         // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections) | ||||
|         private DateTime _orderDate; | ||||
| 
 | ||||
|         // Address is a Value Object pattern example persisted as EF Core 2.0 owned entity | ||||
|         public Address Address { get; private set; } | ||||
| 
 | ||||
|         private int? _buyerId; | ||||
| @ -29,12 +30,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O | ||||
|         // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, | ||||
|         // but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour. | ||||
|         private readonly List<OrderItem> _orderItems; | ||||
| 
 | ||||
|         public IReadOnlyCollection<OrderItem> OrderItems => _orderItems; | ||||
|         // Using List<>.AsReadOnly()  | ||||
|         // This will create a read only wrapper around the private list so is protected against "external updates". | ||||
|         // It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance) | ||||
|         //https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx  | ||||
| 
 | ||||
|         private int? _paymentMethodId; | ||||
| 
 | ||||
| @ -177,16 +173,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O | ||||
|         private void AddOrderStartedDomainEvent(string userId, int cardTypeId, string cardNumber, | ||||
|                 string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) | ||||
|         { | ||||
|             var orderStartedDomainEvent = new OrderStartedDomainEvent( | ||||
|                 this, userId, cardTypeId, cardNumber, cardSecurityNumber, | ||||
|                 cardHolderName, cardExpiration); | ||||
|             var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, cardTypeId,  | ||||
|                                                                       cardNumber, cardSecurityNumber, | ||||
|                                                                       cardHolderName, cardExpiration); | ||||
| 
 | ||||
|             this.AddDomainEvent(orderStartedDomainEvent); | ||||
|         } | ||||
| 
 | ||||
|         private void StatusChangeException(OrderStatus orderStatusToChange) | ||||
|         { | ||||
|             throw new OrderingDomainException($"Not possible to change order status from {OrderStatus.Name} to {orderStatusToChange.Name}."); | ||||
|             throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}."); | ||||
|         } | ||||
| 
 | ||||
|         public decimal GetTotal() | ||||
|  | ||||
| @ -9,8 +9,7 @@ namespace Ordering.Domain.Events | ||||
|     /// <summary> | ||||
|     /// Event used when an order is created | ||||
|     /// </summary> | ||||
|     public class OrderStartedDomainEvent | ||||
|         : INotification | ||||
|     public class OrderStartedDomainEvent : INotification | ||||
|     { | ||||
|         public string UserId { get; private set; } | ||||
|         public int CardTypeId { get; private set; } | ||||
| @ -21,9 +20,9 @@ namespace Ordering.Domain.Events | ||||
|         public Order Order { get; private set; } | ||||
| 
 | ||||
|         public OrderStartedDomainEvent(Order order, string userId, | ||||
|             int cardTypeId, string cardNumber,  | ||||
|             string cardSecurityNumber, string cardHolderName,  | ||||
|             DateTime cardExpiration) | ||||
|                                        int cardTypeId, string cardNumber,  | ||||
|                                        string cardSecurityNumber, string cardHolderName,  | ||||
|                                        DateTime cardExpiration) | ||||
|         { | ||||
|             Order = order; | ||||
|             UserId = userId; | ||||
|  | ||||
| @ -6,12 +6,8 @@ | ||||
| 
 | ||||
|     public abstract class Entity | ||||
|     { | ||||
| 
 | ||||
|         int? _requestedHashCode; | ||||
|         int _Id; | ||||
|          | ||||
|         private List<INotification> _domainEvents; | ||||
| 
 | ||||
|         int _Id;         | ||||
|         public virtual  int Id  | ||||
|         { | ||||
|             get | ||||
| @ -24,13 +20,14 @@ | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public List<INotification> DomainEvents => _domainEvents;         | ||||
|         private List<INotification> _domainEvents; | ||||
|         public List<INotification> DomainEvents => _domainEvents;        | ||||
|          | ||||
|         public void AddDomainEvent(INotification eventItem) | ||||
|         { | ||||
|             _domainEvents = _domainEvents ?? new List<INotification>(); | ||||
|             _domainEvents.Add(eventItem); | ||||
|         } | ||||
| 
 | ||||
|         public void RemoveDomainEvent(INotification eventItem) | ||||
|         { | ||||
|             if (_domainEvents is null) return; | ||||
| @ -74,7 +71,6 @@ | ||||
|                 return base.GetHashCode(); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public static bool operator ==(Entity left, Entity right) | ||||
|         { | ||||
|             if (Object.Equals(left, null)) | ||||
|  | ||||
| @ -14,16 +14,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork | ||||
|             return ReferenceEquals(left, null) || left.Equals(right); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         protected static bool NotEqualOperator(ValueObject left, ValueObject right) | ||||
|         { | ||||
|             return !(EqualOperator(left, right)); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         protected abstract IEnumerable<object> GetAtomicValues(); | ||||
| 
 | ||||
| 
 | ||||
|         public override bool Equals(object obj) | ||||
|         { | ||||
|             if (obj == null || obj.GetType() != GetType()) | ||||
| @ -47,7 +44,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork | ||||
|             return !thisValues.MoveNext() && !otherValues.MoveNext(); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public override int GetHashCode() | ||||
|         { | ||||
|             return GetAtomicValues() | ||||
|  | ||||
| @ -7,8 +7,7 @@ using System; | ||||
| 
 | ||||
| namespace Ordering.Infrastructure.EntityConfigurations | ||||
| { | ||||
|     class OrderEntityTypeConfiguration | ||||
|         : IEntityTypeConfiguration<Order> | ||||
|     class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order> | ||||
|     { | ||||
|         public void Configure(EntityTypeBuilder<Order> orderConfiguration) | ||||
|         { | ||||
| @ -21,6 +20,7 @@ namespace Ordering.Infrastructure.EntityConfigurations | ||||
|             orderConfiguration.Property(o => o.Id) | ||||
|                 .ForSqlServerUseSequenceHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA); | ||||
| 
 | ||||
|             //Address value object persisted as owned entity type supported since EF Core 2.0 | ||||
|             orderConfiguration.OwnsOne(o => o.Address); | ||||
| 
 | ||||
|             orderConfiguration.Property<DateTime>("OrderDate").IsRequired(); | ||||
| @ -32,7 +32,7 @@ namespace Ordering.Infrastructure.EntityConfigurations | ||||
|             var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems)); | ||||
|              | ||||
|             // DDD Patterns comment: | ||||
|             //Set as Field (New since EF 1.1) to access the OrderItem collection property through its field | ||||
|             //Set as field (New since EF 1.1) to access the OrderItem collection property through its field | ||||
|             navigation.SetPropertyAccessMode(PropertyAccessMode.Field); | ||||
| 
 | ||||
|             orderConfiguration.HasOne<PaymentMethod>() | ||||
|  | ||||
| @ -12,22 +12,14 @@ using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure | ||||
| { | ||||
|     public class OrderingContext | ||||
|       : DbContext,IUnitOfWork | ||||
| 
 | ||||
|     public class OrderingContext : DbContext, IUnitOfWork | ||||
|     { | ||||
|         public const string DEFAULT_SCHEMA = "ordering"; | ||||
| 
 | ||||
|         public DbSet<Order> Orders { get; set; } | ||||
| 
 | ||||
|         public DbSet<OrderItem> OrderItems { get; set; } | ||||
| 
 | ||||
|         public DbSet<PaymentMethod> Payments { get; set; } | ||||
| 
 | ||||
|         public DbSet<Buyer> Buyers { get; set; } | ||||
| 
 | ||||
|         public DbSet<CardType> CardTypes { get; set; } | ||||
| 
 | ||||
|         public DbSet<OrderStatus> OrderStatus { get; set; } | ||||
| 
 | ||||
|         private readonly IMediator _mediator; | ||||
| @ -63,7 +55,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure | ||||
|             // You will need to handle eventual consistency and compensatory actions in case of failures in any of the Handlers.  | ||||
|             await _mediator.DispatchDomainEventsAsync(this); | ||||
| 
 | ||||
| 
 | ||||
|             // After executing this line all the changes (from the Command Handler and Domain Event Handlers)  | ||||
|             // performed throught the DbContext will be commited | ||||
|             var result = await base.SaveChangesAsync(); | ||||
|  | ||||
| @ -11,7 +11,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor | ||||
|         : IBuyerRepository | ||||
|     { | ||||
|         private readonly OrderingContext _context; | ||||
| 
 | ||||
|         public IUnitOfWork UnitOfWork | ||||
|         { | ||||
|             get | ||||
| @ -29,7 +28,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor | ||||
|         { | ||||
|             if (buyer.IsTransient()) | ||||
|             { | ||||
|                 //TODO: when migrating to ef core 1.1.1 change Add by AddAsync-. A bug in ef core 1.1.0 does not allow to do it https://github.com/aspnet/EntityFramework/issues/7298  | ||||
|                 return _context.Buyers | ||||
|                     .Add(buyer) | ||||
|                     .Entity; | ||||
| @ -37,8 +35,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor | ||||
|             else | ||||
|             { | ||||
|                 return buyer; | ||||
|             } | ||||
|              | ||||
|             }            | ||||
|         } | ||||
| 
 | ||||
|         public Buyer Update(Buyer buyer) | ||||
|  | ||||
| @ -127,6 +127,8 @@ namespace Payment.API | ||||
| 
 | ||||
|         private void RegisterEventBus(IServiceCollection services) | ||||
|         { | ||||
|             var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|             if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
| @ -134,8 +136,7 @@ namespace Payment.API | ||||
|                     var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                     var subscriptionClientName = Configuration["SubscriptionClientName"]; | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();                     | ||||
| 
 | ||||
|                     return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                         eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | ||||
| @ -156,7 +157,7 @@ namespace Payment.API | ||||
|                         retryCount = int.Parse(Configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
| 
 | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount); | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -22,7 +22,7 @@ | ||||
|         </article> | ||||
|     @if (Model != null && Model.Any()) | ||||
|     { | ||||
|         @foreach (var item in Model) | ||||
|         foreach (var item in Model) | ||||
|         { | ||||
|             <article class="esh-orders-items row"> | ||||
|                 <section class="esh-orders-item col-xs-2">@Html.DisplayFor(modelItem => item.OrderNumber)</section> | ||||
|  | ||||
| @ -39,6 +39,7 @@ | ||||
|                         <a asp-area="" asp-controller="Catalog" asp-action="Index"> | ||||
|                             <img src="~/images/brand.png" /> | ||||
|                         </a> | ||||
|                     </a> | ||||
|                 </section> | ||||
| 
 | ||||
|                 @await Html.PartialAsync("_LoginPartial") | ||||
|  | ||||
| @ -31,7 +31,7 @@ | ||||
|             }); | ||||
| 
 | ||||
|             Assert.NotNull(basket); | ||||
|             Assert.Equal(1, basket.Items.Count); | ||||
|             Assert.Single(basket.Items); | ||||
|         } | ||||
| 
 | ||||
|         [Fact] | ||||
|  | ||||
| @ -41,7 +41,7 @@ namespace IntegrationTests.Services.Catalog | ||||
|                 var response = await server.CreateClient() | ||||
|                     .GetAsync(Get.ItemById(int.MinValue)); | ||||
| 
 | ||||
|                 Assert.Equal(response.StatusCode, HttpStatusCode.BadRequest); | ||||
|                 Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -53,7 +53,7 @@ namespace IntegrationTests.Services.Catalog | ||||
|                 var response = await server.CreateClient() | ||||
|                     .GetAsync(Get.ItemById(int.MaxValue)); | ||||
| 
 | ||||
|                 Assert.Equal(response.StatusCode, HttpStatusCode.NotFound); | ||||
|                 Assert.Equal( HttpStatusCode.NotFound, response.StatusCode); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -147,15 +147,15 @@ | ||||
|     <None Include="WebMVC\AddProducts.webtest"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Include="WebMVC\CatalogFilter.webtest"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Include="Identity.API\Logout.webtest"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Include="WebMVC\CreateNewOrder.webtest"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Include="WebMVC\CatalogFilter.webtest"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup /> | ||||
|   <Choose> | ||||
|  | ||||
| @ -3,7 +3,10 @@ | ||||
|   <Scenarios> | ||||
|     <Scenario Name="OrderProductsLoadTest" DelayBetweenIterations="0" PercentNewUsers="0" IPSwitching="false" TestMixType="PercentageOfTestsStarted" ApplyDistributionToPacingDelay="true" MaxTestIterations="0" DisableDuringWarmup="false" DelayStartTime="0" AllowedAgents=""> | ||||
|       <ThinkProfile Value="0.2" Pattern="NormalDistribution" /> | ||||
|       <LoadProfile Pattern="Step" InitialUsers="1" MaxUsers="50" StepUsers="2" StepDuration="10" StepRampTime="10" /> | ||||
|       <LoadProfile Pattern="Step" InitialUsers="1" MaxUsers="1000" StepUsers="50" StepDuration="10" StepRampTime="10" /> | ||||
|       <InitializeTest> | ||||
|         <TestProfile Name="AddProducts" Path="webmvc\addproducts.webtest" Id="2c9d53ae-0237-47bd-a5d2-6500ef5d8fcb" Percentage="0.0" Type="Microsoft.VisualStudio.TestTools.WebStress.DeclarativeWebTestElement, Microsoft.VisualStudio.QualityTools.LoadTest, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | ||||
|       </InitializeTest> | ||||
|       <TestMix> | ||||
|         <TestProfile Name="GetItems" Path="catalog.api\getitems.webtest" Id="e527de7e-beff-4824-af52-dda763fd5e6c" Percentage="19" Type="Microsoft.VisualStudio.TestTools.WebStress.DeclarativeWebTestElement, Microsoft.VisualStudio.QualityTools.LoadTest, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | ||||
|         <TestProfile Name="GetCatalogTypes" Path="catalog.api\getcatalogtypes.webtest" Id="7df20b29-d5c3-447b-b73d-95c63e9c4061" Percentage="15" Type="Microsoft.VisualStudio.TestTools.WebStress.DeclarativeWebTestElement, Microsoft.VisualStudio.QualityTools.LoadTest, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | ||||
|  | ||||
| @ -164,7 +164,7 @@ | ||||
|         <FormPostParameter Name="Total" Value="8.5" RecordedValue="8.5" CorrelationBinding="" UrlEncode="True" /> | ||||
|         <FormPostParameter Name="action" Value="[ Place Order ]" RecordedValue="[ Place Order ]" CorrelationBinding="{{FormPostParam9.action}}" UrlEncode="True" /> | ||||
|         <FormPostParameter Name="ZipCode" Value="98052" RecordedValue="98052" CorrelationBinding="" UrlEncode="True" /> | ||||
|         <FormPostParameter Name="RequestId" Value="{{GenGuid}} " RecordedValue="f58b9345-ea25-4125-a8bf-b0992233af6c" CorrelationBinding="" UrlEncode="True" /> | ||||
|         <FormPostParameter Name="RequestId" Value="{{GenGuid}}" RecordedValue="{{GenGuid}}" CorrelationBinding="" UrlEncode="True" /> | ||||
|         <FormPostParameter Name="CardTypeId" Value="1" RecordedValue="" CorrelationBinding="" UrlEncode="True" /> | ||||
|       </FormPostHttpBody> | ||||
|     </Request> | ||||
|  | ||||
							
								
								
									
										84
									
								
								test/Services/LoadTest/readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								test/Services/LoadTest/readme.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| # Load Testing settings | ||||
| 
 | ||||
| This folder contains files needed to run load tests locally or on a Kubernetes / Service Fabric cluster. | ||||
| 
 | ||||
| <p> | ||||
| <img src="../../../img/loadtests/loadtestproj_dir.png"> | ||||
| <p> | ||||
| 
 | ||||
| ## Set a local environment | ||||
| 
 | ||||
| Modify the **app.config** file in the LoadTest project directory and set the following service urls. | ||||
| 
 | ||||
| ``` | ||||
| <Servers> | ||||
|     <MvcWebServer url="http://localhost:5100" /> | ||||
|     <CatalogApiServer url="http://localhost:5101" /> | ||||
|     <OrderingApiServer url="http://localhost:5102" /> | ||||
|     <BasketApiServer url="http://localhost:5103" /> | ||||
|     <IdentityApiServer url="http://localhost:5105" /> | ||||
|     <LocationsApiServer url="http://localhost:5109" /> | ||||
|     <MarketingApiServer url="http://localhost:5110" /> | ||||
|   </Servers> | ||||
| ``` | ||||
| 
 | ||||
| Modify the **.env** file and set the following config property as shown bellow. | ||||
| 
 | ||||
| ``` | ||||
| USE_LOADTEST=True | ||||
| ``` | ||||
| ## Set a Service Fabric environment | ||||
| 
 | ||||
| Modify the **app.config** file in the LoadTest project directory and set the following service urls. | ||||
| 
 | ||||
| ``` | ||||
| <Servers> | ||||
|     <MvcWebServer url="http://<target_sf_dns>:5100" /> | ||||
|     <CatalogApiServer url="http://<target_sf_dns>:5101" /> | ||||
|     <OrderingApiServer url="http://<target_sf_dns>:5102" /> | ||||
|     <BasketApiServer url="http://<target_sf_dns>:5103" /> | ||||
|     <IdentityApiServer url="http://<target_sf_dns>:5105" /> | ||||
|     <LocationsApiServer url="http://<target_sf_dns>:5109" /> | ||||
|     <MarketingApiServer url="http://<target_sf_dns>:5110" /> | ||||
|   </Servers> | ||||
| ``` | ||||
| 
 | ||||
| Modify the **ServiceManifest.xml** files of the eShop SF Services and set the **UseLoadTest** environment variable to True. This setting enables the load tests to bypass authorization in api services. | ||||
| 
 | ||||
| <p> | ||||
| <img src="../../../img/loadtests/sfmanifestsettings.png"> | ||||
| <p> | ||||
| 
 | ||||
| Deploy the SF services. **PLEASE** Read our [SF deployment guide for Linux](./../../../deploy/az/servicefabric/LinuxContainers/readme.md) And [SF deployment guide for Windows](./../../../deploy/az/servicefabric/WindowsContainers/readme.md) to know about how to deploy eshop on SF. | ||||
| 
 | ||||
| ## Set a Kubernetes environment | ||||
| 
 | ||||
| Modify the **app.config** file in the LoadTest project directory and set the following service urls. | ||||
| 
 | ||||
| ``` | ||||
| <Servers> | ||||
|     <MvcWebServer url="http://<public_ip_k8s>/webmvc" /> | ||||
|     <CatalogApiServer url="http://<public_ip_k8s>/catalog-api" /> | ||||
|     <OrderingApiServer url="http://<public_ip_k8s>/ordering-api" /> | ||||
|     <BasketApiServer url="http://<public_ip_k8s>/basket-api" /> | ||||
|     <IdentityApiServer url="http://<public_ip_k8s>/identity" /> | ||||
|     <LocationsApiServer url="http://<public_ip_k8s>/locations-api" /> | ||||
|     <MarketingApiServer url="http://<public_ip_k8s>/marketing-api" /> | ||||
|   </Servers> | ||||
| ``` | ||||
| 
 | ||||
| Modify the **conf_local.yml** file in the K8s directory and set the **EnableLoadTest** environment variable to True. This setting enables the load tests to bypass authorization in api services. | ||||
| 
 | ||||
| <p> | ||||
| <img src="../../../img/loadtests/k8ssettings.png"> | ||||
| <p> | ||||
| 
 | ||||
| Deploy the kubernetes services. **PLEASE** Read our [k8s deployment guide](./../../../k8s/README.k8s.md) to know about how to deploy eshop on Kubernetes. | ||||
| 
 | ||||
| ## Run Load Tests | ||||
| 
 | ||||
| Open the load test you want to perform ***.loadtest** files and click the Run Load test button. | ||||
| 
 | ||||
| <p> | ||||
| <img src="./../../../img/loadtests/runloadtest.png"> | ||||
| <p> | ||||
| @ -13,12 +13,12 @@ namespace UnitTest.Ordering.Application | ||||
|     using System.Collections.Generic; | ||||
|     using System.Threading.Tasks; | ||||
|     using Xunit; | ||||
|     public class IdentifierCommandHandlerTest | ||||
|     public class IdentifiedCommandHandlerTest | ||||
|     { | ||||
|         private readonly Mock<IRequestManager> _requestManager; | ||||
|         private readonly Mock<IMediator> _mediator; | ||||
| 
 | ||||
|         public IdentifierCommandHandlerTest() | ||||
|         public IdentifiedCommandHandlerTest() | ||||
|         { | ||||
|             _requestManager = new Mock<IRequestManager>(); | ||||
|             _mediator = new Mock<IMediator>(); | ||||
| @ -38,7 +38,7 @@ namespace UnitTest.Ordering.Application | ||||
|                .Returns(Task.FromResult(true)); | ||||
| 
 | ||||
|             //Act | ||||
|             var handler = new IdentifierCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object); | ||||
|             var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object); | ||||
|             var result = await handler.Handle(fakeOrderCmd); | ||||
| 
 | ||||
|             //Assert | ||||
| @ -60,7 +60,7 @@ namespace UnitTest.Ordering.Application | ||||
|                .Returns(Task.FromResult(true)); | ||||
| 
 | ||||
|             //Act | ||||
|             var handler = new IdentifierCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object); | ||||
|             var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object); | ||||
|             var result = await handler.Handle(fakeOrderCmd); | ||||
| 
 | ||||
|             //Assert | ||||
| @ -93,7 +93,7 @@ namespace UnitTest.Ordering.Application | ||||
|         public async Task Get_orders_success() | ||||
|         { | ||||
|             //Arrange | ||||
|             var fakeDynamicResult = Enumerable.Empty<object>(); | ||||
|             var fakeDynamicResult = Enumerable.Empty<OrderSummary>(); | ||||
|             _orderQueriesMock.Setup(x => x.GetOrdersAsync()) | ||||
|                 .Returns(Task.FromResult(fakeDynamicResult)); | ||||
| 
 | ||||
| @ -110,7 +110,7 @@ namespace UnitTest.Ordering.Application | ||||
|         { | ||||
|             //Arrange | ||||
|             var fakeOrderId = 123; | ||||
|             var fakeDynamicResult = new Object(); | ||||
|             var fakeDynamicResult = new Order(); | ||||
|             _orderQueriesMock.Setup(x => x.GetOrderAsync(It.IsAny<int>())) | ||||
|                 .Returns(Task.FromResult(fakeDynamicResult)); | ||||
| 
 | ||||
| @ -126,7 +126,7 @@ namespace UnitTest.Ordering.Application | ||||
|         public async Task Get_cardTypes_success() | ||||
|         { | ||||
|             //Arrange | ||||
|             var fakeDynamicResult = Enumerable.Empty<object>(); | ||||
|             var fakeDynamicResult = Enumerable.Empty<CardType>(); | ||||
|             _orderQueriesMock.Setup(x => x.GetCardTypesAsync()) | ||||
|                 .Returns(Task.FromResult(fakeDynamicResult)); | ||||
| 
 | ||||
|  | ||||
| @ -111,7 +111,6 @@ public class OrderAggregateTest | ||||
|     public void Add_new_Order_raises_new_event() | ||||
|     { | ||||
|         //Arrange | ||||
|         var userId = new Guid(); | ||||
|         var street = "fakeStreet"; | ||||
|         var city = "FakeCity"; | ||||
|         var state = "fakeState"; | ||||
| @ -135,7 +134,6 @@ public class OrderAggregateTest | ||||
|     public void Add_event_Order_explicitly_raises_new_event() | ||||
|     { | ||||
|         //Arrange    | ||||
|         var userId = new Guid(); | ||||
|         var street = "fakeStreet"; | ||||
|         var city = "FakeCity"; | ||||
|         var state = "fakeState"; | ||||
| @ -159,7 +157,6 @@ public class OrderAggregateTest | ||||
|     public void Remove_event_Order_explicitly() | ||||
|     { | ||||
|         //Arrange     | ||||
|         var userId = new Guid(); | ||||
|         var street = "fakeStreet"; | ||||
|         var city = "FakeCity"; | ||||
|         var state = "fakeState"; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user