Merge pull request #122 from sixeyed/master
Add .NET Core variants running in Windows containers
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -2,3 +2,4 @@
 | 
				
			|||||||
project.lock.json
 | 
					project.lock.json
 | 
				
			||||||
bin/
 | 
					bin/
 | 
				
			||||||
obj/
 | 
					obj/
 | 
				
			||||||
 | 
					.vs/
 | 
				
			||||||
							
								
								
									
										37
									
								
								ExampleVotingApp.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ExampleVotingApp.sln
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					Microsoft Visual Studio Solution File, Format Version 12.00
 | 
				
			||||||
 | 
					# Visual Studio 15
 | 
				
			||||||
 | 
					VisualStudioVersion = 15.0.28010.2036
 | 
				
			||||||
 | 
					MinimumVisualStudioVersion = 10.0.40219.1
 | 
				
			||||||
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vote", "vote\dotnet\Vote\Vote.csproj", "{9687EAF5-BFF3-4F8D-9C78-1B8EE12CE091}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker", "worker\dotnet\Worker\Worker.csproj", "{083764E8-4C34-43FB-A468-F80CE0ADE10A}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Result", "result\dotnet\Result\Result.csproj", "{9AD16D72-E3F5-4A76-814C-40EBD1EE7892}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
 | 
					Global
 | 
				
			||||||
 | 
						GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
				
			||||||
 | 
							Debug|Any CPU = Debug|Any CPU
 | 
				
			||||||
 | 
							Release|Any CPU = Release|Any CPU
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(ProjectConfigurationPlatforms) = postSolution
 | 
				
			||||||
 | 
							{9687EAF5-BFF3-4F8D-9C78-1B8EE12CE091}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{9687EAF5-BFF3-4F8D-9C78-1B8EE12CE091}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{9687EAF5-BFF3-4F8D-9C78-1B8EE12CE091}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{9687EAF5-BFF3-4F8D-9C78-1B8EE12CE091}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
							{083764E8-4C34-43FB-A468-F80CE0ADE10A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{083764E8-4C34-43FB-A468-F80CE0ADE10A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{083764E8-4C34-43FB-A468-F80CE0ADE10A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{083764E8-4C34-43FB-A468-F80CE0ADE10A}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
							{9AD16D72-E3F5-4A76-814C-40EBD1EE7892}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{9AD16D72-E3F5-4A76-814C-40EBD1EE7892}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{9AD16D72-E3F5-4A76-814C-40EBD1EE7892}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{9AD16D72-E3F5-4A76-814C-40EBD1EE7892}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(SolutionProperties) = preSolution
 | 
				
			||||||
 | 
							HideSolutionNode = FALSE
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(ExtensibilityGlobals) = postSolution
 | 
				
			||||||
 | 
							SolutionGuid = {9DEFC158-1225-4393-8A38-22256211D43D}
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
					EndGlobal
 | 
				
			||||||
							
								
								
									
										42
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								README.md
									
									
									
									
									
								
							@@ -1,10 +1,19 @@
 | 
				
			|||||||
Example Voting App
 | 
					Example Voting App
 | 
				
			||||||
=========
 | 
					=========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A simple distributed application running across multiple Docker containers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Getting started
 | 
					Getting started
 | 
				
			||||||
---------------
 | 
					---------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Download [Docker](https://www.docker.com/products/overview). If you are on Mac or Windows, [Docker Compose](https://docs.docker.com/compose) will be automatically installed. On Linux, make sure you have the latest version of [Compose](https://docs.docker.com/compose/install/). If you're using [Docker for Windows](https://docs.docker.com/docker-for-windows/) on Windows 10 pro or later, you must also [switch to Linux containers](https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers).
 | 
					Download [Docker Desktop](https://www.docker.com/products/docker-desktop) for Mac or Windows. [Docker Compose](https://docs.docker.com/compose) will be automatically installed. On Linux, make sure you have the latest version of [Compose](https://docs.docker.com/compose/install/). 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Linux Containers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Linux stack uses Python, Node.js, .NET Core (or optionally Java), with Redis for messaging and Postgres for storage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> If you're using [Docker Desktop on Windows](https://store.docker.com/editions/community/docker-ce-desktop-windows), you can run the Linux version by [switching to Linux containers](https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers), or run the Windows containers version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Run in this directory:
 | 
					Run in this directory:
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
@@ -21,6 +30,27 @@ Once you have your swarm, in this directory run:
 | 
				
			|||||||
docker stack deploy --compose-file docker-stack.yml vote
 | 
					docker stack deploy --compose-file docker-stack.yml vote
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Windows Containers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An alternative version of the app uses Windows containers based on Nano Server. This stack runs on .NET Core, using [NATS](https://nats.io) for messaging and [TiDB](https://github.com/pingcap/tidb) for storage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can build from source using:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					docker-compose -f docker-compose-windows.yml build
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then run the app using:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					docker-compose -f docker-compose-windows.yml up -d
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> Or in a Windows swarm, run `docker stack deploy -c docker-stack-windows.yml vote`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The app will be running at [http://localhost:5000](http://localhost:5000), and the results will be at [http://localhost:5001](http://localhost:5001).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Run the app in Kubernetes
 | 
					Run the app in Kubernetes
 | 
				
			||||||
-------------------------
 | 
					-------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,11 +77,11 @@ Architecture
 | 
				
			|||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* A Python webapp which lets you vote between two options
 | 
					* A front-end web app in [Python](/vote) or [ASP.NET Core](/vote/dotnet) which lets you vote between two options
 | 
				
			||||||
* A Redis queue which collects new votes
 | 
					* A [Redis](https://hub.docker.com/_/redis/) or [NATS](https://hub.docker.com/_/nats/) queue which collects new votes
 | 
				
			||||||
* A .NET worker which consumes votes and stores them in…
 | 
					* A [.NET Core](/worker/src/Worker), [Java](/worker/src/main) or [.NET Core 2.1](/worker/dotnet) worker which consumes votes and stores them in…
 | 
				
			||||||
* A Postgres database backed by a Docker volume
 | 
					* A [Postgres](https://hub.docker.com/_/postgres/) or [TiDB](https://hub.docker.com/r/dockersamples/tidb/tags/) database backed by a Docker volume
 | 
				
			||||||
* A Node.js webapp which shows the results of the voting in real time
 | 
					* A [Node.js](/result) or [ASP.NET Core SignalR](/result/dotnet) webapp which shows the results of the voting in real time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Note
 | 
					Note
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										45
									
								
								docker-compose-windows.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								docker-compose-windows.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					version: "3.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  vote:
 | 
				
			||||||
 | 
					    image: dockersamples/examplevotingapp_vote:dotnet-nanoserver-sac2016
 | 
				
			||||||
 | 
					    build: 
 | 
				
			||||||
 | 
					      context: ./vote/dotnet
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "5000:80"
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - message-queue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  result:
 | 
				
			||||||
 | 
					    image: dockersamples/examplevotingapp_result:dotnet-nanoserver-sac2016
 | 
				
			||||||
 | 
					    build: 
 | 
				
			||||||
 | 
					      context: ./result/dotnet
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "5001:80"
 | 
				
			||||||
 | 
					    environment:      
 | 
				
			||||||
 | 
					      - "ConnectionStrings:ResultData=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  worker:
 | 
				
			||||||
 | 
					    image: dockersamples/examplevotingapp_worker:dotnet-nanoserver-sac2016
 | 
				
			||||||
 | 
					    build:
 | 
				
			||||||
 | 
					      context: ./worker/dotnet
 | 
				
			||||||
 | 
					    environment:      
 | 
				
			||||||
 | 
					      - "ConnectionStrings:VoteData=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - message-queue
 | 
				
			||||||
 | 
					      - db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  message-queue:
 | 
				
			||||||
 | 
					    image: nats:nanoserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  db:
 | 
				
			||||||
 | 
					    image: dockersamples/tidb:nanoserver    
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "3306:4000"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					networks:
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    external:
 | 
				
			||||||
 | 
					      name: nat
 | 
				
			||||||
							
								
								
									
										61
									
								
								docker-stack-windows.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								docker-stack-windows.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					version: "3.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  vote:
 | 
				
			||||||
 | 
					    image: dockersamples/examplevotingapp_vote:dotnet-nanoserver-sac2016
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - mode: host
 | 
				
			||||||
 | 
					        target: 80
 | 
				
			||||||
 | 
					        published: 5000
 | 
				
			||||||
 | 
					    deploy:
 | 
				
			||||||
 | 
					      endpoint_mode: dnsrr
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      - frontend
 | 
				
			||||||
 | 
					      - backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  result:
 | 
				
			||||||
 | 
					    image: dockersamples/examplevotingapp_result:dotnet-nanoserver-sac2016
 | 
				
			||||||
 | 
					    environment:      
 | 
				
			||||||
 | 
					      - "ConnectionStrings:ResultData=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - mode: host
 | 
				
			||||||
 | 
					        target: 80
 | 
				
			||||||
 | 
					        published: 5001
 | 
				
			||||||
 | 
					    deploy:
 | 
				
			||||||
 | 
					      endpoint_mode: dnsrr
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      - frontend
 | 
				
			||||||
 | 
					      - backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  worker:
 | 
				
			||||||
 | 
					    image: dockersamples/examplevotingapp_worker:dotnet-nanoserver-sac2016
 | 
				
			||||||
 | 
					    environment:      
 | 
				
			||||||
 | 
					      - "ConnectionStrings:VoteData=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
 | 
				
			||||||
 | 
					    deploy:
 | 
				
			||||||
 | 
					      endpoint_mode: dnsrr
 | 
				
			||||||
 | 
					      mode: replicated
 | 
				
			||||||
 | 
					      replicas: 3
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      - backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  message-queue:
 | 
				
			||||||
 | 
					    image: nats:nanoserver
 | 
				
			||||||
 | 
					    deploy:
 | 
				
			||||||
 | 
					      endpoint_mode: dnsrr
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      - backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  db:
 | 
				
			||||||
 | 
					    image: dockersamples/tidb:nanoserver
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - mode: host
 | 
				
			||||||
 | 
					        target: 4000
 | 
				
			||||||
 | 
					        published: 3306
 | 
				
			||||||
 | 
					    deploy:
 | 
				
			||||||
 | 
					      endpoint_mode: dnsrr
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      - backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					networks:
 | 
				
			||||||
 | 
					  frontend:
 | 
				
			||||||
 | 
					  backend:
 | 
				
			||||||
							
								
								
									
										16
									
								
								result/dotnet/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								result/dotnet/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					FROM microsoft/dotnet:2.1-sdk-nanoserver-sac2016 as builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /Result
 | 
				
			||||||
 | 
					COPY Result/Result.csproj .
 | 
				
			||||||
 | 
					RUN dotnet restore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY /Result .
 | 
				
			||||||
 | 
					RUN dotnet publish -c Release -o /out Result.csproj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# app image
 | 
				
			||||||
 | 
					FROM microsoft/dotnet:2.1-aspnetcore-runtime-nanoserver-sac2016
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					ENTRYPOINT ["dotnet", "Result.dll"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY --from=builder /out .
 | 
				
			||||||
							
								
								
									
										9
									
								
								result/dotnet/Result/Data/IResultData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								result/dotnet/Result/Data/IResultData.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Result.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Result.Data
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IResultData
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ResultsModel GetResults();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								result/dotnet/Result/Data/MySqlResultData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								result/dotnet/Result/Data/MySqlResultData.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using Dapper;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using MySql.Data.MySqlClient;
 | 
				
			||||||
 | 
					using Result.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Result.Data
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MySqlResultData : IResultData
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly string _connectionString;
 | 
				
			||||||
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public MySqlResultData(IConfiguration config, ILogger<MySqlResultData> logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _connectionString = config.GetConnectionString("ResultData");
 | 
				
			||||||
 | 
					            _logger = logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ResultsModel GetResults()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var model = new ResultsModel();            
 | 
				
			||||||
 | 
					            using (var connection = new MySqlConnection(_connectionString))
 | 
				
			||||||
 | 
					            {                
 | 
				
			||||||
 | 
					                var results = connection.Query("SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote ORDER BY vote");
 | 
				
			||||||
 | 
					                if (results.Any(x => x.vote == "a"))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    model.OptionA = (int) results.First(x => x.vote == "a").count;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (results.Any(x => x.vote == "b"))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    model.OptionB = (int) results.First(x => x.vote == "b").count;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                model.VoteCount = model.OptionA + model.OptionB;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return model;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								result/dotnet/Result/Hubs/ResultsHub.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								result/dotnet/Result/Hubs/ResultsHub.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Microsoft.AspNetCore.SignalR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Result.Hubs
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ResultsHub : Hub
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        //no public methods, only used for push from PublishRTesultsTimer
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								result/dotnet/Result/Models/ResultsModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								result/dotnet/Result/Models/ResultsModel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					namespace Result.Models
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ResultsModel
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public int OptionA { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int OptionB { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int VoteCount { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										45
									
								
								result/dotnet/Result/Pages/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								result/dotnet/Result/Pages/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					@page
 | 
				
			||||||
 | 
					@model Result.Pages.IndexModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					    <meta charset="utf-8">
 | 
				
			||||||
 | 
					    <title>@Model.OptionA vs @Model.OptionB -- Result</title>
 | 
				
			||||||
 | 
					    <base href="/index.html">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width, initial-scale = 1.0">
 | 
				
			||||||
 | 
					    <meta name="keywords" content="docker-compose, docker, stack">
 | 
				
			||||||
 | 
					    <meta name="author" content="Docker">
 | 
				
			||||||
 | 
					    <link rel='stylesheet' href='~/css/site.css' />
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					    <div id="background-stats">
 | 
				
			||||||
 | 
					        <div id="background-stats-1">
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <!--
 | 
				
			||||||
 | 
					        -->
 | 
				
			||||||
 | 
					        <div id="background-stats-2">
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div id="content-container">
 | 
				
			||||||
 | 
					        <div id="content-container-center">
 | 
				
			||||||
 | 
					            <div id="choice">
 | 
				
			||||||
 | 
					                <div class="choice resulta">
 | 
				
			||||||
 | 
					                    <div class="label">@Model.OptionA</div>
 | 
				
			||||||
 | 
					                    <div class="stat" id="optionA">50%</div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="divider"></div>
 | 
				
			||||||
 | 
					                <div class="choice resultb">
 | 
				
			||||||
 | 
					                    <div class="label">@Model.OptionB</div>
 | 
				
			||||||
 | 
					                    <div class="stat" id="optionB">50%</div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div id="result">
 | 
				
			||||||
 | 
					        <span id="totalVotes">No votes yet</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <script src="~/lib/signalr/dist/browser/signalr.min.js"></script>
 | 
				
			||||||
 | 
					    <script src="~/js/results.js"></script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										33
									
								
								result/dotnet/Result/Pages/Index.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								result/dotnet/Result/Pages/Index.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Result.Pages
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class IndexModel : PageModel
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private string _optionA;
 | 
				
			||||||
 | 
					        private string _optionB;
 | 
				
			||||||
 | 
					        protected readonly IConfiguration _configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string OptionA { get; private set; }
 | 
				
			||||||
 | 
					        public string OptionB { get; private set; }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public IndexModel(IConfiguration configuration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _configuration = configuration;
 | 
				
			||||||
 | 
					            _optionA = _configuration.GetValue<string>("Voting:OptionA");
 | 
				
			||||||
 | 
					            _optionB = _configuration.GetValue<string>("Voting:OptionB");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void OnGet()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            OptionA = _optionA;
 | 
				
			||||||
 | 
					            OptionB = _optionB;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								result/dotnet/Result/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								result/dotnet/Result/Program.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Result
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class Program
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static void Main(string[] args)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            CreateWebHostBuilder(args).Build().Run();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
 | 
				
			||||||
 | 
					            WebHost.CreateDefaultBuilder(args)
 | 
				
			||||||
 | 
					                .UseStartup<Startup>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								result/dotnet/Result/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								result/dotnet/Result/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "iisSettings": {
 | 
				
			||||||
 | 
					    "windowsAuthentication": false,
 | 
				
			||||||
 | 
					    "anonymousAuthentication": true,
 | 
				
			||||||
 | 
					    "iisExpress": {
 | 
				
			||||||
 | 
					      "applicationUrl": "http://localhost:56785",
 | 
				
			||||||
 | 
					      "sslPort": 0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "profiles": {
 | 
				
			||||||
 | 
					    "IIS Express": {
 | 
				
			||||||
 | 
					      "commandName": "IISExpress",
 | 
				
			||||||
 | 
					      "launchBrowser": true,
 | 
				
			||||||
 | 
					      "environmentVariables": {
 | 
				
			||||||
 | 
					        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Result": {
 | 
				
			||||||
 | 
					      "commandName": "Project",
 | 
				
			||||||
 | 
					      "launchBrowser": true,
 | 
				
			||||||
 | 
					      "environmentVariables": {
 | 
				
			||||||
 | 
					        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "applicationUrl": "https://localhost:5001;http://localhost:5000"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								result/dotnet/Result/Result.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								result/dotnet/Result/Result.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk.Web">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <PropertyGroup>
 | 
				
			||||||
 | 
					    <TargetFramework>netcoreapp2.1</TargetFramework>
 | 
				
			||||||
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <PackageReference Include="Dapper" Version="1.50.5" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.AspNetCore.App" />
 | 
				
			||||||
 | 
					    <PackageReference Include="MySql.Data" Version="8.0.12" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										52
									
								
								result/dotnet/Result/Startup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								result/dotnet/Result/Startup.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Result.Data;
 | 
				
			||||||
 | 
					using Result.Hubs;
 | 
				
			||||||
 | 
					using Result.Timers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Result
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class Startup
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public Startup(IConfiguration configuration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Configuration = configuration;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IConfiguration Configuration { get; }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					        public void ConfigureServices(IServiceCollection services)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
 | 
				
			||||||
 | 
					            services.AddSignalR();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            services.AddTransient<IResultData, MySqlResultData>()
 | 
				
			||||||
 | 
					                    .AddSingleton<PublishResultsTimer>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (env.IsDevelopment())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                app.UseDeveloperExceptionPage();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                app.UseExceptionHandler("/Error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            app.UseStaticFiles();
 | 
				
			||||||
 | 
					            app.UseSignalR(routes =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                routes.MapHub<ResultsHub>("/resultsHub");
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            app.UseMvc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var timer = app.ApplicationServices.GetService<PublishResultsTimer>();
 | 
				
			||||||
 | 
					            timer.Start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										41
									
								
								result/dotnet/Result/Timers/PublishResultsTimer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								result/dotnet/Result/Timers/PublishResultsTimer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					using System.Timers;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.SignalR;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Result.Data;
 | 
				
			||||||
 | 
					using Result.Hubs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Result.Timers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class PublishResultsTimer
 | 
				
			||||||
 | 
					    {        
 | 
				
			||||||
 | 
					        private readonly IHubContext<ResultsHub> _hubContext;
 | 
				
			||||||
 | 
					        private readonly IResultData _resultData;
 | 
				
			||||||
 | 
					        private readonly Timer _timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public PublishResultsTimer(IHubContext<ResultsHub> hubContext, IResultData resultData, IConfiguration configuration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _hubContext = hubContext;
 | 
				
			||||||
 | 
					            _resultData = resultData;
 | 
				
			||||||
 | 
					            var publishMilliseconds = configuration.GetValue<int>("ResultsTimer:PublishMilliseconds");
 | 
				
			||||||
 | 
					            _timer = new Timer(publishMilliseconds)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Enabled = false
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            _timer.Elapsed += PublishResults;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Start()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!_timer.Enabled)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _timer.Start();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void PublishResults(object sender, ElapsedEventArgs e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var model = _resultData.GetResults();
 | 
				
			||||||
 | 
					            _hubContext.Clients.All.SendAsync("UpdateResults", model);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								result/dotnet/Result/appsettings.Development.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								result/dotnet/Result/appsettings.Development.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Logging": {
 | 
				
			||||||
 | 
					    "LogLevel": {
 | 
				
			||||||
 | 
					      "Default": "Debug",
 | 
				
			||||||
 | 
					      "System": "Information",
 | 
				
			||||||
 | 
					      "Microsoft": "Information"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								result/dotnet/Result/appsettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								result/dotnet/Result/appsettings.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Voting": {
 | 
				
			||||||
 | 
					    "OptionA": "Cats",
 | 
				
			||||||
 | 
					    "OptionB": "Dogs"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "ResultsTimer": {
 | 
				
			||||||
 | 
					    "PublishMilliseconds": 2500
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "ConnectionStrings": {
 | 
				
			||||||
 | 
					    "ResultData": "Server=mysql;Port=4000;Database=votes;User=root;SslMode=None"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "Logging": {
 | 
				
			||||||
 | 
					    "LogLevel": {
 | 
				
			||||||
 | 
					      "Default": "Warning"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "AllowedHosts": "*"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								result/dotnet/Result/libman.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								result/dotnet/Result/libman.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "version": "1.0",
 | 
				
			||||||
 | 
					  "defaultProvider": "unpkg",
 | 
				
			||||||
 | 
					  "libraries": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "library": "@aspnet/signalr@1.0.3",
 | 
				
			||||||
 | 
					      "destination": "wwwroot/lib/signalr/",
 | 
				
			||||||
 | 
					      "files": [
 | 
				
			||||||
 | 
					        "dist/browser/signalr.js",
 | 
				
			||||||
 | 
					        "dist/browser/signalr.min.js"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										121
									
								
								result/dotnet/Result/wwwroot/css/site.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								result/dotnet/Result/wwwroot/css/site.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					@import url(//fonts.googleapis.com/css?family=Open+Sans:400,700,600);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* {
 | 
				
			||||||
 | 
						box-sizing: border-box;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html, body {
 | 
				
			||||||
 | 
						margin: 0;
 | 
				
			||||||
 | 
						padding: 0;
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
						font-family: 'Open Sans';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
						opacity: 0;
 | 
				
			||||||
 | 
						transition: all 1s linear;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.divider {
 | 
				
			||||||
 | 
						height: 150px;
 | 
				
			||||||
 | 
						width: 2px;
 | 
				
			||||||
 | 
						background-color: #C0C9CE;
 | 
				
			||||||
 | 
						position: relative;
 | 
				
			||||||
 | 
						top: 50%;
 | 
				
			||||||
 | 
						float: left;
 | 
				
			||||||
 | 
						transform: translateY(-50%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#background-stats-1 {
 | 
				
			||||||
 | 
						background-color: #2196f3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#background-stats-2 {
 | 
				
			||||||
 | 
						background-color: #00cbca;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#content-container {
 | 
				
			||||||
 | 
						z-index: 2;
 | 
				
			||||||
 | 
						position: relative;
 | 
				
			||||||
 | 
						margin: 0 auto;
 | 
				
			||||||
 | 
						display: table;
 | 
				
			||||||
 | 
						padding: 10px;
 | 
				
			||||||
 | 
						max-width: 940px;
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#content-container-center {
 | 
				
			||||||
 | 
						display: table-cell;
 | 
				
			||||||
 | 
						text-align: center;
 | 
				
			||||||
 | 
						vertical-align: middle;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#result {
 | 
				
			||||||
 | 
						z-index: 3;
 | 
				
			||||||
 | 
						position: absolute;
 | 
				
			||||||
 | 
						bottom: 40px;
 | 
				
			||||||
 | 
						right: 20px;
 | 
				
			||||||
 | 
						color: #fff;
 | 
				
			||||||
 | 
						opacity: 0.5;
 | 
				
			||||||
 | 
						font-size: 45px;
 | 
				
			||||||
 | 
						font-weight: 600;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#choice {
 | 
				
			||||||
 | 
						transition: all 300ms linear;
 | 
				
			||||||
 | 
						line-height: 1.3em;
 | 
				
			||||||
 | 
						background: #fff;
 | 
				
			||||||
 | 
						box-shadow: 10px 0 0 #fff, -10px 0 0 #fff;
 | 
				
			||||||
 | 
						vertical-align: middle;
 | 
				
			||||||
 | 
						font-size: 40px;
 | 
				
			||||||
 | 
						font-weight: 600;
 | 
				
			||||||
 | 
						width: 450px;
 | 
				
			||||||
 | 
						height: 200px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#choice a {
 | 
				
			||||||
 | 
							text-decoration: none;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice a:hover, #choice a:focus {
 | 
				
			||||||
 | 
								outline: 0;
 | 
				
			||||||
 | 
								text-decoration: underline;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#choice .choice {
 | 
				
			||||||
 | 
							width: 49%;
 | 
				
			||||||
 | 
							position: relative;
 | 
				
			||||||
 | 
							top: 50%;
 | 
				
			||||||
 | 
							transform: translateY(-50%);
 | 
				
			||||||
 | 
							text-align: left;
 | 
				
			||||||
 | 
							padding-left: 50px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice .choice .label {
 | 
				
			||||||
 | 
								text-transform: uppercase;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice .choice.resultb {
 | 
				
			||||||
 | 
								color: #00cbca;
 | 
				
			||||||
 | 
								float: right;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice .choice.resulta {
 | 
				
			||||||
 | 
								color: #2196f3;
 | 
				
			||||||
 | 
								float: left;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#background-stats {
 | 
				
			||||||
 | 
						z-index: 1;
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						position: absolute;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#background-stats div {
 | 
				
			||||||
 | 
							transition: width 400ms ease-in-out;
 | 
				
			||||||
 | 
							display: inline-block;
 | 
				
			||||||
 | 
							margin-bottom: -4px;
 | 
				
			||||||
 | 
							width: 50%;
 | 
				
			||||||
 | 
							height: 100%;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
							
								
								
									
										1
									
								
								result/dotnet/Result/wwwroot/css/site.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								result/dotnet/Result/wwwroot/css/site.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}}
 | 
				
			||||||
							
								
								
									
										41
									
								
								result/dotnet/Result/wwwroot/js/results.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								result/dotnet/Result/wwwroot/js/results.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var connection = new signalR.HubConnectionBuilder().withUrl("/resultsHub").build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					connection.on("UpdateResults", function (results) {
 | 
				
			||||||
 | 
					    document.body.style.opacity=1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var a = parseInt(results.optionA || 0);
 | 
				
			||||||
 | 
					    var b = parseInt(results.optionB || 0);
 | 
				
			||||||
 | 
					    var percentages = getPercentages(a, b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document.getElementById("optionA").innerText = percentages.a + "%";
 | 
				
			||||||
 | 
					    document.getElementById("optionB").innerText = percentages.b + "%";
 | 
				
			||||||
 | 
					    var totalVotes = 'No votes yet';
 | 
				
			||||||
 | 
					    if (results.voteCount > 0) {
 | 
				
			||||||
 | 
					        totalVotes = results.voteCount + (results.voteCount > 1 ? " votes" : " vote");        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    document.getElementById("totalVotes").innerText = totalVotes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var bg1 = document.getElementById('background-stats-1');
 | 
				
			||||||
 | 
					    var bg2 = document.getElementById('background-stats-2');
 | 
				
			||||||
 | 
					    bg1.style.width = (percentages.a-0.2) + "%";
 | 
				
			||||||
 | 
					    bg2.style.width = (percentages.b-0.2) + "%";
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					connection.start().catch(function (err) {
 | 
				
			||||||
 | 
					    return console.error(err.toString());
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getPercentages(a, b) {
 | 
				
			||||||
 | 
					    var result = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (a + b > 0) {
 | 
				
			||||||
 | 
					        result.a = Math.round(a / (a + b) * 100);
 | 
				
			||||||
 | 
					        result.b = 100 - result.a;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        result.a = result.b = 50;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3441
									
								
								result/dotnet/Result/wwwroot/lib/signalr/dist/browser/signalr.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3441
									
								
								result/dotnet/Result/wwwroot/lib/signalr/dist/browser/signalr.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										17
									
								
								result/dotnet/Result/wwwroot/lib/signalr/dist/browser/signalr.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								result/dotnet/Result/wwwroot/lib/signalr/dist/browser/signalr.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										16
									
								
								vote/dotnet/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vote/dotnet/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					FROM microsoft/dotnet:2.1-sdk-nanoserver-sac2016 as builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /Vote
 | 
				
			||||||
 | 
					COPY Vote/Vote.csproj .
 | 
				
			||||||
 | 
					RUN dotnet restore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY /Vote .
 | 
				
			||||||
 | 
					RUN dotnet publish -c Release -o /out Vote.csproj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# app image
 | 
				
			||||||
 | 
					FROM microsoft/dotnet:2.1-aspnetcore-runtime-nanoserver-sac2016
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					ENTRYPOINT ["dotnet", "Vote.dll"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY --from=builder /out .
 | 
				
			||||||
							
								
								
									
										12
									
								
								vote/dotnet/Vote/Messaging/IMessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vote/dotnet/Vote/Messaging/IMessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using NATS.Client;
 | 
				
			||||||
 | 
					using Vote.Messaging.Messages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote.Messaging
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IMessageQueue
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        IConnection CreateConnection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void Publish<TMessage>(TMessage message) where TMessage : Message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								vote/dotnet/Vote/Messaging/MessageHelper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vote/dotnet/Vote/Messaging/MessageHelper.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using Vote.Messaging.Messages;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote.Messaging
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MessageHelper
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static byte[] ToData<TMessage>(TMessage message)
 | 
				
			||||||
 | 
					            where TMessage : Message
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var json = JsonConvert.SerializeObject(message);
 | 
				
			||||||
 | 
					            return Encoding.Unicode.GetBytes(json);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static TMessage FromData<TMessage>(byte[] data)
 | 
				
			||||||
 | 
					            where TMessage : Message
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var json = Encoding.Unicode.GetString(data);
 | 
				
			||||||
 | 
					            return (TMessage)JsonConvert.DeserializeObject<TMessage>(json);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								vote/dotnet/Vote/Messaging/MessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vote/dotnet/Vote/Messaging/MessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using NATS.Client;
 | 
				
			||||||
 | 
					using Vote.Messaging.Messages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote.Messaging
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MessageQueue : IMessageQueue
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        protected readonly IConfiguration _configuration;
 | 
				
			||||||
 | 
					        protected readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public MessageQueue(IConfiguration configuration, ILogger<MessageQueue> logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _configuration = configuration;
 | 
				
			||||||
 | 
					            _logger = logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Publish<TMessage>(TMessage message)
 | 
				
			||||||
 | 
					            where TMessage : Message
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (var connection = CreateConnection())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var data = MessageHelper.ToData(message);
 | 
				
			||||||
 | 
					                connection.Publish(message.Subject, data);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IConnection CreateConnection()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var url = _configuration.GetValue<string>("MessageQueue:Url");
 | 
				
			||||||
 | 
					            return new ConnectionFactory().CreateConnection(url);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								vote/dotnet/Vote/Messaging/Messages/Events/VoteCastEvent.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vote/dotnet/Vote/Messaging/Messages/Events/VoteCastEvent.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote.Messaging.Messages
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class VoteCastEvent : Message
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public override string Subject { get { return MessageSubject; } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string VoterId {get; set;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Vote {get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         public static string MessageSubject = "events.vote.votecast";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								vote/dotnet/Vote/Messaging/Messages/Message.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vote/dotnet/Vote/Messaging/Messages/Message.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote.Messaging.Messages
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public abstract class Message
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string CorrelationId { get; set; }  
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public abstract string Subject { get; }      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Message()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            CorrelationId = Guid.NewGuid().ToString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										50
									
								
								vote/dotnet/Vote/Pages/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vote/dotnet/Vote/Pages/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					@page
 | 
				
			||||||
 | 
					@model Vote.Pages.IndexModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					    <meta charset="utf-8">
 | 
				
			||||||
 | 
					    <title>@Model.OptionA vs @Model.OptionB!</title>
 | 
				
			||||||
 | 
					    <base href="/index.html">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width, initial-scale = 1.0">
 | 
				
			||||||
 | 
					    <meta name="keywords" content="docker-compose, docker, stack">
 | 
				
			||||||
 | 
					    <meta name="author" content="Docker DevRel team">
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="~/css/site.css" />
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					    <div id="content-container">
 | 
				
			||||||
 | 
					        <div id="content-container-center">
 | 
				
			||||||
 | 
					            <h3>@Model.OptionA vs @Model.OptionB!</h3>
 | 
				
			||||||
 | 
					            <form method="POST" id="choice" name='form'>
 | 
				
			||||||
 | 
					                <button id="a" type="submit" name="vote" class="a" value="a">@Model.OptionA</button>
 | 
				
			||||||
 | 
					                <button id="b" type="submit" name="vote" class="b" value="b">@Model.OptionB</button>
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
 | 
					            <div id="tip">
 | 
				
			||||||
 | 
					                (Tip: you can change your vote)
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div id="hostname">
 | 
				
			||||||
 | 
					                Processed by container ID @System.Environment.MachineName
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <script src="~/js/jquery-1.11.1-min.js" type="text/javascript"></script>
 | 
				
			||||||
 | 
					    @*<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>*@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					      var vote = "@Model.Vote";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if(vote == "a"){
 | 
				
			||||||
 | 
					        $(".a").prop('disabled', true);
 | 
				
			||||||
 | 
					        $(".a").html('@Model.OptionA <i class="fa fa-check-circle"></i>');
 | 
				
			||||||
 | 
					        $(".b").css('opacity','0.5');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if(vote == "b"){
 | 
				
			||||||
 | 
					        $(".b").prop('disabled', true);
 | 
				
			||||||
 | 
					        $(".b").html('@Model.OptionB <i class="fa fa-check-circle"></i>');
 | 
				
			||||||
 | 
					        $(".a").css('opacity','0.5');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										75
									
								
								vote/dotnet/Vote/Pages/Index.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								vote/dotnet/Vote/Pages/Index.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using Vote.Messaging;
 | 
				
			||||||
 | 
					using Vote.Messaging.Messages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote.Pages
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class IndexModel : PageModel
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private string _optionA;
 | 
				
			||||||
 | 
					        private string _optionB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected readonly IMessageQueue _messageQueue;
 | 
				
			||||||
 | 
					        protected readonly IConfiguration _configuration;
 | 
				
			||||||
 | 
					        protected readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IndexModel(IMessageQueue messageQueue, IConfiguration configuration, ILogger<IndexModel> logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _messageQueue = messageQueue;
 | 
				
			||||||
 | 
					            _configuration = configuration;
 | 
				
			||||||
 | 
					            _logger = logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _optionA = _configuration.GetValue<string>("Voting:OptionA");
 | 
				
			||||||
 | 
					            _optionB = _configuration.GetValue<string>("Voting:OptionB");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string OptionA { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string OptionB { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [BindProperty]
 | 
				
			||||||
 | 
					        public string Vote { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string _voterId 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get { return TempData.Peek("VoterId") as string; }
 | 
				
			||||||
 | 
					            set { TempData["VoterId"] = value; }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void OnGet()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            OptionA = _optionA;
 | 
				
			||||||
 | 
					            OptionB = _optionB;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IActionResult OnPost(string vote)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Vote = vote;
 | 
				
			||||||
 | 
					            OptionA = _optionA;
 | 
				
			||||||
 | 
					            OptionB = _optionB;
 | 
				
			||||||
 | 
					            if (_configuration.GetValue<bool>("MessageQueue:Enabled"))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                PublishVote(vote);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return Page();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void PublishVote(string vote)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (string.IsNullOrEmpty(_voterId))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _voterId = Guid.NewGuid().ToString();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var message = new VoteCastEvent
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                VoterId = _voterId,
 | 
				
			||||||
 | 
					                Vote = vote
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					           _messageQueue.Publish(message);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								vote/dotnet/Vote/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vote/dotnet/Vote/Program.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class Program
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static void Main(string[] args)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            CreateWebHostBuilder(args).Build().Run();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
 | 
				
			||||||
 | 
					            WebHost.CreateDefaultBuilder(args)
 | 
				
			||||||
 | 
					                .UseStartup<Startup>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								vote/dotnet/Vote/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vote/dotnet/Vote/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "iisSettings": {
 | 
				
			||||||
 | 
					    "windowsAuthentication": false,
 | 
				
			||||||
 | 
					    "anonymousAuthentication": true,
 | 
				
			||||||
 | 
					    "iisExpress": {
 | 
				
			||||||
 | 
					      "applicationUrl": "http://localhost:6116",
 | 
				
			||||||
 | 
					      "sslPort": 44316
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "profiles": {
 | 
				
			||||||
 | 
					    "IIS Express": {
 | 
				
			||||||
 | 
					      "commandName": "IISExpress",
 | 
				
			||||||
 | 
					      "launchBrowser": true,
 | 
				
			||||||
 | 
					      "environmentVariables": {
 | 
				
			||||||
 | 
					        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Vote": {
 | 
				
			||||||
 | 
					      "commandName": "Project",
 | 
				
			||||||
 | 
					      "launchBrowser": true,
 | 
				
			||||||
 | 
					      "applicationUrl": "https://localhost:5001;http://localhost:5000",
 | 
				
			||||||
 | 
					      "environmentVariables": {
 | 
				
			||||||
 | 
					        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										45
									
								
								vote/dotnet/Vote/Startup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vote/dotnet/Vote/Startup.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Vote.Messaging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Vote
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class Startup
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public Startup(IConfiguration configuration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Configuration = configuration;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IConfiguration Configuration { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void ConfigureServices(IServiceCollection services)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            services.AddMvc()
 | 
				
			||||||
 | 
					                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
 | 
				
			||||||
 | 
					                .AddRazorPagesOptions(options =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    options.Conventions.ConfigureFilter(new IgnoreAntiforgeryTokenAttribute());
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            services.AddTransient<IMessageQueue, MessageQueue>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (env.IsDevelopment())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                app.UseDeveloperExceptionPage();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                app.UseExceptionHandler("/Error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					                                 
 | 
				
			||||||
 | 
					            app.UseStaticFiles();
 | 
				
			||||||
 | 
					            app.UseMvc();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								vote/dotnet/Vote/Vote.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vote/dotnet/Vote/Vote.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk.Web">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <PropertyGroup>
 | 
				
			||||||
 | 
					    <TargetFramework>netcoreapp2.1</TargetFramework>
 | 
				
			||||||
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.AspNetCore.App" />
 | 
				
			||||||
 | 
					    <PackageReference Include="NATS.Client" Version="0.8.1" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										25
									
								
								vote/dotnet/Vote/Vote.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vote/dotnet/Vote/Vote.sln
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					Microsoft Visual Studio Solution File, Format Version 12.00
 | 
				
			||||||
 | 
					# Visual Studio 15
 | 
				
			||||||
 | 
					VisualStudioVersion = 15.0.27703.2042
 | 
				
			||||||
 | 
					MinimumVisualStudioVersion = 10.0.40219.1
 | 
				
			||||||
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vote", "Vote.csproj", "{DA159FEC-BE4D-4704-ACB2-E03FFA6F2D3B}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
 | 
					Global
 | 
				
			||||||
 | 
						GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
				
			||||||
 | 
							Debug|Any CPU = Debug|Any CPU
 | 
				
			||||||
 | 
							Release|Any CPU = Release|Any CPU
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(ProjectConfigurationPlatforms) = postSolution
 | 
				
			||||||
 | 
							{DA159FEC-BE4D-4704-ACB2-E03FFA6F2D3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{DA159FEC-BE4D-4704-ACB2-E03FFA6F2D3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{DA159FEC-BE4D-4704-ACB2-E03FFA6F2D3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{DA159FEC-BE4D-4704-ACB2-E03FFA6F2D3B}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(SolutionProperties) = preSolution
 | 
				
			||||||
 | 
							HideSolutionNode = FALSE
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(ExtensibilityGlobals) = postSolution
 | 
				
			||||||
 | 
							SolutionGuid = {BEABFEFC-9957-41E3-96A1-7F501F69411D}
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
					EndGlobal
 | 
				
			||||||
							
								
								
									
										12
									
								
								vote/dotnet/Vote/appsettings.Development.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vote/dotnet/Vote/appsettings.Development.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "MessageQueue": {
 | 
				
			||||||
 | 
					    "Enabled": false
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "Logging": {
 | 
				
			||||||
 | 
					    "LogLevel": {
 | 
				
			||||||
 | 
					      "Default": "Debug",
 | 
				
			||||||
 | 
					      "System": "Information",
 | 
				
			||||||
 | 
					      "Microsoft": "Information"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								vote/dotnet/Vote/appsettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vote/dotnet/Vote/appsettings.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Voting": {
 | 
				
			||||||
 | 
					    "OptionA": "Cats",
 | 
				
			||||||
 | 
					    "OptionB":  "Dogs"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "MessageQueue": {
 | 
				
			||||||
 | 
					    "Enabled": true,
 | 
				
			||||||
 | 
					    "Url": "nats://message-queue:4222"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "Logging": {
 | 
				
			||||||
 | 
					    "LogLevel": {
 | 
				
			||||||
 | 
					      "Default": "Information"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "AllowedHosts": "*"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										135
									
								
								vote/dotnet/Vote/wwwroot/css/site.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								vote/dotnet/Vote/wwwroot/css/site.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					@import url(//fonts.googleapis.com/css?family=Open+Sans:400,700,600);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* {
 | 
				
			||||||
 | 
						box-sizing: border-box;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html, body {
 | 
				
			||||||
 | 
						margin: 0;
 | 
				
			||||||
 | 
						padding: 0;
 | 
				
			||||||
 | 
						background-color: #F7F8F9;
 | 
				
			||||||
 | 
						height: 100vh;
 | 
				
			||||||
 | 
						font-family: 'Open Sans';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button {
 | 
				
			||||||
 | 
						border-radius: 0;
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						height: 50%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						button[type="submit"] {
 | 
				
			||||||
 | 
							-webkit-appearance: none;
 | 
				
			||||||
 | 
							-webkit-border-radius: 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						button i {
 | 
				
			||||||
 | 
							float: right;
 | 
				
			||||||
 | 
							padding-right: 30px;
 | 
				
			||||||
 | 
							margin-top: 3px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						button.a {
 | 
				
			||||||
 | 
							background-color: #1aaaf8;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						button.b {
 | 
				
			||||||
 | 
							background-color: #00cbca;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#tip {
 | 
				
			||||||
 | 
						text-align: left;
 | 
				
			||||||
 | 
						color: #c0c9ce;
 | 
				
			||||||
 | 
						font-size: 14px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#hostname {
 | 
				
			||||||
 | 
						position: absolute;
 | 
				
			||||||
 | 
						bottom: 100px;
 | 
				
			||||||
 | 
						right: 0;
 | 
				
			||||||
 | 
						left: 0;
 | 
				
			||||||
 | 
						color: #8f9ea8;
 | 
				
			||||||
 | 
						font-size: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#content-container {
 | 
				
			||||||
 | 
						z-index: 2;
 | 
				
			||||||
 | 
						position: relative;
 | 
				
			||||||
 | 
						margin: 0 auto;
 | 
				
			||||||
 | 
						display: table;
 | 
				
			||||||
 | 
						padding: 10px;
 | 
				
			||||||
 | 
						max-width: 940px;
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#content-container-center {
 | 
				
			||||||
 | 
						display: table-cell;
 | 
				
			||||||
 | 
						text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#content-container-center h3 {
 | 
				
			||||||
 | 
							color: #254356;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#choice {
 | 
				
			||||||
 | 
						transition: all 300ms linear;
 | 
				
			||||||
 | 
						line-height: 1.3em;
 | 
				
			||||||
 | 
						display: inline;
 | 
				
			||||||
 | 
						vertical-align: middle;
 | 
				
			||||||
 | 
						font-size: 3em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#choice a {
 | 
				
			||||||
 | 
							text-decoration: none;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice a:hover, #choice a:focus {
 | 
				
			||||||
 | 
								outline: 0;
 | 
				
			||||||
 | 
								text-decoration: underline;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#choice button {
 | 
				
			||||||
 | 
							display: block;
 | 
				
			||||||
 | 
							height: 80px;
 | 
				
			||||||
 | 
							width: 330px;
 | 
				
			||||||
 | 
							border: none;
 | 
				
			||||||
 | 
							color: white;
 | 
				
			||||||
 | 
							text-transform: uppercase;
 | 
				
			||||||
 | 
							font-size: 18px;
 | 
				
			||||||
 | 
							font-weight: 700;
 | 
				
			||||||
 | 
							margin-top: 10px;
 | 
				
			||||||
 | 
							margin-bottom: 10px;
 | 
				
			||||||
 | 
							text-align: left;
 | 
				
			||||||
 | 
							padding-left: 50px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice button.a:hover {
 | 
				
			||||||
 | 
								background-color: #1488c6;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice button.b:hover {
 | 
				
			||||||
 | 
								background-color: #00a2a1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice button.a:focus {
 | 
				
			||||||
 | 
								background-color: #1488c6;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							#choice button.b:focus {
 | 
				
			||||||
 | 
								background-color: #00a2a1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#background-stats {
 | 
				
			||||||
 | 
						z-index: 1;
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						position: absolute;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#background-stats div {
 | 
				
			||||||
 | 
							transition: width 400ms ease-in-out;
 | 
				
			||||||
 | 
							display: inline-block;
 | 
				
			||||||
 | 
							margin-bottom: -4px;
 | 
				
			||||||
 | 
							width: 50%;
 | 
				
			||||||
 | 
							height: 100%;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
							
								
								
									
										1
									
								
								vote/dotnet/Vote/wwwroot/css/site.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vote/dotnet/Vote/wwwroot/css/site.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}}
 | 
				
			||||||
							
								
								
									
										13
									
								
								vote/dotnet/Vote/wwwroot/js/jquery-1.11.1-min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vote/dotnet/Vote/wwwroot/js/jquery-1.11.1-min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										16
									
								
								worker/dotnet/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								worker/dotnet/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					FROM microsoft/dotnet:2.1-sdk-nanoserver-sac2016 as builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /Worker
 | 
				
			||||||
 | 
					COPY Worker/Worker.csproj .
 | 
				
			||||||
 | 
					RUN dotnet restore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY /Worker .
 | 
				
			||||||
 | 
					RUN dotnet publish -c Release -o /out Worker.csproj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# app image
 | 
				
			||||||
 | 
					FROM microsoft/dotnet:2.1-runtime-nanoserver-sac2016
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					ENTRYPOINT ["dotnet", "Worker.dll"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY --from=builder /out .
 | 
				
			||||||
							
								
								
									
										7
									
								
								worker/dotnet/Worker/Data/IVoteData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								worker/dotnet/Worker/Data/IVoteData.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					namespace Worker.Data
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IVoteData
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        void Set(string voterId, string vote);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								worker/dotnet/Worker/Data/MySqlVoteData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								worker/dotnet/Worker/Data/MySqlVoteData.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using Worker.Entities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Data
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MySqlVoteData : IVoteData
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly VoteContext _context;        
 | 
				
			||||||
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public MySqlVoteData(VoteContext context, ILogger<MySqlVoteData> logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _context = context;
 | 
				
			||||||
 | 
					            _logger = logger;            
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Set(string voterId, string vote)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var currentVote = _context.Votes.Find(voterId);
 | 
				
			||||||
 | 
					            if (currentVote == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _context.Votes.Add(new Vote
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    VoterId = voterId,
 | 
				
			||||||
 | 
					                    VoteOption = vote
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (currentVote.VoteOption != vote)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                currentVote.VoteOption = vote;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _context.SaveChanges();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								worker/dotnet/Worker/Entities/Vote.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								worker/dotnet/Worker/Entities/Vote.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					using System.ComponentModel.DataAnnotations;
 | 
				
			||||||
 | 
					using System.ComponentModel.DataAnnotations.Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Entities
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [Table("votes")]
 | 
				
			||||||
 | 
					    public class Vote
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [Column("id")]
 | 
				
			||||||
 | 
					        [Key]
 | 
				
			||||||
 | 
					        public string VoterId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Column("vote")]
 | 
				
			||||||
 | 
					        public string VoteOption { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								worker/dotnet/Worker/Entities/VoteContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								worker/dotnet/Worker/Entities/VoteContext.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Entities
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class VoteContext : DbContext
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private static bool _EnsureCreated;
 | 
				
			||||||
 | 
					        public VoteContext(DbContextOptions options) : base(options)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!_EnsureCreated)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Database.EnsureCreated();
 | 
				
			||||||
 | 
					                _EnsureCreated = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DbSet<Vote> Votes { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								worker/dotnet/Worker/Messaging/IMessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								worker/dotnet/Worker/Messaging/IMessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using NATS.Client;
 | 
				
			||||||
 | 
					using Worker.Messaging.Messages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Messaging
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IMessageQueue
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        IConnection CreateConnection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void Publish<TMessage>(TMessage message) where TMessage : Message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								worker/dotnet/Worker/Messaging/MessageHelper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								worker/dotnet/Worker/Messaging/MessageHelper.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using Worker.Messaging.Messages;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Messaging
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MessageHelper
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static byte[] ToData<TMessage>(TMessage message)
 | 
				
			||||||
 | 
					            where TMessage : Message
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var json = JsonConvert.SerializeObject(message);
 | 
				
			||||||
 | 
					            return Encoding.Unicode.GetBytes(json);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static TMessage FromData<TMessage>(byte[] data)
 | 
				
			||||||
 | 
					            where TMessage : Message
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var json = Encoding.Unicode.GetString(data);
 | 
				
			||||||
 | 
					            return (TMessage)JsonConvert.DeserializeObject<TMessage>(json);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								worker/dotnet/Worker/Messaging/MessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								worker/dotnet/Worker/Messaging/MessageQueue.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using NATS.Client;
 | 
				
			||||||
 | 
					using Worker.Messaging.Messages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Messaging
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MessageQueue : IMessageQueue
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        protected readonly IConfiguration _configuration;
 | 
				
			||||||
 | 
					        protected readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public MessageQueue(IConfiguration configuration, ILogger<MessageQueue> logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _configuration = configuration;
 | 
				
			||||||
 | 
					            _logger = logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Publish<TMessage>(TMessage message)
 | 
				
			||||||
 | 
					            where TMessage : Message
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (var connection = CreateConnection())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var data = MessageHelper.ToData(message);
 | 
				
			||||||
 | 
					                connection.Publish(message.Subject, data);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IConnection CreateConnection()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var url = _configuration.GetValue<string>("MessageQueue:Url");
 | 
				
			||||||
 | 
					            return new ConnectionFactory().CreateConnection(url);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Messaging.Messages
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class VoteCastEvent : Message
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public override string Subject { get { return MessageSubject; } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string VoterId {get; set;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Vote {get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         public static string MessageSubject = "events.vote.votecast";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								worker/dotnet/Worker/Messaging/Messages/Message.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								worker/dotnet/Worker/Messaging/Messages/Message.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Messaging.Messages
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public abstract class Message
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string CorrelationId { get; set; }  
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public abstract string Subject { get; }      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Message()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            CorrelationId = Guid.NewGuid().ToString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								worker/dotnet/Worker/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								worker/dotnet/Worker/Program.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Worker.Data;
 | 
				
			||||||
 | 
					using Worker.Entities;
 | 
				
			||||||
 | 
					using Worker.Messaging;
 | 
				
			||||||
 | 
					using Worker.Workers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class Program
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static void Main(string[] args)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var config = new ConfigurationBuilder()
 | 
				
			||||||
 | 
					                .AddJsonFile("appsettings.json")
 | 
				
			||||||
 | 
					                .AddEnvironmentVariables()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var loggerFactory = new LoggerFactory()
 | 
				
			||||||
 | 
					                .AddConsole();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var services = new ServiceCollection()
 | 
				
			||||||
 | 
					                .AddSingleton(loggerFactory)
 | 
				
			||||||
 | 
					                .AddLogging()
 | 
				
			||||||
 | 
					                .AddSingleton<IConfiguration>(config)
 | 
				
			||||||
 | 
					                .AddTransient<IVoteData, MySqlVoteData>()
 | 
				
			||||||
 | 
					                .AddTransient<IMessageQueue, MessageQueue>()
 | 
				
			||||||
 | 
					                .AddSingleton<QueueWorker>()
 | 
				
			||||||
 | 
					                .AddDbContext<VoteContext>(builder => builder.UseMySQL(config.GetConnectionString("VoteData")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var provider = services.BuildServiceProvider();
 | 
				
			||||||
 | 
					            var worker = provider.GetService<QueueWorker>();
 | 
				
			||||||
 | 
					            worker.Start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								worker/dotnet/Worker/Worker.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								worker/dotnet/Worker/Worker.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <PropertyGroup>
 | 
				
			||||||
 | 
					    <OutputType>Exe</OutputType>
 | 
				
			||||||
 | 
					    <TargetFramework>netcoreapp2.1</TargetFramework>
 | 
				
			||||||
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <None Remove="appsettings.json" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <Content Include="appsettings.json">
 | 
				
			||||||
 | 
					      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
 | 
				
			||||||
 | 
					      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
 | 
					    </Content>
 | 
				
			||||||
 | 
					  </ItemGroup>  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.3" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="MySql.Data.EntityFrameworkCore" Version="8.0.12" />
 | 
				
			||||||
 | 
					    <PackageReference Include="NATS.Client" Version="0.8.1" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										62
									
								
								worker/dotnet/Worker/Workers/QueueWorker.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								worker/dotnet/Worker/Workers/QueueWorker.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using NATS.Client;
 | 
				
			||||||
 | 
					using Worker.Data;
 | 
				
			||||||
 | 
					using Worker.Messaging;
 | 
				
			||||||
 | 
					using Worker.Messaging.Messages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Worker.Workers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class QueueWorker
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private static ManualResetEvent _ResetEvent = new ManualResetEvent(false);
 | 
				
			||||||
 | 
					        private const string QUEUE_GROUP = "save-handler";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly IMessageQueue _messageQueue;
 | 
				
			||||||
 | 
					        private readonly IConfiguration _config;
 | 
				
			||||||
 | 
					        private readonly IVoteData _data;
 | 
				
			||||||
 | 
					        protected readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public QueueWorker(IMessageQueue messageQueue, IVoteData data, IConfiguration config, ILogger<QueueWorker> logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _messageQueue = messageQueue;
 | 
				
			||||||
 | 
					            _data = data;
 | 
				
			||||||
 | 
					            _config = config;
 | 
				
			||||||
 | 
					            _logger = logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Start()
 | 
				
			||||||
 | 
					        {       
 | 
				
			||||||
 | 
					            _logger.LogInformation($"Connecting to message queue url: {_config.GetValue<string>("MessageQueue:Url")}");
 | 
				
			||||||
 | 
					            using (var connection = _messageQueue.CreateConnection())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var subscription = connection.SubscribeAsync(VoteCastEvent.MessageSubject, QUEUE_GROUP);
 | 
				
			||||||
 | 
					                subscription.MessageHandler += SaveVote;
 | 
				
			||||||
 | 
					                subscription.Start();
 | 
				
			||||||
 | 
					                _logger.LogInformation($"Listening on subject: {VoteCastEvent.MessageSubject}, queue: {QUEUE_GROUP}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _ResetEvent.WaitOne();
 | 
				
			||||||
 | 
					                connection.Close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void SaveVote(object sender, MsgHandlerEventArgs e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _logger.LogDebug($"Received message, subject: {e.Message.Subject}");
 | 
				
			||||||
 | 
					            var voteMessage = MessageHelper.FromData<VoteCastEvent>(e.Message.Data);
 | 
				
			||||||
 | 
					            _logger.LogInformation($"Processing vote for '{voteMessage.Vote}' by '{voteMessage.VoterId}'");
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _data.Set(voteMessage.VoterId, voteMessage.Vote);
 | 
				
			||||||
 | 
					                _logger.LogDebug($"Succesffuly processed vote by '{voteMessage.VoterId}'");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.LogError($"Vote processing FAILED for '{voteMessage.VoterId}', exception: {ex}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								worker/dotnet/Worker/appsettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								worker/dotnet/Worker/appsettings.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "MessageQueue": {
 | 
				
			||||||
 | 
					    "Url": "nats://message-queue:4222"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "ConnectionStrings": {
 | 
				
			||||||
 | 
					    "VoteData": "Server=mysql;Port=4000;Database=votes;User=root;SslMode=None"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "Logging": {
 | 
				
			||||||
 | 
					    "LogLevel": {
 | 
				
			||||||
 | 
					      "Default": "Information"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "AllowedHosts": "*"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user