From 75f2d0baea806b72619808817b16da4a2240e005 Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Thu, 8 Feb 2024 23:32:41 +0100 Subject: [PATCH] Address breaking changes in products API --- .../Generated/AnalogCoreV2/AnalogCoreV2.cs | 218 +++++--------- Shifty.App/Components/ProductManager.razor | 44 +-- Shifty.App/DomainModels/Product.cs | 16 - Shifty.App/Repositories/IProductRepository.cs | 4 +- Shifty.App/Repositories/ProductRepository.cs | 6 +- Shifty.App/Services/ProductService.cs | 37 +-- .../OpenApiSpecs/AnalogCoreV2.json | 279 +++++++----------- 7 files changed, 215 insertions(+), 389 deletions(-) diff --git a/Shifty.Api/Generated/AnalogCoreV2/AnalogCoreV2.cs b/Shifty.Api/Generated/AnalogCoreV2/AnalogCoreV2.cs index b8b3125..949144b 100644 --- a/Shifty.Api/Generated/AnalogCoreV2/AnalogCoreV2.cs +++ b/Shifty.Api/Generated/AnalogCoreV2/AnalogCoreV2.cs @@ -1631,24 +1631,24 @@ public virtual async System.Threading.Tasks.Task ApiV2MobilepayWebhookAsync(stri } /// - /// Adds a new product to the database. + /// Adds a new product /// - /// The request containing the details of the product to be added and allowed user groups. - /// The request was successful, and the product was added. + /// The request containing the details of the product to be added and allowed user groups + /// The newly added product /// A server side error occurred. - public virtual System.Threading.Tasks.Task ApiV2ProductsPostAsync(AddProductRequest addProductRequest) + public virtual System.Threading.Tasks.Task ApiV2ProductsPostAsync(AddProductRequest addProductRequest) { return ApiV2ProductsPostAsync(addProductRequest, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Adds a new product to the database. + /// Adds a new product /// - /// The request containing the details of the product to be added and allowed user groups. - /// The request was successful, and the product was added. + /// The request containing the details of the product to be added and allowed user groups + /// The newly added product /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ApiV2ProductsPostAsync(AddProductRequest addProductRequest, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task ApiV2ProductsPostAsync(AddProductRequest addProductRequest, System.Threading.CancellationToken cancellationToken) { if (addProductRequest == null) throw new System.ArgumentNullException("addProductRequest"); @@ -1690,9 +1690,9 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 200) + if (status_ == 201) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1720,28 +1720,23 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr } /// - /// Updates a product with the specified changes. + /// Returns a list of available products based on a account's user group. /// - /// The request containing the changes to be applied to the product. - /// The request was successful, and the product was updated. + /// Successful request /// A server side error occurred. - public virtual System.Threading.Tasks.Task ApiV2ProductsPutAsync(UpdateProductRequest product) + public virtual System.Threading.Tasks.Task> ApiV2ProductsGetAsync() { - return ApiV2ProductsPutAsync(product, System.Threading.CancellationToken.None); + return ApiV2ProductsGetAsync(System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Updates a product with the specified changes. + /// Returns a list of available products based on a account's user group. /// - /// The request containing the changes to be applied to the product. - /// The request was successful, and the product was updated. + /// Successful request /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ApiV2ProductsPutAsync(UpdateProductRequest product, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> ApiV2ProductsGetAsync(System.Threading.CancellationToken cancellationToken) { - if (product == null) - throw new System.ArgumentNullException("product"); - var urlBuilder_ = new System.Text.StringBuilder(); urlBuilder_.Append("api/v2/products"); @@ -1751,11 +1746,7 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(product, _settings.Value); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); + request_.Method = new System.Net.Http.HttpMethod("GET"); request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); PrepareRequest(client_, request_, urlBuilder_); @@ -1781,7 +1772,7 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1789,6 +1780,12 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr return objectResponse_.Object; } else + if (status_ == 401) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new ApiException("Invalid credentials", status_, responseText_, headers_, null); + } + else { var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); @@ -1809,25 +1806,36 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr } /// - /// Returns a list of available products based on a account's user group. + /// Updates a product with the specified changes. /// - /// Successful request + /// Product Id + /// The request containing the changes to be applied to the product + /// The product was updated /// A server side error occurred. - public virtual System.Threading.Tasks.Task> ApiV2ProductsGetAsync() + public virtual System.Threading.Tasks.Task ApiV2ProductsPutAsync(int productId, UpdateProductRequest product) { - return ApiV2ProductsGetAsync(System.Threading.CancellationToken.None); + return ApiV2ProductsPutAsync(productId, product, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Returns a list of available products based on a account's user group. + /// Updates a product with the specified changes. /// - /// Successful request + /// Product Id + /// The request containing the changes to be applied to the product + /// The product was updated /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> ApiV2ProductsGetAsync(System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task ApiV2ProductsPutAsync(int productId, UpdateProductRequest product, System.Threading.CancellationToken cancellationToken) { + if (productId == null) + throw new System.ArgumentNullException("productId"); + + if (product == null) + throw new System.ArgumentNullException("product"); + var urlBuilder_ = new System.Text.StringBuilder(); - urlBuilder_.Append("api/v2/products"); + urlBuilder_.Append("api/v2/products/{id}"); + urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(productId, System.Globalization.CultureInfo.InvariantCulture))); var client_ = _httpClient; var disposeClient_ = false; @@ -1835,7 +1843,11 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - request_.Method = new System.Net.Http.HttpMethod("GET"); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(product, _settings.Value); + var content_ = new System.Net.Http.StringContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("PUT"); request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); PrepareRequest(client_, request_, urlBuilder_); @@ -1861,7 +1873,7 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1869,12 +1881,6 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr return objectResponse_.Object; } else - if (status_ == 401) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("Invalid credentials", status_, responseText_, headers_, null); - } - else { var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); @@ -1897,29 +1903,29 @@ public virtual async System.Threading.Tasks.Task ApiV2Pr /// /// Returns a product with the specified id /// - /// The id of the product to be returned + /// The id of the product to be returned /// Successful request /// A server side error occurred. - public virtual System.Threading.Tasks.Task ApiV2ProductsGetAsync(int id) + public virtual System.Threading.Tasks.Task ApiV2ProductsGetAsync(int productId) { - return ApiV2ProductsGetAsync(id, System.Threading.CancellationToken.None); + return ApiV2ProductsGetAsync(productId, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Returns a product with the specified id /// - /// The id of the product to be returned + /// The id of the product to be returned /// Successful request /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ApiV2ProductsGetAsync(int id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task ApiV2ProductsGetAsync(int productId, System.Threading.CancellationToken cancellationToken) { - if (id == null) - throw new System.ArgumentNullException("id"); + if (productId == null) + throw new System.ArgumentNullException("productId"); var urlBuilder_ = new System.Text.StringBuilder(); urlBuilder_.Append("api/v2/products/{id}"); - urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(productId, System.Globalization.CultureInfo.InvariantCulture))); var client_ = _httpClient; var disposeClient_ = false; @@ -3479,58 +3485,67 @@ public partial class EventData } /// - /// Represents the product response. + /// Represents a purchasable product /// [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.18.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v10.0.0.0))")] - public partial class ChangedProductResponse + public partial class ProductResponse { /// - /// Gets or sets the price of the product. + /// Id of product + /// + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] + public int Id { get; set; } + + /// + /// Product price /// [Newtonsoft.Json.JsonProperty("price", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Range(0, 2147483647)] public int Price { get; set; } /// - /// Gets or sets the number of tickets associated with the product. + /// Number of tickets in product /// [Newtonsoft.Json.JsonProperty("numberOfTickets", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Range(0, 2147483647)] public int NumberOfTickets { get; set; } /// - /// Gets or sets the name of the product. + /// Name of product /// [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Always)] [System.ComponentModel.DataAnnotations.Required] public string Name { get; set; } /// - /// Gets or sets the description of the product. + /// Description of products /// [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.Always)] [System.ComponentModel.DataAnnotations.Required] public string Description { get; set; } /// - /// Gets or sets the visibility of the product. + /// Eligible due to a user perk privilege + /// + [Newtonsoft.Json.JsonProperty("isPerk", Required = Newtonsoft.Json.Required.Always)] + public bool IsPerk { get; set; } + + /// + /// Visibility of products for users /// [Newtonsoft.Json.JsonProperty("visible", Required = Newtonsoft.Json.Required.Always)] public bool Visible { get; set; } /// - /// Gets or sets the user groups that can access the product. + /// Decides the user groups that can access the product. /// [Newtonsoft.Json.JsonProperty("allowedUserGroups", Required = Newtonsoft.Json.Required.Always, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] [System.ComponentModel.DataAnnotations.Required] public System.Collections.Generic.ICollection AllowedUserGroups { get; set; } = new System.Collections.ObjectModel.Collection(); /// - /// Gets or sets the eligibible menu items for the product. + /// The menu items that this product can be used on. /// - [Newtonsoft.Json.JsonProperty("menuItems", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public System.Collections.Generic.ICollection MenuItems { get; set; } = new System.Collections.ObjectModel.Collection(); + [Newtonsoft.Json.JsonProperty("eligibleMenuItems", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection EligibleMenuItems { get; set; } } @@ -3596,12 +3611,6 @@ public partial class AddProductRequest [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.18.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v10.0.0.0))")] public partial class UpdateProductRequest { - /// - /// Gets or sets the ID of the product to update. - /// - [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] - public int Id { get; set; } - /// /// Gets or sets the updated price of the product. /// @@ -3652,71 +3661,6 @@ public partial class UpdateProductRequest } - /// - /// Represents a purchasable product - /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.18.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v10.0.0.0))")] - public partial class ProductResponse - { - /// - /// Id of product - /// - [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] - public int Id { get; set; } - - /// - /// Product price - /// - [Newtonsoft.Json.JsonProperty("price", Required = Newtonsoft.Json.Required.Always)] - public int Price { get; set; } - - /// - /// Number of tickets in product - /// - [Newtonsoft.Json.JsonProperty("numberOfTickets", Required = Newtonsoft.Json.Required.Always)] - public int NumberOfTickets { get; set; } - - /// - /// Name of product - /// - [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public string Name { get; set; } - - /// - /// Description of products - /// - [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public string Description { get; set; } - - /// - /// Eligible due to a user perk privilege - /// - [Newtonsoft.Json.JsonProperty("isPerk", Required = Newtonsoft.Json.Required.Always)] - public bool IsPerk { get; set; } - - /// - /// Visibility of products for users - /// - [Newtonsoft.Json.JsonProperty("visible", Required = Newtonsoft.Json.Required.Always)] - public bool Visible { get; set; } - - /// - /// Decides the user groups that can access the product. - /// - [Newtonsoft.Json.JsonProperty("allowedUserGroups", Required = Newtonsoft.Json.Required.Always, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.ComponentModel.DataAnnotations.Required] - public System.Collections.Generic.ICollection AllowedUserGroups { get; set; } = new System.Collections.ObjectModel.Collection(); - - /// - /// The menu items that this product can be used on. - /// - [Newtonsoft.Json.JsonProperty("eligibleMenuItems", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.ICollection EligibleMenuItems { get; set; } - - } - /// /// Represents a purchase /// diff --git a/Shifty.App/Components/ProductManager.razor b/Shifty.App/Components/ProductManager.razor index 6cf7d43..f47b292 100644 --- a/Shifty.App/Components/ProductManager.razor +++ b/Shifty.App/Components/ProductManager.razor @@ -8,6 +8,7 @@ @using LanguageExt.UnsafeValueAccess @using Components @using Shifty.App.DomainModels +@using System.Collections.ObjectModel @inject ISnackbar Snackbar @inject IProductService ProductService @inject IJSRuntime JSRuntime @@ -127,7 +128,7 @@ @code { private MudDataGrid _dataGrid; - private IEnumerable _products = new List(); + private ObservableCollection _products = new ObservableCollection(); private Dictionary UserGroupDict = new(); private IEnumerable _allMenuItems = new List(); private Dictionary SelectedMenuItems = new(); @@ -148,7 +149,8 @@ var productsResult = await ProductService.GetProducts(); productsResult.Match( Succ: products => { - _products = products; + _products = new ObservableCollection(products); + StateHasChanged(); }, Fail: error => { Snackbar.Add(error.Message, Severity.Error); @@ -194,22 +196,16 @@ var result = await ProductService.AddProduct(item); result.Match( - Succ: async product => + Succ: product => { // Succesfully added product Snackbar.Add("Product added", Severity.Success); - // Retrieve all items again in order to update id - var retrieveItems = await ProductService.GetProducts(); - retrieveItems.Match( - Succ: items => { - _products = items; - }, - Fail: error => { - // Errors while re-retrieving items, non-fatal (just means _products are slightly outdated) - Snackbar.Add(error.Message, Severity.Warning); - } - ); + // Override the created product with the placeholder item + _products.Remove(item); + _products.Add(product); + + StateHasChanged(); }, Fail: error => { Snackbar.Add(error.Message, Severity.Error); @@ -221,20 +217,10 @@ var result = await ProductService.UpdateProduct(item, item.Id); result.Match( - Succ: async result => + Succ: product => { Snackbar.Add("Product updated", Severity.Success); - - var retrieveItems = await ProductService.GetProducts(); - retrieveItems.Match( - Succ: items => { - _products = items; - }, - Fail: error => { - // Errors while re-retrieving items, non-fatal (just means _products are slightly outdated) - Snackbar.Add(error.Message, Severity.Warning); - } - ); + _products.FirstOrDefault(p => p.Id == item.Id).IsPerk = product.IsPerk; }, Fail: error => { @@ -246,7 +232,7 @@ void AddItemToDataGrid() { - _products = _products.Append(new Product{ + _products.Add(new Product{ Id = 0, Name = "", Description = "", @@ -254,8 +240,10 @@ NumberOfTickets = 1, Price = 0, Visible = true, - AllowedUserGroups = new List() + AllowedUserGroups = new List(), + EligibleMenuItems = new List(), }); + StateHasChanged(); _dataGrid.SetEditingItemAsync(_products.Last()); } diff --git a/Shifty.App/DomainModels/Product.cs b/Shifty.App/DomainModels/Product.cs index caacad6..4c2af2c 100644 --- a/Shifty.App/DomainModels/Product.cs +++ b/Shifty.App/DomainModels/Product.cs @@ -29,21 +29,6 @@ public static Product FromDto(ProductResponse dto) IsPerk = dto.IsPerk }; } - public static Product FromChangedProduct(ChangedProductResponse dto, int id, bool isPerk) - { - return new Product() - { - Name = dto.Name, - Price = dto.Price, - Description = dto.Description, - NumberOfTickets = dto.NumberOfTickets, - EligibleMenuItems = dto.MenuItems.Map(MenuItem.FromDto), - AllowedUserGroups = dto.AllowedUserGroups, - Visible = dto.Visible, - Id = id, - IsPerk = isPerk - }; - } public static AddProductRequest ToAddRequest(Product product) { @@ -70,7 +55,6 @@ public static UpdateProductRequest ToUpdateRequest(Product product) AllowedUserGroups = product.AllowedUserGroups.ToList(), Visible = product.Visible, Price = product.Price, - Id = product.Id, }; } diff --git a/Shifty.App/Repositories/IProductRepository.cs b/Shifty.App/Repositories/IProductRepository.cs index 44e439c..0c3c01f 100644 --- a/Shifty.App/Repositories/IProductRepository.cs +++ b/Shifty.App/Repositories/IProductRepository.cs @@ -8,8 +8,8 @@ namespace Shifty.App.Repositories { public interface IProductRepository { - Task> AddProduct(AddProductRequest addProductRequest); + Task> AddProduct(AddProductRequest addProductRequest); public Task>> GetProducts(); - Task> UpdateProduct(UpdateProductRequest product); + Task> UpdateProduct(int productId, UpdateProductRequest product); } } \ No newline at end of file diff --git a/Shifty.App/Repositories/ProductRepository.cs b/Shifty.App/Repositories/ProductRepository.cs index b6dfa61..8fab7e1 100644 --- a/Shifty.App/Repositories/ProductRepository.cs +++ b/Shifty.App/Repositories/ProductRepository.cs @@ -20,14 +20,14 @@ public ProductRepository(AnalogCoreV2 client) _client = client; } - public async Task> AddProduct(AddProductRequest addProductRequest) + public async Task> AddProduct(AddProductRequest addProductRequest) { return await TryAsync(async () => await _client.ApiV2ProductsPostAsync(addProductRequest)); } - public async Task> UpdateProduct(UpdateProductRequest product) + public async Task> UpdateProduct(int productId, UpdateProductRequest product) { - return await TryAsync(async () => await _client.ApiV2ProductsPutAsync(product)); + return await TryAsync(async () => await _client.ApiV2ProductsPutAsync(productId, product)); } async Task>> IProductRepository.GetProducts() diff --git a/Shifty.App/Services/ProductService.cs b/Shifty.App/Services/ProductService.cs index 27b2dab..02456b5 100644 --- a/Shifty.App/Services/ProductService.cs +++ b/Shifty.App/Services/ProductService.cs @@ -1,14 +1,6 @@ -using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; -using Blazored.LocalStorage; using LanguageExt; -using LanguageExt.Common; -using LanguageExt.UnsafeValueAccess; -using Shifty.Api.Generated.AnalogCoreV1; -using Shifty.Api.Generated.AnalogCoreV2; -using Shifty.App.Authentication; using Shifty.App.DomainModels; using Shifty.App.Repositories; @@ -32,36 +24,17 @@ public async Task>> GetProducts() public async Task> UpdateProduct(Product product, int id) { - var productrequest = new UpdateProductRequest(){ - Id = id, - Name = product.Name, - Price = product.Price, - Description = product.Description, - AllowedUserGroups = product.AllowedUserGroups.ToList(), - MenuItemIds = product.EligibleMenuItems.Map(x => x.Id).ToList(), - NumberOfTickets = product.NumberOfTickets, - Visible = product.Visible - }; + var productrequest = Product.ToUpdateRequest(product); - return await _productRepository.UpdateProduct(productrequest) - .Map(p => Product.FromChangedProduct(p, id, false)); // TODO: Fix when UpdateProduct returns IsPerk property + return await _productRepository.UpdateProduct(id, productrequest) + .Map(p => Product.FromDto(p)); } public async Task> AddProduct(Product product) { - var productrequest = new AddProductRequest(){ - Name = product.Name, - Price = product.Price, - Description = product.Description, - AllowedUserGroups = product.AllowedUserGroups.ToList(), - MenuItemIds = product.EligibleMenuItems.Map(x => x.Id).ToList(), - NumberOfTickets = product.NumberOfTickets, - Visible = product.Visible - }; + var newproduct = await _productRepository.AddProduct(Product.ToAddRequest(product)); - var changedproduct = await _productRepository.AddProduct(productrequest); - - return changedproduct.Map(p => Product.FromChangedProduct(p, 0, false)); // TODO: Fix when AddProduct no longer returns a ChangedProductResponse + return newproduct.Map(p => Product.FromDto(p)); } } } \ No newline at end of file diff --git a/Shifty.GenerateApi/OpenApiSpecs/AnalogCoreV2.json b/Shifty.GenerateApi/OpenApiSpecs/AnalogCoreV2.json index 3ebbcb7..b11ad5b 100644 --- a/Shifty.GenerateApi/OpenApiSpecs/AnalogCoreV2.json +++ b/Shifty.GenerateApi/OpenApiSpecs/AnalogCoreV2.json @@ -17,7 +17,7 @@ }, "servers": [ { - "url": "https://core.dev.analogio.dk" + "url": "http://localhost:5001" } ], "paths": { @@ -773,11 +773,11 @@ "tags": [ "Products" ], - "summary": "Adds a new product to the database.", + "summary": "Adds a new product", "operationId": "Products_AddProduct", "requestBody": { "x-name": "addProductRequest", - "description": "The request containing the details of the product to be added and allowed user groups.", + "description": "The request containing the details of the product to be added and allowed user groups", "content": { "application/json": { "schema": { @@ -789,12 +789,12 @@ "x-position": 1 }, "responses": { - "200": { - "description": "The request was successful, and the product was added.", + "201": { + "description": "The newly added product", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChangedProductResponse" + "$ref": "#/components/schemas/ProductResponse" } } } @@ -809,35 +809,28 @@ } ] }, - "put": { + "get": { "tags": [ "Products" ], - "summary": "Updates a product with the specified changes.", - "operationId": "Products_UpdateProduct", - "requestBody": { - "x-name": "product", - "description": "The request containing the changes to be applied to the product.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateProductRequest" - } - } - }, - "required": true, - "x-position": 1 - }, + "summary": "Returns a list of available products based on a account's user group.", + "operationId": "Products_GetProducts", "responses": { "200": { - "description": "The request was successful, and the product was updated.", + "description": "Successful request", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChangedProductResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductResponse" + } } } } + }, + "401": { + "description": "Invalid credentials" } }, "security": [ @@ -848,29 +841,52 @@ "apikey": [] } ] - }, - "get": { + } + }, + "/api/v2/products/{id}": { + "put": { "tags": [ "Products" ], - "summary": "Returns a list of available products based on a account's user group.", - "operationId": "Products_GetProducts", + "summary": "Updates a product with the specified changes.", + "operationId": "Products_UpdateProduct", + "parameters": [ + { + "name": "id", + "x-originalName": "productId", + "in": "path", + "required": true, + "description": "Product Id", + "schema": { + "type": "integer", + "format": "int32" + }, + "x-position": 1 + } + ], + "requestBody": { + "x-name": "product", + "description": "The request containing the changes to be applied to the product", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProductRequest" + } + } + }, + "required": true, + "x-position": 2 + }, "responses": { "200": { - "description": "Successful request", + "description": "The product was updated", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProductResponse" - } + "$ref": "#/components/schemas/ProductResponse" } } } - }, - "401": { - "description": "Invalid credentials" } }, "security": [ @@ -881,9 +897,7 @@ "apikey": [] } ] - } - }, - "/api/v2/products/{id}": { + }, "get": { "tags": [ "Products" @@ -893,6 +907,7 @@ "parameters": [ { "name": "id", + "x-originalName": "productId", "in": "path", "required": true, "description": "The id of the product to be returned", @@ -2011,70 +2026,98 @@ } } }, - "ChangedProductResponse": { + "ProductResponse": { "type": "object", - "description": "Represents the product response.", + "description": "Represents a purchasable product", + "example": { + "id": 1, + "price": 300, + "numberOfTickets": 10, + "name": "Coffee clip card", + "description": "Coffee clip card of 10 clips", + "isPerk": true, + "visible": true, + "allowedUserGroups": [ + "Manager", + "Board" + ], + "eligibleMenuItems": [ + { + "id": 1, + "name": "Cappuccino" + }, + { + "id": 2, + "name": "Caffe Latte" + } + ] + }, "additionalProperties": false, "required": [ + "id", "price", "numberOfTickets", "name", "description", + "isPerk", "visible", - "allowedUserGroups", - "menuItems" + "allowedUserGroups" ], "properties": { + "id": { + "type": "integer", + "description": "Id of product", + "format": "int32", + "example": 1 + }, "price": { "type": "integer", - "description": "Gets or sets the price of the product.", + "description": "Product price", "format": "int32", - "maximum": 2147483647.0, - "minimum": 0.0, - "example": 150 + "example": 300 }, "numberOfTickets": { "type": "integer", - "description": "Gets or sets the number of tickets associated with the product.", + "description": "Number of tickets in product", "format": "int32", - "maximum": 2147483647.0, - "minimum": 0.0, - "example": 5 + "example": 10 }, "name": { "type": "string", - "description": "Gets or sets the name of the product.", + "description": "Name of product", "minLength": 1, - "example": "Espresso" + "example": "Coffee clip card" }, "description": { "type": "string", - "description": "Gets or sets the description of the product.", + "description": "Description of products", "minLength": 1, - "example": "A homemade espresso from fresh beans" + "example": "Coffee clip card of 10 clips" + }, + "isPerk": { + "type": "boolean", + "description": "Eligible due to a user perk privilege ", + "example": true }, "visible": { "type": "boolean", - "description": "Gets or sets the visibility of the product.", + "description": "Visibility of products for users", "example": true }, "allowedUserGroups": { "type": "array", - "description": "Gets or sets the user groups that can access the product.", - "example": [ - "Manager", - "Board" - ], + "description": "Decides the user groups that can access the product.", + "example": "Manager, Board ", "items": { "$ref": "#/components/schemas/UserGroup" } }, - "menuItems": { + "eligibleMenuItems": { "type": "array", - "description": "Gets or sets the eligibible menu items for the product.", + "description": "The menu items that this product can be used on.", "example": [ - "Espresso", - "Cappuccino" + "Cappuccino", + "Caffe Latte" ], "items": { "$ref": "#/components/schemas/MenuItemResponse" @@ -2160,7 +2203,6 @@ "description": "Initiate an update product request.", "additionalProperties": false, "required": [ - "id", "price", "numberOfTickets", "name", @@ -2170,12 +2212,6 @@ "menuItemIds" ], "properties": { - "id": { - "type": "integer", - "description": "Gets or sets the ID of the product to update.", - "format": "int32", - "example": 1 - }, "price": { "type": "integer", "description": "Gets or sets the updated price of the product.", @@ -2235,105 +2271,6 @@ } } }, - "ProductResponse": { - "type": "object", - "description": "Represents a purchasable product", - "example": { - "id": 1, - "price": 300, - "numberOfTickets": 10, - "name": "Coffee clip card", - "description": "Coffee clip card of 10 clips", - "isPerk": true, - "visible": true, - "allowedUserGroups": [ - "Manager", - "Board" - ], - "eligibleMenuItems": [ - { - "id": 1, - "name": "Cappuccino" - }, - { - "id": 2, - "name": "Caffe Latte" - } - ] - }, - "additionalProperties": false, - "required": [ - "id", - "price", - "numberOfTickets", - "name", - "description", - "isPerk", - "visible", - "allowedUserGroups" - ], - "properties": { - "id": { - "type": "integer", - "description": "Id of product", - "format": "int32", - "example": 1 - }, - "price": { - "type": "integer", - "description": "Product price", - "format": "int32", - "example": 300 - }, - "numberOfTickets": { - "type": "integer", - "description": "Number of tickets in product", - "format": "int32", - "example": 10 - }, - "name": { - "type": "string", - "description": "Name of product", - "minLength": 1, - "example": "Coffee clip card" - }, - "description": { - "type": "string", - "description": "Description of products", - "minLength": 1, - "example": "Coffee clip card of 10 clips" - }, - "isPerk": { - "type": "boolean", - "description": "Eligible due to a user perk privilege ", - "example": true - }, - "visible": { - "type": "boolean", - "description": "Visibility of products for users", - "example": true - }, - "allowedUserGroups": { - "type": "array", - "description": "Decides the user groups that can access the product.", - "example": "Manager, Board ", - "items": { - "$ref": "#/components/schemas/UserGroup" - } - }, - "eligibleMenuItems": { - "type": "array", - "description": "The menu items that this product can be used on.", - "example": [ - "Cappuccino", - "Caffe Latte" - ], - "items": { - "$ref": "#/components/schemas/MenuItemResponse" - } - } - } - }, "SimplePurchaseResponse": { "type": "object", "description": "Represents a purchase",