From 8860d8bfddf9adda8f7c81e1ef8195bfb1634e9d Mon Sep 17 00:00:00 2001 From: Simon Zeltser Date: Thu, 21 Jun 2018 11:09:12 -0700 Subject: [PATCH] added redis integration and command line arguments handling to cart service. Didn't test in the cloud yet --- .gitignore | 3 + .vscode/launch.json | 28 +++++++ .vscode/tasks.json | 15 ++++ src/cartservice/cartstore/LocalCartStore.cs | 41 ++++++++++ src/cartservice/cartstore/RedisCartStore.cs | 74 +++++++++++++++++++ src/cartservice/interfaces/ICartStore.cs | 12 +++ .../run_redis_emulator_windows.bat | 11 +++ 7 files changed, 184 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 src/cartservice/cartstore/LocalCartStore.cs create mode 100644 src/cartservice/cartstore/RedisCartStore.cs create mode 100644 src/cartservice/interfaces/ICartStore.cs create mode 100644 src/cartservice/run_redis_emulator_windows.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf3cbd3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +src/cartservice/bin/* +src/cartservice/obj/* +.vs/*.* \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6cba3ec --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/src/cartservice/bin/Debug/netcoreapp2.0/cartservice.dll", + "args": [], + "cwd": "${workspaceFolder}/src/cartservice", + // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window + "console": "internalConsole", + "stopAtEntry": false, + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ,] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..ac157b4 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/cartservice/cartservice.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/src/cartservice/cartstore/LocalCartStore.cs b/src/cartservice/cartstore/LocalCartStore.cs new file mode 100644 index 0000000..66c8d40 --- /dev/null +++ b/src/cartservice/cartstore/LocalCartStore.cs @@ -0,0 +1,41 @@ +using System.Collections.Concurrent; +using cartservice.interfaces; + +namespace cartservice.cartstore +{ + internal class LocalCartStore + { + // Maps between user and their cart + private ConcurrentDictionary userCartItems = new ConcurrentDictionary(); + + public void AddItem(string userId, string productId, int quantity) + { + Cart cart; + if (!userCartItems.TryGetValue(userId, out cart)) + { + cart = new Cart(userId); + } + else + { + cart = userCartItems[userId]; + } + cart.AddItem(productId, quantity); + } + + public void EmptyCart(string userId) + { + Cart cart; + if (userCartItems.TryGetValue(userId, out cart)) + { + cart.EmptyCart(); + } + } + + public Cart GetCart(string userId) + { + Cart cart = null; + userCartItems.TryGetValue(userId, out cart); + return cart; + } + } +} \ No newline at end of file diff --git a/src/cartservice/cartstore/RedisCartStore.cs b/src/cartservice/cartstore/RedisCartStore.cs new file mode 100644 index 0000000..8745a19 --- /dev/null +++ b/src/cartservice/cartstore/RedisCartStore.cs @@ -0,0 +1,74 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using cartservice.interfaces; +using Google.Protobuf; +using Hipstershop; +using StackExchange.Redis; + +namespace cartservice.cartstore +{ + public class RedisCartStore : ICartStore + { + private const string CART_FIELD_NAME = "cart"; + + private readonly ConnectionMultiplexer redis; + + private readonly byte[] emptyCartBytes; + + public RedisCartStore(string redisAddress) + { + // Serialize empty cart into byte array. + var cart = new Hipstershop.Cart(); + emptyCartBytes = cart.ToByteArray(); + + string connectionString = $"{redisAddress},ssl=false,allowAdmin=true"; + Console.WriteLine("Connecting to Redis: " + connectionString); + redis = ConnectionMultiplexer.Connect(connectionString); + } + + public async Task AddItemAsync(string userId, string productId, int quantity) + { + var db = redis.GetDatabase(); + + // Access the cart from the cache + var value = await db.HashGetAsync(userId, CART_FIELD_NAME); + + Hipstershop.Cart cart; + if (value.IsNull) + { + cart = new Hipstershop.Cart(); + } + else + { + cart = Hipstershop.Cart.Parser.ParseFrom(value); + } + + cart.Items.Add(new Hipstershop.CartItem { ProductId = productId, Quantity = quantity }); + } + + public async Task EmptyCartAsync(string userId) + { + var db = redis.GetDatabase(); + + // Update the cache with empty cart for given user + await db.HashSetAsync(userId, new[] { new HashEntry(CART_FIELD_NAME, emptyCartBytes) }); + } + + public async Task GetCartAsync(string userId) + { + var db = redis.GetDatabase(); + + // Access the cart from the cache + var value = await db.HashGetAsync(userId, CART_FIELD_NAME); + + Hipstershop.Cart cart = null; + if (!value.IsNull) + { + cart = Hipstershop.Cart.Parser.ParseFrom(value); + } + + return cart; + } + } +} \ No newline at end of file diff --git a/src/cartservice/interfaces/ICartStore.cs b/src/cartservice/interfaces/ICartStore.cs new file mode 100644 index 0000000..f4fa1e5 --- /dev/null +++ b/src/cartservice/interfaces/ICartStore.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace cartservice.interfaces +{ + internal interface ICartStore + { + Task AddItemAsync(string userId, string productId, int quantity); + Task EmptyCartAsync(string userId); + + Task GetCartAsync(string userId); + } +} \ No newline at end of file diff --git a/src/cartservice/run_redis_emulator_windows.bat b/src/cartservice/run_redis_emulator_windows.bat new file mode 100644 index 0000000..8b1d9c0 --- /dev/null +++ b/src/cartservice/run_redis_emulator_windows.bat @@ -0,0 +1,11 @@ +@echo off +rem install redis on windows using choco +rem choco install redis-64 + +rem run redis +redis-server --daemonize yes + +rem testing locally +rem redis-cli +rem SET foo bar +rem GET foo \ No newline at end of file