diff --git a/.gitignore b/.gitignore index fa5af1f..6e5b5e7 100644 --- a/.gitignore +++ b/.gitignore @@ -355,3 +355,6 @@ MigrationBackup/ # App_Data directory - ignore all contents except .gitkeep **/App_Data/* !**/App_Data/.gitkeep + +# macOS +**/.DS_Store diff --git a/global.json b/global.json new file mode 100644 index 0000000..4443491 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "9.0.302", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/packages.lock.json b/packages.lock.json deleted file mode 100644 index 38e5a2d..0000000 --- a/packages.lock.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net9.0": { - "coverlet.collector": { - "type": "Direct", - "requested": "[6.0.2, )", - "resolved": "6.0.2", - "contentHash": "bJShQ6uWRTQ100ZeyiMqcFlhP7WJ+bCuabUs885dJiBEzMsJMSFr7BOyeCw4rgvQokteGi5rKQTlkhfQPUXg2A==" - }, - "FSharp.Core": { - "type": "Direct", - "requested": "[9.0.202, )", - "resolved": "9.0.202", - "contentHash": "p8iGT2PeRL9q0ZIQ3ZhmRMRs4+b5Xaf9SSYKOt/+tDtznMQhHovYWaD1491RvBz8rnxcesAISbrkrjI1/4TUtw==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.12.0, )", - "resolved": "17.12.0", - "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==", - "dependencies": { - "Microsoft.CodeCoverage": "17.12.0", - "Microsoft.TestPlatform.TestHost": "17.12.0" - } - }, - "xunit": { - "type": "Direct", - "requested": "[2.9.2, )", - "resolved": "2.9.2", - "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==", - "dependencies": { - "xunit.analyzers": "1.16.0", - "xunit.assert": "2.9.2", - "xunit.core": "[2.9.2]" - } - }, - "xunit.runner.visualstudio": { - "type": "Direct", - "requested": "[2.8.2, )", - "resolved": "2.8.2", - "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.12.0", - "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA==" - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.12.0", - "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.12.0", - "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.12.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" - }, - "xunit.analyzers": { - "type": "Transitive", - "resolved": "1.16.0", - "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q==" - }, - "xunit.assert": { - "type": "Transitive", - "resolved": "2.9.2", - "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ==" - }, - "xunit.core": { - "type": "Transitive", - "resolved": "2.9.2", - "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==", - "dependencies": { - "xunit.extensibility.core": "[2.9.2]", - "xunit.extensibility.execution": "[2.9.2]" - } - }, - "xunit.extensibility.core": { - "type": "Transitive", - "resolved": "2.9.2", - "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } - }, - "xunit.extensibility.execution": { - "type": "Transitive", - "resolved": "2.9.2", - "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==", - "dependencies": { - "xunit.extensibility.core": "[2.9.2]" - } - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.eShopWeb.Web/Basket/Basket.Component.fs b/src/Microsoft.eShopWeb.Web/Basket/Basket.Component.fs index 94f3c2b..c22c725 100644 --- a/src/Microsoft.eShopWeb.Web/Basket/Basket.Component.fs +++ b/src/Microsoft.eShopWeb.Web/Basket/Basket.Component.fs @@ -28,7 +28,21 @@ module BasketComponent = class' "esh-basket-image" ] ] section [ class' "esh-basket-item esh-basket-item--middle col" ] [ raw item.ProductName ] section [ class' "esh-basket-item esh-basket-item--middle col" ] [ raw (item.UnitPrice.ToString "C") ] - section [ class' "esh-basket-item esh-basket-item--middle col" ] [ raw (item.Quantity.ToString()) ] + section [ class' "esh-basket-item esh-basket-item--middle col" ] [ + div [ class' "d-flex align-items-center" ] [ + Elem.form + [ method "post"; action "/basket/update-quantity"; class' "d-inline me-1" ] + [ input [ type' "hidden"; name "id"; value $"{item.CatalogItemId}" ] + input [ type' "hidden"; name "quantity"; value $"{item.Quantity - 1}" ] + input [ class' "btn btn-outline-secondary btn-sm"; type' "submit"; value "-" ] ] + Elem.span [ class' "mx-2" ] [ raw (item.Quantity.ToString()) ] + Elem.form + [ method "post"; action "/basket/update-quantity"; class' "d-inline ms-1" ] + [ input [ type' "hidden"; name "id"; value $"{item.CatalogItemId}" ] + input [ type' "hidden"; name "quantity"; value $"{item.Quantity + 1}" ] + input [ class' "btn btn-outline-secondary btn-sm"; type' "submit"; value "+" ] ] + ] + ] section [ class' "esh-basket-item esh-basket-item--middle col" ] [ raw ((decimal(item.Quantity) * item.UnitPrice).ToString "C" ) ] section [ class' "esh-basket-item esh-basket-item--middle col" ] [ Elem.form diff --git a/src/Microsoft.eShopWeb.Web/Basket/Basket.Domain.fs b/src/Microsoft.eShopWeb.Web/Basket/Basket.Domain.fs index 1ba0b52..da9e901 100644 --- a/src/Microsoft.eShopWeb.Web/Basket/Basket.Domain.fs +++ b/src/Microsoft.eShopWeb.Web/Basket/Basket.Domain.fs @@ -99,6 +99,37 @@ module BasketDomain = return None } + let updateBasketItemQuantity (db: ShopContext) catalogItemId newQuantity = + async { + let! existingBasket = + (db.Baskets.Include(fun b -> b.Items).OrderBy(fun b -> b.Id)) |> tryFirstAsync + + let basket = existingBasket |> defaultValue emptyBasket + + try + // Find the basket item to update + let itemToUpdate = + db.BasketItems.Where(fun bi -> bi.CatalogItemId = catalogItemId && bi.BasketId = basket.Id) + |> Seq.tryHead + + match itemToUpdate with + | Some item -> + if newQuantity > 0 then + item.Quantity <- newQuantity + do! saveChangesAsync' db |> Async.Ignore + return Some newQuantity + else + // Remove the item if quantity becomes 0 or negative + db.BasketItems.Remove(item) |> ignore + do! saveChangesAsync' db |> Async.Ignore + return Some 0 + | None -> + return None + with exp -> + printfn $"Error updating quantity for item {catalogItemId} in basket"; printfn $"{exp}" + return None + } + let removeFromBasket (db: ShopContext) catalogItemId = async { let! existingBasket = diff --git a/src/Microsoft.eShopWeb.Web/Basket/Basket.Page.fs b/src/Microsoft.eShopWeb.Web/Basket/Basket.Page.fs index ab1ef95..a01db14 100644 --- a/src/Microsoft.eShopWeb.Web/Basket/Basket.Page.fs +++ b/src/Microsoft.eShopWeb.Web/Basket/Basket.Page.fs @@ -64,6 +64,23 @@ module BasketPage = | None -> Response.redirectPermanently "/basket?error=notfound" | Some _ -> Response.redirectPermanently "/basket?removed=success")) + let updateQuantity: HttpHandler = + Services.inject (fun db -> + + let mapAsync = fun (form: FormCollectionReader) -> + let catalogItemId = form.TryGetGuid "id" + let quantity = form.TryGetInt "quantity" + + match catalogItemId, quantity with + | Some id, Some qty -> BasketDomain.updateBasketItemQuantity db id qty |> Async.StartAsTask + | _ -> async { return None } |> Async.StartAsTask + + Request.mapFormAsync mapAsync (fun result -> + match result with + | None -> Response.redirectPermanently "/basket?error=updatefailed" + | Some 0 -> Response.redirectPermanently "/basket?removed=success" + | Some qty -> Response.redirectPermanently $"/basket?updated={qty}")) + // This uses a more low-level approach to reading the form let postAlternate: HttpHandler = Services.inject (fun db -> fun ctx -> diff --git a/src/Microsoft.eShopWeb.Web/Microsoft.eShopWeb.Web.fsproj b/src/Microsoft.eShopWeb.Web/Microsoft.eShopWeb.Web.fsproj index fcaad0f..f40355b 100644 --- a/src/Microsoft.eShopWeb.Web/Microsoft.eShopWeb.Web.fsproj +++ b/src/Microsoft.eShopWeb.Web/Microsoft.eShopWeb.Web.fsproj @@ -24,6 +24,6 @@ - + diff --git a/src/Microsoft.eShopWeb.Web/Program.fs b/src/Microsoft.eShopWeb.Web/Program.fs index 868e953..ecb944d 100644 --- a/src/Microsoft.eShopWeb.Web/Program.fs +++ b/src/Microsoft.eShopWeb.Web/Program.fs @@ -68,6 +68,7 @@ module Program = get "/basket" BasketPage.get post "/basket" BasketPage.post post "/basket/remove" BasketPage.remove + post "/basket/update-quantity" BasketPage.updateQuantity get "/identity/account/login" LoginPage.handler diff --git a/src/Microsoft.eShopWeb.Web/Properties/launchSettings.json b/src/Microsoft.eShopWeb.Web/Properties/launchSettings.json index b3e9f07..b471e16 100644 --- a/src/Microsoft.eShopWeb.Web/Properties/launchSettings.json +++ b/src/Microsoft.eShopWeb.Web/Properties/launchSettings.json @@ -6,7 +6,6 @@ "applicationUrl": "https://localhost:7055", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_HTTPS_PORT": "7055", "DOTNET_WATCH_RESTART_ON_RUDE_EDIT": "true" } }, @@ -25,4 +24,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/Microsoft.eShopWeb.Web/packages.lock.json b/src/Microsoft.eShopWeb.Web/packages.lock.json index b45c10f..4b72766 100644 --- a/src/Microsoft.eShopWeb.Web/packages.lock.json +++ b/src/Microsoft.eShopWeb.Web/packages.lock.json @@ -30,18 +30,18 @@ }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Direct", - "requested": "[9.0.6, )", - "resolved": "9.0.6", - "contentHash": "bVSdfFrqIo3ZeQfWYYfnVVanP1GWghkdw+MnEmZJz7jUwtdPQpBKHr0BW9dMizPamzU+SMA1Qu4nXuRTlKVAGQ==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.6", - "Microsoft.Extensions.DependencyModel": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6", + "requested": "[9.0.7, )", + "resolved": "9.0.7", + "contentHash": "87dAv0nX4rBIa29L7sZdUZ1FE4NDn9J51g6WJ+j5dTUQwNEg52YDEmo+/TxBtRnBSAca9boo80k8F7+LzUo2qQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.DependencyModel": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.10", "SQLitePCLRaw.core": "2.1.10", - "System.Text.Json": "9.0.6" + "System.Text.Json": "9.0.7" } }, "Falco.Markup": { @@ -59,32 +59,32 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "3auiudiViGzj1TidUdjuDqtP3+f6PBk4xdw6r9sBaTtkYoGc3AZn0cP8LgYZaLRnJBqY5bXRLB+qhjoB+iATzA==", + "resolved": "9.0.7", + "contentHash": "yjlU0Wu0tAexFPlo/bbkYFMWyEkVHqr5AONyh91YJ4KH+hys+eAzHQxd14aZwtQOgpJ5s9r3QL9+tVJww8w69Q==", "dependencies": { "SQLitePCLRaw.core": "2.1.10" } }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "r5hzM6Bhw4X3z28l5vmsaCPjk9VsQP4zaaY01THh1SAYjgTMVadYIvpNkCfmrv/Klks6aIf2A9eY7cpGZab/hg==", + "resolved": "9.0.7", + "contentHash": "PbD0q5ax15r91jD4TN7xbDCjldZSz4JfpYN4ZZjAkWeUyROkV92Ydg0O2/1keFA+2u3KPsDkJMmBKv2zQ06ZVg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "9.0.6", - "Microsoft.EntityFrameworkCore.Analyzers": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6" + "Microsoft.EntityFrameworkCore.Abstractions": "9.0.7", + "Microsoft.EntityFrameworkCore.Analyzers": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "7MkhPK8emb8hfOx/mFVvHuIHxQ+mH2YdlK4sFUXgsGlvR0A44vsmd2wcHavZOTTzaKhN+aFUVy3zmkztKmTo+A==" + "resolved": "9.0.7", + "contentHash": "YUXNerEkCf4OANO+zjuMznpUW7R8XxSCqmBfYhBrbrJVc09i84KkNgeUTaOUXCGogSK/3d7ORRhMqfUobnejBg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "VKggHNQC5FCn3/vooaIM/4aEjGmrmWm78IrdRLz9lLV0Rm9bVHEr/jiWApDkU0U9ec2xGAilvQqJ5mMX7QC2cw==" + "resolved": "9.0.7", + "contentHash": "HqiPjAvjVOsyA1svnjL81/Wk2MRQYMK/lxKVWvw0f5IcA//VcxBepVSAqe7CFirdsPXqe8rFKEwZROWZTz7Jqw==" }, "Microsoft.EntityFrameworkCore.Design": { "type": "Transitive", @@ -97,107 +97,107 @@ }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "Ht6OT17sYnO31Dx+hX72YHrc5kZt53g5napaw0FpyIekXCvb+gUVvufEG55Fa7taFm8ccy0Vzs+JVNR9NL0JlA==", + "resolved": "9.0.7", + "contentHash": "Yo5joquG7L79H5BhtpqP8apu+KFOAYfvmj0dZnVkPElBY14wY5qva0SOcrDWzYw5BrJrhIArfCcJCJHBvMYiKg==", "dependencies": { - "Microsoft.EntityFrameworkCore": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6" + "Microsoft.EntityFrameworkCore": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "xP+SvMDR/GZCDNXFw7z4WYbO2sYpECvht3+lqejg+Md8vLtURwTBvdsOUAnY4jBGmNFqHeh87hZSmUGmuxyqMA==", + "resolved": "9.0.7", + "contentHash": "DLB8n5Z7U8+xCkh+NSrvOlylCmveDg5RpPdqBftq5nU8Yt3vIdBg0X/YkESGDBWUL9h0vxuhgH2aqXL3FYz5tQ==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "9.0.6", - "Microsoft.EntityFrameworkCore.Relational": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.6", - "Microsoft.Extensions.DependencyModel": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6", + "Microsoft.Data.Sqlite.Core": "9.0.7", + "Microsoft.EntityFrameworkCore.Relational": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.DependencyModel": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", "SQLitePCLRaw.core": "2.1.10", - "System.Text.Json": "9.0.6" + "System.Text.Json": "9.0.7" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "bL/xQsVNrdVkzjP5yjX4ndkQ03H3+Bk3qPpl+AMCEJR2RkfgAYmoQ/xXffPV7is64+QHShnhA12YAaFmNbfM+A==", + "resolved": "9.0.7", + "contentHash": "30necCQehcg9lFkMEIE7HczcoYGML8GUH6jlincA18d896fLZM9wl5tpTPJHgzANQE/6KXRLZSWbgevgg5csSw==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "qPW2d798tBPZcRmrlaBJqyChf2+0odDdE+0Lxvrr0ywkSNl1oNMK8AKrOfDwyXyjuLCv0ua7p6nrUExCeXhCcg==", + "resolved": "9.0.7", + "contentHash": "nDu6c8fwrHQYccLnWnvyElrdkL3rZ97TZNqL+niMFUcApVBHdpDmKcRvciGymJ4Y0iLDTOo5J2XhDQEbNb+dFg==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "9.0.6", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Logging.Abstractions": "9.0.6", - "Microsoft.Extensions.Options": "9.0.6", - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.Caching.Abstractions": "9.0.7", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "3GgMIi2jP8g1fBW93Z9b9Unamc0SIsgyhiCmC91gq4loTixK9vQMuxxUsfJ1kRGwn+/FqLKwOHqmn0oYWn3Fvw==", + "resolved": "9.0.7", + "contentHash": "lut/kiVvNsQ120VERMUYSFhpXPpKjjql+giy03LesASPBBcC0o6+aoFdzJH9GaYpFTQ3fGVhVjKjvJDoAW5/IQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "vS65HMo5RS10DD543fknsyVDxihMcVxVn3/hNaILgBxWYnOLxWIeCIO9X0QFuCvPRNjClvXe9Aj8KaQNx7vFkQ==", + "resolved": "9.0.7", + "contentHash": "i05AYA91vgq0as84ROVCyltD2gnxaba/f1Qw2rG7mUsS0gv8cPTr1Gm7jPQHq7JTr4MJoQUcanLVs16tIOUJaQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "0Zn6nR/6g+90MxskZyOOMPQvnPnrrGu6bytPwkV+azDcTtCSuQ1+GJUrg8Klmnrjk1i6zMpw2lXijl+tw7Q3kA==" + "resolved": "9.0.7", + "contentHash": "iPK1FxbGFr2Xb+4Y+dTYI8Gupu9pOi8I3JPuPsrogUmEhe2hzZ9LpCmolMEBhVDo2ikcSr7G5zYiwaapHSQTew==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "grVU1ixgMHp+kuhIgvEzhE73jXRY6XmxNBPWrotmbjB9AvJvkwHnIzm1JlOsPpyixFgnzreh/bFBMJAjveX+fQ==" + "resolved": "9.0.7", + "contentHash": "aXEt8QW1Fj9aC81GfkMtfip4wfbkEA7VBvNkx6Rx6ZKyqXIF/9qzRtH6v/2096IDK4lt6dlQp5Ajf+kjHfUdOA==" }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "XBzjitTFaQhF8EbJ645vblZezV1p52ePTxKHoVkRidHF11Xkjxg94qr0Rvp2qyxK2vBJ4OIZ41NB15YUyxTGMQ==", + "resolved": "9.0.7", + "contentHash": "fdIeQpXYV8yxSWG03cCbU2Otdrq4NWuhnQLXokWLv3L9YcK055E7u8WFJvP+uuP4CFeCEoqZQL4yPcjuXhCZrg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "9.0.6", - "Microsoft.Extensions.Logging.Abstractions": "9.0.6", - "Microsoft.Extensions.Options": "9.0.6" + "Microsoft.Extensions.DependencyInjection": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "LFnyBNK7WtFmKdnHu3v0HOYQ8BcjYuy0jdC9pgCJ/rbLKoJEG9/dBzSKMEeeWDbDeoWS0TIxOC8a9CM5ufca3A==", + "resolved": "9.0.7", + "contentHash": "sMM6NEAdUTE/elJ2wqjOi0iBWqZmSyaTByLF9e8XHv6DRJFFnOe0N+s8Uc6C91E4SboQCfLswaBIZ+9ZXA98AA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "wUPhNM1zsI58Dy10xRdF2+pnsisiUuETg5ZBncyAEEUm/CQ9Q1vmivyUWH8RDbAlqyixf2dJNQ2XZb7HsKUEQw==", + "resolved": "9.0.7", + "contentHash": "trJnF6cRWgR5uMmHpGoHmM1wOVFdIYlELlkO9zX+RfieK0321Y55zrcs4AaEymKup7dxgEN/uJU25CAcMNQRXw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "BHniU24QV67qp1pJknqYSofAPYGmijGI8D+ci9yfw33iuFdyOeB9lWTg78ThyYLyQwZw3s0vZ36VMb0MqbUuLw==" + "resolved": "9.0.7", + "contentHash": "ti/zD9BuuO50IqlvhWQs9GHxkCmoph5BHjGiWKdg2t6Or8XoyAfRJiKag+uvd/fpASnNklfsB01WpZ4fhAe0VQ==" }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", @@ -236,8 +236,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "h+ZtYTyTnTh5Ju6mHCKb3FPGx4ylJZgm9W7Y2psUnkhQRPMOIxX+TCN0ZgaR/+Yea+93XHWAaMzYTar1/EHIPg==" + "resolved": "9.0.7", + "contentHash": "u/lN2FEEXs3ghj2ta8tWA4r2MS9Yni07K7jDmnz8h1UPDf0lIIIEMkWx383Zz4fJjJio7gDl+00RYuQ/7R8ZQw==" } } } diff --git a/tests/Basket/UpdateBasketItemQuantity.fs b/tests/Basket/UpdateBasketItemQuantity.fs new file mode 100644 index 0000000..b329fb9 --- /dev/null +++ b/tests/Basket/UpdateBasketItemQuantity.fs @@ -0,0 +1,143 @@ +module UpdateBasketItemQuantity + +open System +open System.Linq +open Xunit +open Microsoft.EntityFrameworkCore +open Microsoft.eShopWeb.Web +open Microsoft.eShopWeb.Web.Domain +open Microsoft.eShopWeb.Web.Persistence +open Microsoft.eShopWeb.Web.Basket.BasketDomain +open EntityFrameworkCore.FSharp.DbContextHelpers + +// Helper function to create an in-memory database context +let createInMemoryContext () = + let options = DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName = Guid.NewGuid().ToString()) + .Options + new ShopContext(options) + +// Helper function to seed test data +let seedTestData (context: ShopContext) = + let basketId = Guid.NewGuid() + let catalogItemId = Guid.NewGuid() + + // Create a basket entry first to ensure the basket exists + let basketItem = { + Id = 0 + CatalogItemId = catalogItemId + ProductName = "Test Product" + UnitPrice = 10.0M + OldUnitPrice = 10.0M + Quantity = 2 + PictureUri = "/test.png" + BasketId = basketId + } + + // Create the basket by directly executing insert (without navigation property issues) + // Since we can't use SQL with in-memory, we need to create a minimal basket + // We'll work around this by using the emptyBasket approach the function expects + + // First add the basket item with emptyBasket.Id + let basketItemWithEmptyBasketId = { basketItem with BasketId = Unchecked.defaultof } + context.BasketItems.Add(basketItemWithEmptyBasketId) |> ignore + context.SaveChanges() |> ignore + + (catalogItemId, Unchecked.defaultof) + +[] +let ``updateBasketItemQuantity should update quantity when item exists and quantity is positive`` () = + async { + // Arrange + use context = createInMemoryContext() + context.Database.EnsureCreated() |> ignore + let (catalogItemId, basketId) = seedTestData context + let newQuantity = 5 + + // Act + let! result = updateBasketItemQuantity context catalogItemId newQuantity + + // Assert + Assert.Equal(Some newQuantity, result) + + // Verify item quantity was updated in database + let updatedItem = context.BasketItems.Where(fun bi -> bi.CatalogItemId = catalogItemId && bi.BasketId = basketId) + |> Seq.tryHead + match updatedItem with + | Some item -> Assert.Equal(newQuantity, item.Quantity) + | None -> Assert.True(false, "Item should exist in database") + } + +[] +let ``updateBasketItemQuantity should remove item when quantity is zero`` () = + async { + // Arrange + use context = createInMemoryContext() + context.Database.EnsureCreated() |> ignore + let (catalogItemId, basketId) = seedTestData context + let newQuantity = 0 + + // Act + let! result = updateBasketItemQuantity context catalogItemId newQuantity + + // Assert + Assert.Equal(Some 0, result) + + // Verify item was removed from database + let remainingItems = context.BasketItems.Where(fun bi -> bi.CatalogItemId = catalogItemId && bi.BasketId = basketId) + |> Seq.toList + Assert.Empty(remainingItems) + } + +[] +let ``updateBasketItemQuantity should remove item when quantity is negative`` () = + async { + // Arrange + use context = createInMemoryContext() + context.Database.EnsureCreated() |> ignore + let (catalogItemId, basketId) = seedTestData context + let newQuantity = -1 + + // Act + let! result = updateBasketItemQuantity context catalogItemId newQuantity + + // Assert + Assert.Equal(Some 0, result) + + // Verify item was removed from database + let remainingItems = context.BasketItems.Where(fun bi -> bi.CatalogItemId = catalogItemId && bi.BasketId = basketId) + |> Seq.toList + Assert.Empty(remainingItems) + } + +[] +let ``updateBasketItemQuantity should return None when item does not exist`` () = + async { + // Arrange + use context = createInMemoryContext() + context.Database.EnsureCreated() |> ignore + let nonExistentItemId = Guid.NewGuid() + let newQuantity = 5 + + // Act + let! result = updateBasketItemQuantity context nonExistentItemId newQuantity + + // Assert + Assert.Equal(None, result) + } + +[] +let ``updateBasketItemQuantity should handle empty basket gracefully`` () = + async { + // Arrange + use context = createInMemoryContext() + context.Database.EnsureCreated() |> ignore + let itemId = Guid.NewGuid() + let newQuantity = 3 + + // Act + let! result = updateBasketItemQuantity context itemId newQuantity + + // Assert + Assert.Equal(None, result) + } \ No newline at end of file diff --git a/tests/FShopOnWeb.Tests.fsproj b/tests/FShopOnWeb.Tests.fsproj index 88f719e..7e105ce 100644 --- a/tests/FShopOnWeb.Tests.fsproj +++ b/tests/FShopOnWeb.Tests.fsproj @@ -8,6 +8,7 @@ + @@ -16,7 +17,7 @@ - + diff --git a/tests/packages.lock.json b/tests/packages.lock.json index 1da65ff..1edbf9c 100644 --- a/tests/packages.lock.json +++ b/tests/packages.lock.json @@ -26,13 +26,13 @@ }, "Microsoft.EntityFrameworkCore.InMemory": { "type": "Direct", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "Pm4NBnv3aB8O5bBNwWRkL4a/H+3WdgKRKYD93FkR9TrUNb0jfns9JVN5w9WEUsQCm0C69Eg2Y85i8pdmSfaNnQ==", + "requested": "[9.0.7, )", + "resolved": "9.0.7", + "contentHash": "/EQbD4/m83r3AW4z69SIwjRvEPFJchhCIQ80DbUiw3H6T2fXEVCf6YjbBlLE6Q/syHYu2JYBScxyAQ4/q8J2xA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "9.0.0", - "Microsoft.Extensions.Caching.Memory": "9.0.0", - "Microsoft.Extensions.Logging": "9.0.0" + "Microsoft.EntityFrameworkCore": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7" } }, "Microsoft.NET.Test.Sdk": { @@ -91,32 +91,32 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "3auiudiViGzj1TidUdjuDqtP3+f6PBk4xdw6r9sBaTtkYoGc3AZn0cP8LgYZaLRnJBqY5bXRLB+qhjoB+iATzA==", + "resolved": "9.0.7", + "contentHash": "yjlU0Wu0tAexFPlo/bbkYFMWyEkVHqr5AONyh91YJ4KH+hys+eAzHQxd14aZwtQOgpJ5s9r3QL9+tVJww8w69Q==", "dependencies": { "SQLitePCLRaw.core": "2.1.10" } }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "r5hzM6Bhw4X3z28l5vmsaCPjk9VsQP4zaaY01THh1SAYjgTMVadYIvpNkCfmrv/Klks6aIf2A9eY7cpGZab/hg==", + "resolved": "9.0.7", + "contentHash": "PbD0q5ax15r91jD4TN7xbDCjldZSz4JfpYN4ZZjAkWeUyROkV92Ydg0O2/1keFA+2u3KPsDkJMmBKv2zQ06ZVg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "9.0.6", - "Microsoft.EntityFrameworkCore.Analyzers": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6" + "Microsoft.EntityFrameworkCore.Abstractions": "9.0.7", + "Microsoft.EntityFrameworkCore.Analyzers": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "7MkhPK8emb8hfOx/mFVvHuIHxQ+mH2YdlK4sFUXgsGlvR0A44vsmd2wcHavZOTTzaKhN+aFUVy3zmkztKmTo+A==" + "resolved": "9.0.7", + "contentHash": "YUXNerEkCf4OANO+zjuMznpUW7R8XxSCqmBfYhBrbrJVc09i84KkNgeUTaOUXCGogSK/3d7ORRhMqfUobnejBg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "VKggHNQC5FCn3/vooaIM/4aEjGmrmWm78IrdRLz9lLV0Rm9bVHEr/jiWApDkU0U9ec2xGAilvQqJ5mMX7QC2cw==" + "resolved": "9.0.7", + "contentHash": "HqiPjAvjVOsyA1svnjL81/Wk2MRQYMK/lxKVWvw0f5IcA//VcxBepVSAqe7CFirdsPXqe8rFKEwZROWZTz7Jqw==" }, "Microsoft.EntityFrameworkCore.Design": { "type": "Transitive", @@ -129,122 +129,122 @@ }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "Ht6OT17sYnO31Dx+hX72YHrc5kZt53g5napaw0FpyIekXCvb+gUVvufEG55Fa7taFm8ccy0Vzs+JVNR9NL0JlA==", + "resolved": "9.0.7", + "contentHash": "Yo5joquG7L79H5BhtpqP8apu+KFOAYfvmj0dZnVkPElBY14wY5qva0SOcrDWzYw5BrJrhIArfCcJCJHBvMYiKg==", "dependencies": { - "Microsoft.EntityFrameworkCore": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6" + "Microsoft.EntityFrameworkCore": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "bVSdfFrqIo3ZeQfWYYfnVVanP1GWghkdw+MnEmZJz7jUwtdPQpBKHr0BW9dMizPamzU+SMA1Qu4nXuRTlKVAGQ==", + "resolved": "9.0.7", + "contentHash": "87dAv0nX4rBIa29L7sZdUZ1FE4NDn9J51g6WJ+j5dTUQwNEg52YDEmo+/TxBtRnBSAca9boo80k8F7+LzUo2qQ==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.6", - "Microsoft.Extensions.DependencyModel": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6", + "Microsoft.EntityFrameworkCore.Sqlite.Core": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.DependencyModel": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.10", "SQLitePCLRaw.core": "2.1.10", - "System.Text.Json": "9.0.6" + "System.Text.Json": "9.0.7" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "xP+SvMDR/GZCDNXFw7z4WYbO2sYpECvht3+lqejg+Md8vLtURwTBvdsOUAnY4jBGmNFqHeh87hZSmUGmuxyqMA==", + "resolved": "9.0.7", + "contentHash": "DLB8n5Z7U8+xCkh+NSrvOlylCmveDg5RpPdqBftq5nU8Yt3vIdBg0X/YkESGDBWUL9h0vxuhgH2aqXL3FYz5tQ==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "9.0.6", - "Microsoft.EntityFrameworkCore.Relational": "9.0.6", - "Microsoft.Extensions.Caching.Memory": "9.0.6", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.6", - "Microsoft.Extensions.DependencyModel": "9.0.6", - "Microsoft.Extensions.Logging": "9.0.6", + "Microsoft.Data.Sqlite.Core": "9.0.7", + "Microsoft.EntityFrameworkCore.Relational": "9.0.7", + "Microsoft.Extensions.Caching.Memory": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.DependencyModel": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", "SQLitePCLRaw.core": "2.1.10", - "System.Text.Json": "9.0.6" + "System.Text.Json": "9.0.7" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "bL/xQsVNrdVkzjP5yjX4ndkQ03H3+Bk3qPpl+AMCEJR2RkfgAYmoQ/xXffPV7is64+QHShnhA12YAaFmNbfM+A==", + "resolved": "9.0.7", + "contentHash": "30necCQehcg9lFkMEIE7HczcoYGML8GUH6jlincA18d896fLZM9wl5tpTPJHgzANQE/6KXRLZSWbgevgg5csSw==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "qPW2d798tBPZcRmrlaBJqyChf2+0odDdE+0Lxvrr0ywkSNl1oNMK8AKrOfDwyXyjuLCv0ua7p6nrUExCeXhCcg==", + "resolved": "9.0.7", + "contentHash": "nDu6c8fwrHQYccLnWnvyElrdkL3rZ97TZNqL+niMFUcApVBHdpDmKcRvciGymJ4Y0iLDTOo5J2XhDQEbNb+dFg==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "9.0.6", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Logging.Abstractions": "9.0.6", - "Microsoft.Extensions.Options": "9.0.6", - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.Caching.Abstractions": "9.0.7", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "3GgMIi2jP8g1fBW93Z9b9Unamc0SIsgyhiCmC91gq4loTixK9vQMuxxUsfJ1kRGwn+/FqLKwOHqmn0oYWn3Fvw==", + "resolved": "9.0.7", + "contentHash": "lut/kiVvNsQ120VERMUYSFhpXPpKjjql+giy03LesASPBBcC0o6+aoFdzJH9GaYpFTQ3fGVhVjKjvJDoAW5/IQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "vS65HMo5RS10DD543fknsyVDxihMcVxVn3/hNaILgBxWYnOLxWIeCIO9X0QFuCvPRNjClvXe9Aj8KaQNx7vFkQ==", + "resolved": "9.0.7", + "contentHash": "i05AYA91vgq0as84ROVCyltD2gnxaba/f1Qw2rG7mUsS0gv8cPTr1Gm7jPQHq7JTr4MJoQUcanLVs16tIOUJaQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "0Zn6nR/6g+90MxskZyOOMPQvnPnrrGu6bytPwkV+azDcTtCSuQ1+GJUrg8Klmnrjk1i6zMpw2lXijl+tw7Q3kA==" + "resolved": "9.0.7", + "contentHash": "iPK1FxbGFr2Xb+4Y+dTYI8Gupu9pOi8I3JPuPsrogUmEhe2hzZ9LpCmolMEBhVDo2ikcSr7G5zYiwaapHSQTew==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "grVU1ixgMHp+kuhIgvEzhE73jXRY6XmxNBPWrotmbjB9AvJvkwHnIzm1JlOsPpyixFgnzreh/bFBMJAjveX+fQ==" + "resolved": "9.0.7", + "contentHash": "aXEt8QW1Fj9aC81GfkMtfip4wfbkEA7VBvNkx6Rx6ZKyqXIF/9qzRtH6v/2096IDK4lt6dlQp5Ajf+kjHfUdOA==" }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "XBzjitTFaQhF8EbJ645vblZezV1p52ePTxKHoVkRidHF11Xkjxg94qr0Rvp2qyxK2vBJ4OIZ41NB15YUyxTGMQ==", + "resolved": "9.0.7", + "contentHash": "fdIeQpXYV8yxSWG03cCbU2Otdrq4NWuhnQLXokWLv3L9YcK055E7u8WFJvP+uuP4CFeCEoqZQL4yPcjuXhCZrg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "9.0.6", - "Microsoft.Extensions.Logging.Abstractions": "9.0.6", - "Microsoft.Extensions.Options": "9.0.6" + "Microsoft.Extensions.DependencyInjection": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "LFnyBNK7WtFmKdnHu3v0HOYQ8BcjYuy0jdC9pgCJ/rbLKoJEG9/dBzSKMEeeWDbDeoWS0TIxOC8a9CM5ufca3A==", + "resolved": "9.0.7", + "contentHash": "sMM6NEAdUTE/elJ2wqjOi0iBWqZmSyaTByLF9e8XHv6DRJFFnOe0N+s8Uc6C91E4SboQCfLswaBIZ+9ZXA98AA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "wUPhNM1zsI58Dy10xRdF2+pnsisiUuETg5ZBncyAEEUm/CQ9Q1vmivyUWH8RDbAlqyixf2dJNQ2XZb7HsKUEQw==", + "resolved": "9.0.7", + "contentHash": "trJnF6cRWgR5uMmHpGoHmM1wOVFdIYlELlkO9zX+RfieK0321Y55zrcs4AaEymKup7dxgEN/uJU25CAcMNQRXw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Primitives": "9.0.6" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "BHniU24QV67qp1pJknqYSofAPYGmijGI8D+ci9yfw33iuFdyOeB9lWTg78ThyYLyQwZw3s0vZ36VMb0MqbUuLw==" + "resolved": "9.0.7", + "contentHash": "ti/zD9BuuO50IqlvhWQs9GHxkCmoph5BHjGiWKdg2t6Or8XoyAfRJiKag+uvd/fpASnNklfsB01WpZ4fhAe0VQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", @@ -310,8 +310,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "9.0.6", - "contentHash": "h+ZtYTyTnTh5Ju6mHCKb3FPGx4ylJZgm9W7Y2psUnkhQRPMOIxX+TCN0ZgaR/+Yea+93XHWAaMzYTar1/EHIPg==" + "resolved": "9.0.7", + "contentHash": "u/lN2FEEXs3ghj2ta8tWA4r2MS9Yni07K7jDmnz8h1UPDf0lIIIEMkWx383Zz4fJjJio7gDl+00RYuQ/7R8ZQw==" }, "xunit.abstractions": { "type": "Transitive", @@ -359,7 +359,7 @@ "EntityFrameworkCore.FSharp": "[6.0.7, )", "FSharp.Core": "[9.0.300, )", "Falco": "[4.0.6, )", - "Microsoft.EntityFrameworkCore.Sqlite": "[9.0.6, )" + "Microsoft.EntityFrameworkCore.Sqlite": "[9.0.7, )" } } }