diff --git a/README.md b/README.md index b6474a0..754b66c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## Description -This integration is primary intended for existing Kentico 13 (KX 13) E-Commerce projects to enable them to migrate +This integration is primary intended for existing Kentico Xperience 13 (KX 13) E-Commerce projects to enable them to migrate to new Xperience By Kentico (XbyK) and still use KX 13 E-Commerce functionality.\ It can also be used as a basis for new projects where E-Commerce data will be stored on KX 13, but further development is necessary to achieve this goal. @@ -30,20 +30,20 @@ is located in [Dancing Goat XbyK example project](./examples/DancingGoat-K13Ecom ![Product listing widget](./images/screenshots/product_listing_widget.png "Product listing widget") ### Full scale e-commerce solution - We recommend to use for possible partial migration of existing e-commerce projects from KX 13 to XbyK. - - Product data (with variants and images) are [synchronized to Content hub](./docs/Usage-Guide.md#products-synchronization) (can be [turned off](./docs/Usage-Guide.md#setup-1)). + - Product data (with variants and images) is [synchronized to Content hub](./docs/Usage-Guide.md#products-synchronization) (can be [turned off](./docs/Usage-Guide.md#setup-1)). ![Products in content hub](./images/screenshots/products_content_hub.png "Products in content hub") - Product listing, detail and checkout process are placed on XbyK (shopping cart is saved and calculated still on KX 13). ![Cart content](./images/screenshots/cart_content.png "Cart content") - - Orders are created from cart, order related data are saved on KX 13 side. - - Linking products to categories in Pages channels need to be done manually from Content hub. - Page types are prepared to CI restore, details info in [this section of User Guide](./docs/Usage-Guide.md#dancing-goat-example---setup). + - Orders are created from cart, order related data is saved on KX 13 side. + - Products need to be manually linked from Content hub to pages in website channel applications. You can use CI to restore examples of content types for pages that display products. + See [this section of User Guide](./docs/Usage-Guide.md#dancing-goat-example---setup) for detailed information. ![Store pages](./images/screenshots/store_pages.png "Store pages") - [Sample XbyK Dancing Goat site](./examples/DancingGoat-K13Ecommerce) implements store functionality and can be used as an example of migration of existing e-commerce projects to new XbyK. - There are a couple of services which cover these actions: +The integration provides an API with services for implementing the following scenarios: - Listing products based on parameters, product categories, prices and inventory - Actions with shopping cart, changing currency and order creation - - Listing of orders (currently suitable for implementing listing orders in administration, not in My account) + - Listing of orders (currently suitable for implementing listing orders in administration) - **Order updates and listing for specific customers are under development** - Listing site cultures and currencies - Check [this part of User Guide](./docs/Usage-Guide.md#kx-13-e-commerce-integration-in-xperience-by-kentico) for more specific description @@ -67,14 +67,14 @@ create simple e-shop with product listing, product detail and checkout process o REST Store API on your own KX 13 e-commerce solution. - **KX 13 administration project (CMSApp) is not part of this solution!** -![Project diagram](./images/project_diagram.png "Project diagram") +![Project diagram](./images/kentico_ecommerce_diagram.svg "Project diagram") #### Kentico.Xperience.K13Ecommerce.Libs.sln - - Contains only libraries without sample sites, used for publishing of nuget packages + - Contains only libraries without sample sites, used for publishing of NuGet packages ## Library Version Matrix -Summary of libraries which are supported by the following versions Xperince by Kentico / Kentico Xperience 13 +Summary of libraries which are supported by the following versions Xperience by Kentico / Kentico Xperience 13 ### Kentico Xperience 13 E-Commerce integration @@ -89,11 +89,11 @@ Summary of libraries which are supported by the following versions Xperince by K Xperience by Kentico application: - [ASP.NET Core 8.0](https://dotnet.microsoft.com/en-us/download) -- [Xperience by Kentico](https://docs.xperience.io/xp/changelog) +- [Xperience by Kentico](https://docs.kentico.com/x/6wocCQ) Kentico Xperience 13 application (or standalone API app): - [ASP.NET Core 6.0](https://dotnet.microsoft.com/en-us/download) -- [Kentico Xperience 13 Refresh 11](https://docs.kentico.com/13/release-notes-xperience-13) +- [Kentico Xperience 13 Refresh 11](https://docs.kentico.com/x/GQeRBg) ## Package Installation @@ -121,7 +121,7 @@ dotnet add package Kentico.Xperience.StoreApi **First set up your Kentico 13 ASP.NET Core application**: -1. Set up your own [settings](.\examples\Kentico13_DancingGoatStore\appsettings.json) for Store REST API authentication (based on JWT and OAuth client credentials flow) +1. Set up your own [settings](./examples/Kentico13_DancingGoatStore/appsettings.json) for Store REST API authentication (based on JWT and OAuth client credentials flow) ```json { "CMSStoreApi": { @@ -161,7 +161,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment environment) **Then set up your Xperience By Kentico application** -1. Fill [settings](.\examples\DancingGoat-K13Ecommerce\appsettings.json) to connect your Kentico Xperience 13 instance +1. Fill [settings](./examples/DancingGoat-K13Ecommerce/appsettings.json) to connect your Kentico Xperience 13 instance ```json { "CMSKenticoStoreConfig": { diff --git a/docs/Usage-Guide.md b/docs/Usage-Guide.md index 612e445..226b9bb 100644 --- a/docs/Usage-Guide.md +++ b/docs/Usage-Guide.md @@ -11,7 +11,7 @@ to create E-Commerce solution on XbyK. ## Store API (Kentico Xperience 13) -Store API (library `Kentico.Xperience.StoreApi`) is REST API which exposes KX 13 E-Commerce features to consume then from another sources +Store API (library `Kentico.Xperience.StoreApi`) is a REST API which exposes KX 13 E-Commerce features, and allows them to be consumed from other sources. (primary intended for Xperience By Kentico, but you are completely free to use it any way you want). API is exposed via [Swagger](https://swagger.io/) ([Open API 3 standard](https://swagger.io/specification/)) on relative path `/swagger/storeapi/swagger.json` @@ -27,13 +27,13 @@ already generated there. API is intended to use with [OAuth 2.0 client credentials flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4), when ClientId and ClientSecret are shared between client application (XByK) and KX 13 application. Access tokens are generated in [JWT standard](https://jwt.io/introduction) (from endpoint `/api/store/auth/token`). -Token request can contain `username` parameter to identify for which user token is generated. -This user name's existence is validated and then embedded in token as `sub` and `name` claims. All subsequent +Token request can contain `user_email` parameter to identify for which user token is generated. +The endpoint validates that the user for given email exists, and then embeds it into the token as `sub` and `name` claims. All subsequent requests need to be [sent with Bearer token](https://www.dofactory.com/code-examples/csharp/authorization-header) in [Authorization](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) header. All API controllers are secured by custom authorization attribute and filter `AuthorizeStore`. This filter checks user claim and when this user exists and is enabled, is then assigned to `MembershipContext.AuthenticatedUser`. When -specific user name isn't provided, AuthenticatedUser remains as public user. +specific user email isn't provided, AuthenticatedUser remains as public user. ### Products @@ -47,11 +47,11 @@ These endpoints have prefix `/api/store/products` and cover these domains: These endpoints have prefix `api/store/cart` and cover work with current shopping cart. Many actions correspond to functionality in KX 13 `CMS.Ecommerce.IShoppingService` (adding/removing items to cart, set delivery data, creating order etc.). All endpoints use `ShoppingCartGUID` parameter sent -in HTTP header to identify current shopping cart. Client application (XbyK) needs to manage this identifier (this already covers -`Kentico.Xperience.K13Ecommerce` library for XByK applications). +in HTTP header to identify current shopping cart. +Management of this identifier is automatically handled in client (XByK) applications by the `Kentico.Xperience.K13Ecommerce` package. All calls internally use IShoppingService with some -noticeable customizations to handle [retrieving cart](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/retrieving-the-current-shopping-cart) in RESTful manner. +notable customizations to handle [retrieving cart](https://docs.kentico.com/x/gQuRBg) in RESTful manner. These customizations are applied only on request with `api/store` prefix to not break default e-commerce functionality: - Custom `IShoppingCartCache` - session usage is removed, cache key for cart's cache token identifier (`jti` claim) is used instead. @@ -75,31 +75,33 @@ Not all cart's data can be changed, e.g. custom data (properties like ShoppingCa via API. ### Orders -- Endpoint `api/store/order/list` for retrieving list of orders based on request (supports paging) +- Endpoint `api/store/order/list` for retrieving list of orders for current customer based on request (supports paging) +- Endpoint `api/store/order/admin/list` for retrieving list of orders (for all customers) based on request (supports paging) to display in XbyK administration (supports paging) ### Customers -- Endpoint `api/store/customer/addresses` for retrieving customer's addresses +- Endpoint `api/store/customer/addresses` for retrieving current customer's addresses +- Endpoint `api/store/customer/admin/addresses` for retrieving addresses of specific customer to display in XbyK administration ### Store site - Endpoint `api/store/site/cultures` returns all enabled site cultures - Endpoint `api/store/site/currencies` returns all enabled site currencies -### User synchronization -When user is created on XbyK, this user needs to be synchronized to KX 13, then user can be used for API authorization. -(user identity is generated in JWT). -We suppose that users are already synchronized between client (XbyK) and KX app before starting using this API. +### Members synchronization +When [member](https://docs.kentico.com/x/BIsuCw) is created on XbyK (for example when a new customer registers), this member needs to be synchronized to KX 13 as a user. +It is subsequently used for API authorization (member/user identity is generated in JWT). +Before you start using the Store API, you need to synchronize all website members between the client (XbyK) and your KX 13 application. Complete synchronization is not part of this PoC solution. -- Endpoint `api/store/synchronization/user-synchronization` creates new user - - Client app (XbyK) should use this to be ensured that all new users on client's are synchronized to KX 13, this is necessary when client's -e-commerce solution allows users to log in. Users are created with random generated password and are used only for +- Endpoint `api/store/synchronization/user-synchronization` can be used to create a new user in KX 13 + - The client application (XbyK) should use this to ensure that all new members are synchronized to KX 13. This is necessary when client's +e-commerce solution allows visitors to sign in. KX 13 users are created with random generated password and are used only for API authorization and assigning to MembershipContext. -> **_NOTE:_** Please implement double opt-in mechanism for user registration to ensure users's are paired safely between -> XbyK and KX 13. In current Dancing Goat example, we dont't have double opt-in mechanism implemented, but we recommend it as best practice. +> **_NOTE:_** Please implement double opt-in mechanism for user registration to ensure the users are paired safely between +> XbyK and KX 13. The current Dancing Goat example does not have a double opt-in mechanism implemented, but we recommend it as a best practice. #### Current known limitations -User's roles synchronization isn't currently supported. We assume users to be already synchronized between client (XbyK) and KX app before starting using this API. +Roles synchronization isn't currently supported. We assume website members to be already synchronized between client (XbyK) and KX app before starting using this API. ### Setup @@ -112,7 +114,7 @@ when your KX 13 live site is not running) dotnet add package Kentico.Xperience.StoreApi ``` -1. Set up your own [settings](..\examples\Kentico13_DancingGoatStore\appsettings.json) for Store REST API authentication (based on JWT and OAuth client credentials flow) +1. Set up your own [settings](../examples/Kentico13_DancingGoatStore/appsettings.json) for Store REST API authentication (based on JWT and OAuth client credentials flow) ```json { "CMSStoreApi": { @@ -217,11 +219,14 @@ and to browser cookie (uses `IShoppingCartClientStorage`) where customer's addresses are retrieved in cart's second step. - `IOrderService` - List of orders - currently suitable for implementing listing orders in administration + - **Order updates and listing for specific customers are under development** - `ISiteStoreService` - Use for retrieving site's [list of enabled cultures](https://github.com/Kentico/xperience-by-kentico-ecommerce/blob/main/src/Kentico.Xperience.K13Ecommerce/SiteStore/ISiteStoreService.cs#L13), e.g. for implementation of language selector - Use for retrieving site's [list of enabled currencies](https://github.com/Kentico/xperience-by-kentico-ecommerce/blob/main/src/Kentico.Xperience.K13Ecommerce/SiteStore/ISiteStoreService.cs#L18), e.g. for implementation of currency selector - `ICountryService` - [Countries and states](../src/Kentico.Xperience.K13Ecommerce/Countries/ICountryService.cs) - these objects are already on XByK, there is no Store API call + > **_NOTE:_** Countries and states are not synchronized between KX 13 and XbyK. As a result, any modifications or + additions to countries and states in KX 13 are currently not supported. ### Products synchronization @@ -233,19 +238,19 @@ Library also implements product synchronization to Content hub. These are 3 enti - Product images - Content type `K13Store.ProductImage` - Main SKU images (from SKUImagePath column) -Synchronization running in background thread worker periodically and can be disabled (`ProductSyncEnabled` setting). -Interval can be set in minutes (`ProductSyncInterval` setting). Synchronized data are updated when source value +The synchronization runs in a background thread worker periodically and can be disabled (`ProductSyncEnabled` setting). +Interval can be set in minutes (`ProductSyncInterval` setting). Synchronized data is updated when source value changes, so data cannot be edited in XbyK safely, but new custom or reusable fields can be added and edited safely. -No price data are synced, because catalog prices need +No price data is synced, because catalog prices need calculator evaluation in context of user's cart and standalone requests via `IProductService` are required. #### Limitations Products are currently synchronized only in default content culture. **Same language needs to be enabled in XByK**. ### Activity logging -When you are using `IShoppingService` for shopping cart actions, these actions are logged to XByK [Online marketing activities](https://docs.kentico.com/developers-and-admins/digital-marketing-setup/set-up-activities) +When you are using `IShoppingService` for shopping cart actions, these actions are logged to XByK [Online marketing activities](https://docs.kentico.com/x/dY3WCQ) for current contact: | Activity display name | Activity name | Description | @@ -255,13 +260,13 @@ for current contact: | Purchased product | custom_purchasedproduct | Purchased product (after order is created) | | Purchase | custom_purchase | Order created | -You need to ensure these [custom activity types](https://docs.kentico.com/developers-and-admins/digital-marketing-setup/set-up-activities/custom-activities) +You need to ensure these [custom activity types](https://docs.kentico.com/x/xoouCw) are created (via CI restore - see [Setup section](#setup-1) or [manually](https://docs.kentico.com/developers-and-admins/digital-marketing-setup/set-up-activities/custom-activities#add-custom-activity-types)). ### Email notifications Currently all e-commerce email notifications are sent from KX 13 application. -You need to have [configured email sending](https://docs.kentico.com/13/configuring-xperience/configuring-smtp-servers) and -[e-commerce email templates](https://docs.kentico.com/13/e-commerce-features/configuring-on-line-stores/configuring-e-commerce-email-notifications). +You need to have [configured email sending](https://docs.kentico.com/x/IQ_RBg) and +[e-commerce email templates](https://docs.kentico.com/x/-wuRBg). ### Product listing widget - We recommend to use this widget for simple scenarios such as Landing page offers, etc. @@ -341,15 +346,15 @@ dotnet run --kxp-ci-restore ``` All content types and custom activities for e-ecommerce events are created. -Except reusable content types used in product synchronization, additional page types are restored: +Except reusable content types used in product synchronization, additional content types for pages are restored: -These page types are restored for Store page, categories and product detail pages: +These content types are restored for Store page, categories and product detail pages: - `K13Store.StorePage` - Main store page -- `K13Store.CategoryPage` - Page type for categories linking products -- `K13Store.ProductPage` - Page type for product detail page - only linking product SKU from content hub +- `K13Store.CategoryPage` - Content type for category pages (with linking product pages) +- `K13Store.ProductPage` - Content type for product detail page - only linking product SKU from content hub -For checkout process these page types are restored: +For checkout process these content types (for pages) are restored: - `K13Store.CartContent` - used for the shopping cart first step - `K13Store.CartDeliveryDetails`- used for the shopping cart second step - `K13Store.CartSummary` - used for the shopping cart third step @@ -376,11 +381,11 @@ The product synchronization creates reusable content items for products, product It's on you how to display these product on your website. But you can use the approach from [Dancing Goat example](#dancing-goat-example---setup): 1. Create pages for products (e.g. in folder structure) in your web site channel and link them to product content items -(of type `K13Store.ProductSKU`). You can use `K13Store.ProductPage` page type for this. +(of type `K13Store.ProductSKU`). You can use `K13Store.ProductPage` content type for this. ![Link product pages](../images/screenshots/linking_products_webchannel.png "Link product pages") -2. Create Store page (use `K13Store.StorePage` page type) which represents entry point for your store. You can display here main categories +2. Create Store page (use `K13Store.StorePage` content type) which represents entry point for your store. You can display here main categories and Hot tip products. Skip this step when you don't need this type of page. -3. Create pages for categories (use `K13Store.CategoryPage` page type) and select product pages in Products in category field. +3. Create pages for categories (use `K13Store.CategoryPage` content type) and select product pages in Products in category field. ![Products in category](../images/screenshots/category_products.png "Products in category") ### How to display products on your website? @@ -398,7 +403,7 @@ on Dancing Goat example site. Products pages are retrieved for current category 4. Order complete page\ Set Cart next steps / Cart previous step fields for each step page. ![Cart steps](../images/screenshots/cart_steps.png "Cart steps") - This approach has the advantage that you can use [page builder features](https://docs.kentico.com/developers-and-admins/development/builders/page-builder) for each step. + This approach has the advantage that you can use [page builder features](https://docs.kentico.com/x/6QWiCQ) for each step. 2. For shopping cart and checkout process implementation, see [CheckoutController](../examples/DancingGoat-K13Ecommerce/Controllers/KStore/CheckoutController.cs) Here are links for some specific parts of shopping cart: diff --git a/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs b/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs index 9afa650..de833ce 100644 --- a/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs +++ b/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs @@ -28,6 +28,6 @@ public async Task TestSetCurrency(string currencyCode) } public async Task TestOrders([FromServices] IOrderService orderService) - => Json(await orderService.GetOrderList(new OrderListRequest { Page = 1, PageSize = 10, OrderBy = "OrderID DESC" })); + => Json(await orderService.GetCurrentCustomerOrderList(new OrderListRequest { Page = 1, PageSize = 10, OrderBy = "OrderID DESC" })); } #endif diff --git a/examples/DancingGoat-K13Ecommerce/Services/CheckoutService.cs b/examples/DancingGoat-K13Ecommerce/Services/CheckoutService.cs index 99f5a22..bd083e8 100644 --- a/examples/DancingGoat-K13Ecommerce/Services/CheckoutService.cs +++ b/examples/DancingGoat-K13Ecommerce/Services/CheckoutService.cs @@ -65,7 +65,7 @@ public async Task PrepareDeliveryDetailsViewModel(Cust customer ??= new CustomerViewModel(await shoppingService.GetCustomerOrCreateFromAuthenticatedUser(cartDetails.Customer)); var addresses = (cartDetails.Customer != null) - ? await customerService.GetCustomerAddresses(cartDetails.Customer.CustomerId) + ? await customerService.GetCurrentCustomerAddresses() : Enumerable.Empty(); var billingAddresses = new SelectList(addresses, nameof(KAddress.AddressId), nameof(KAddress.AddressName)); @@ -114,7 +114,7 @@ public async Task IsStateValid(int countryId, int? stateId) public async Task GetAddress(int customerId, int addressId) => customerId > 0 && addressId > 0 - ? (await customerService.GetCustomerAddresses(customerId)).FirstOrDefault(a => a.AddressId == addressId) + ? (await customerService.GetCurrentCustomerAddresses()).FirstOrDefault(a => a.AddressId == addressId) : null; diff --git a/images/kentico_ecommerce_diagram.svg b/images/kentico_ecommerce_diagram.svg new file mode 100644 index 0000000..f30c07b --- /dev/null +++ b/images/kentico_ecommerce_diagram.svg @@ -0,0 +1,4 @@ + + + +

KX13 
DB

KX13...
Kentico Xperience 13 
Administration
Kentico Xperience 13...
Kentico Xperience 13
live site application
Kentico Xperience 13...
Store configuration and management
Store configuration and management
Store API
(REST)
Store API...
Xperience By Kentico
application
Xperience By Kentico...
Kentico.Xperience.K13Ecommerce
Kentico.Xperience.K13Ecommerce
Kentico.Xperience.Store.Rcl
Kentico.Xperience.Store.Rcl
Visitor / Customer
Visit...
Kentico.Xperience.StoreApi
Kentico.Xperience.StoreApi
\ No newline at end of file diff --git a/images/project_diagram.png b/images/project_diagram.png deleted file mode 100644 index 1a6db59..0000000 Binary files a/images/project_diagram.png and /dev/null differ diff --git a/src/Kentico.Xperience.K13Ecommerce/Customers/CustomerService.cs b/src/Kentico.Xperience.K13Ecommerce/Customers/CustomerService.cs index 725a388..1674f1d 100644 --- a/src/Kentico.Xperience.K13Ecommerce/Customers/CustomerService.cs +++ b/src/Kentico.Xperience.K13Ecommerce/Customers/CustomerService.cs @@ -5,6 +5,11 @@ namespace Kentico.Xperience.K13Ecommerce.Customers; internal class CustomerService(IKenticoStoreApiClient apiClient) : ICustomerService { /// - public async Task> GetCustomerAddresses(int customerId) => - await apiClient.CustomerAddressesAsync(customerId); + public async Task> GetCurrentCustomerAddresses() => + await apiClient.CurrentCustomerAddressesAsync(); + + + /// + public async Task> GetAdminCustomerAddresses(int customerId) => + await apiClient.AdminCustomerAddressesAsync(customerId); } diff --git a/src/Kentico.Xperience.K13Ecommerce/Customers/ICustomerService.cs b/src/Kentico.Xperience.K13Ecommerce/Customers/ICustomerService.cs index 2b4ea63..8c857c8 100644 --- a/src/Kentico.Xperience.K13Ecommerce/Customers/ICustomerService.cs +++ b/src/Kentico.Xperience.K13Ecommerce/Customers/ICustomerService.cs @@ -8,9 +8,14 @@ namespace Kentico.Xperience.K13Ecommerce.Customers; public interface ICustomerService { /// - /// Get list of addresses for given customer. + /// Get list of addresses for current customer. + /// + Task> GetCurrentCustomerAddresses(); + + + /// + /// Get list of addresses for customer with specified ID to display in XByK administration. /// - /// Customer ID. - /// - Task> GetCustomerAddresses(int customerId); + /// Customer ID. + Task> GetAdminCustomerAddresses(int customerId); } diff --git a/src/Kentico.Xperience.K13Ecommerce/Orders/IOrderService.cs b/src/Kentico.Xperience.K13Ecommerce/Orders/IOrderService.cs index 8f2ed85..fd39794 100644 --- a/src/Kentico.Xperience.K13Ecommerce/Orders/IOrderService.cs +++ b/src/Kentico.Xperience.K13Ecommerce/Orders/IOrderService.cs @@ -8,9 +8,17 @@ namespace Kentico.Xperience.K13Ecommerce.Orders; public interface IOrderService { /// - /// Get orders based on parameters. + /// Get orders based on parameters for current customer. /// /// Request parameters for order listing. /// Paged list of orders. - Task GetOrderList(OrderListRequest request); + Task GetCurrentCustomerOrderList(OrderListRequest request); + + + /// + /// Get orders based on parameters to display in XbyK administration (for all customers). + /// + /// Request parameters for order listing. + /// Paged list of orders. + Task GetAdminOrderList(OrderListRequest request); } diff --git a/src/Kentico.Xperience.K13Ecommerce/Orders/OrderService.cs b/src/Kentico.Xperience.K13Ecommerce/Orders/OrderService.cs index daf1786..4680615 100644 --- a/src/Kentico.Xperience.K13Ecommerce/Orders/OrderService.cs +++ b/src/Kentico.Xperience.K13Ecommerce/Orders/OrderService.cs @@ -5,6 +5,11 @@ namespace Kentico.Xperience.K13Ecommerce.Orders; internal class OrderService(IKenticoStoreApiClient storeApiClient) : IOrderService { /// - public async Task GetOrderList(OrderListRequest request) - => await storeApiClient.OrderListAsync(request.Page, request.PageSize, request.OrderBy); + public async Task GetCurrentCustomerOrderList(OrderListRequest request) + => await storeApiClient.CurrentCustomerOrderListAsync(request.Page, request.PageSize, request.OrderBy); + + + /// + public async Task GetAdminOrderList(OrderListRequest request) + => await storeApiClient.AdminOrderListAsync(request.Page, request.PageSize, request.OrderBy); } diff --git a/src/Kentico.Xperience.K13Ecommerce/StoreApi/swagger.json b/src/Kentico.Xperience.K13Ecommerce/StoreApi/swagger.json index 008536d..ba166ab 100644 --- a/src/Kentico.Xperience.K13Ecommerce/StoreApi/swagger.json +++ b/src/Kentico.Xperience.K13Ecommerce/StoreApi/swagger.json @@ -136,13 +136,47 @@ "tags": [ "Customer" ], - "summary": "Endpoint for customer addresses.", - "operationId": "CustomerAddresses", + "summary": "Endpoint for current customer addresses.", + "operationId": "CurrentCustomerAddresses", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KAddress" + } + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/api/store/customer/admin/addresses": { + "get": { + "tags": [ + "Customer" + ], + "summary": "Endpoint for given customer addresses to display in XbyK administration.", + "operationId": "AdminCustomerAddresses", "parameters": [ { "name": "customerId", "in": "query", - "description": "", + "description": "Customer ID", "required": true, "schema": { "type": "integer", @@ -182,8 +216,66 @@ "tags": [ "Order" ], - "summary": "Endpoint for getting list of orders based on request.", - "operationId": "OrderList", + "summary": "Endpoint for getting list of current customer's orders based on request.", + "operationId": "CurrentCustomerOrderList", + "parameters": [ + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "format": "int32" + } + }, + { + "name": "OrderBy", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrderListResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/api/store/order/admin/list": { + "get": { + "tags": [ + "Order" + ], + "summary": "Endpoint for getting list of orders based on request to display in XbyK administration.", + "operationId": "AdminOrderList", "parameters": [ { "name": "Page", diff --git a/src/Kentico.Xperience.StoreApi/Customers/CustomerController.cs b/src/Kentico.Xperience.StoreApi/Customers/CustomerController.cs index 214be36..2880b84 100644 --- a/src/Kentico.Xperience.StoreApi/Customers/CustomerController.cs +++ b/src/Kentico.Xperience.StoreApi/Customers/CustomerController.cs @@ -23,20 +23,39 @@ public class CustomerController : ControllerBase { private readonly IAddressInfoProvider addressInfoProvider; private readonly IMapper mapper; + private readonly IShoppingService shoppingService; - public CustomerController(IAddressInfoProvider addressInfoProvider, IMapper mapper) + public CustomerController(IAddressInfoProvider addressInfoProvider, IMapper mapper, IShoppingService shoppingService) { this.addressInfoProvider = addressInfoProvider; this.mapper = mapper; + this.shoppingService = shoppingService; } /// - /// Endpoint for customer addresses. + /// Endpoint for current customer addresses. /// - /// /// - [HttpGet("addresses", Name = nameof(CustomerAddresses))] - public ActionResult> CustomerAddresses([FromQuery][Required] int customerId) + [HttpGet("addresses", Name = nameof(CurrentCustomerAddresses))] + public ActionResult> CurrentCustomerAddresses() + { + var cart = shoppingService.GetCurrentShoppingCart(); + if (cart.Customer is null) + { + return Ok(Enumerable.Empty()); + } + var addresses = mapper.Map>(addressInfoProvider.GetByCustomer(cart.Customer.CustomerID)); + return Ok(addresses); + } + + + /// + /// Endpoint for given customer addresses to display in XbyK administration. + /// + /// Customer ID + /// + [HttpGet("admin/addresses", Name = nameof(AdminCustomerAddresses))] + public ActionResult> AdminCustomerAddresses([FromQuery][Required] int customerId) { var addresses = mapper.Map>(addressInfoProvider.GetByCustomer(customerId)); return Ok(addresses); diff --git a/src/Kentico.Xperience.StoreApi/Orders/OrderController.cs b/src/Kentico.Xperience.StoreApi/Orders/OrderController.cs index 138796b..2a0b78d 100644 --- a/src/Kentico.Xperience.StoreApi/Orders/OrderController.cs +++ b/src/Kentico.Xperience.StoreApi/Orders/OrderController.cs @@ -24,21 +24,69 @@ public class OrderController : ControllerBase private readonly IOrderInfoProvider orderInfoProvider; private readonly IMapper mapper; private readonly ISiteService siteService; + private readonly IShoppingService shoppingService; - public OrderController(IOrderInfoProvider orderInfoProvider, IMapper mapper, ISiteService siteService) + public OrderController( + IOrderInfoProvider orderInfoProvider, + IMapper mapper, + ISiteService siteService, + IShoppingService shoppingService) { this.orderInfoProvider = orderInfoProvider; this.mapper = mapper; this.siteService = siteService; + this.shoppingService = shoppingService; } /// - /// Endpoint for getting list of orders based on request. + /// Endpoint for getting list of current customer's orders based on request. /// /// /// - [HttpGet("list", Name = nameof(OrderList))] - public async Task> OrderList([FromQuery] OrderListRequest request) + [HttpGet("list", Name = nameof(CurrentCustomerOrderList))] + public async Task> CurrentCustomerOrderList([FromQuery] OrderListRequest request) + { + int page = request.Page > 0 ? request.Page - 1 : 0; + if (string.IsNullOrWhiteSpace(request.OrderBy)) + { + request.OrderBy = $"{nameof(OrderInfo.OrderDate)} DESC"; + } + + var cart = shoppingService.GetCurrentShoppingCart(); + if (cart.Customer is null) + { + return Ok(new OrderListResponse + { + Orders = Enumerable.Empty(), + MaxPage = 1, + Page = 1 + }); + } + + var orderQuery = orderInfoProvider.Get() + .WhereEquals(nameof(OrderInfo.OrderCustomerID), cart.Customer.CustomerID) + .OnSite(siteService.CurrentSite.SiteID) + .Page(page, request.PageSize) + .OrderBy(request.OrderBy); + + var orders = mapper.Map>(await orderQuery.GetEnumerableTypedResultAsync()); + + return Ok(new OrderListResponse + { + Orders = orders, + Page = page + 1, + MaxPage = (orderQuery.TotalRecords / request.PageSize) + 1 + }); + } + + + /// + /// Endpoint for getting list of orders based on request to display in XbyK administration. + /// + /// + /// + [HttpGet("admin/list", Name = nameof(AdminOrderList))] + public async Task> AdminOrderList([FromQuery] OrderListRequest request) { int page = request.Page > 0 ? request.Page - 1 : 0; if (string.IsNullOrWhiteSpace(request.OrderBy))