From 16afca805824011227e701627f1a76dd2e4b1256 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Wed, 3 Jun 2020 10:28:37 +0200 Subject: [PATCH 01/61] Create GreenField Api Development Docs --- docs/greenfield-development.md | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/greenfield-development.md diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md new file mode 100644 index 000000000..57e9e79e7 --- /dev/null +++ b/docs/greenfield-development.md @@ -0,0 +1,42 @@ +# GreenField API Development Documentation + + +## Updating existing API endpoints + +### Scenario 1: Changing a property type on the model +Changing a property on a model is a breaking change unless the server starts handling both versions. + +#### Solutions +* Bump the version of the endpoint. + +#### Alternatives considered +* Create a `JsonConverter` that allows conversion between the original type and the new type. However, if this option is used, you will need to ensure that the response model returns the same format. In the case of the `GET` endpoint, you will break clients expecting the original type. + +### Scenario 2: Removing a property on the model +Removing a property on a model is a breaking change. + +#### Solutions +* Bump the version of the endpoint. + +#### Alternatives considered +* Create a default value (one that is not useful) to be sent back in the model. Ignore the property being sent on the model to the server. + +### Scenario 3: Adding a property on the model +Adding a property on a model can potentially be a breaking change. It is a breaking change if: +* the property is required +* the property has no default value + +#### Solutions +* Check if the payload has the property present. If not, either set to the default value (in the case of a`POST`) or set to the model's current value. See [Detecting missing properties in a JSON model](#missing-properties-detect) for how to achieve this. + +#### Alternatives considered +* Bump the version of the endpoint +* Assume the property is always sent and let the value be set to the default if not ( in the case of nullable types, this may be problematic when calling update endpoints). +* Use [`[JsonExtensionData]AdditionalData`](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonExtensionDataAttribute.htm) so that clients receive the full payload even after updating only the server. This is problematic as it only fixes clients which implement this opinionated flow (this is not a standard or common way of doing API calls) . + +## Technical specifics + +### Detecting missing properties in a JSON model. +Possible solutions: +* Read the raw JSON object in the controller action and and search for the lack of a specific property. +* Use [`JSON.NET Serialization Callabacks`](https://www.newtonsoft.com/json/help/html/SerializationCallbacks.htm) to set a `List MissingProperties;` variable From 6af3b4a51d2e72e58e270ae9a9bda7a3370fb962 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Wed, 3 Jun 2020 11:12:26 +0200 Subject: [PATCH 02/61] Update greenfield-development.md --- docs/greenfield-development.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index 57e9e79e7..894d5052d 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -1,5 +1,18 @@ # GreenField API Development Documentation +## Adding new API endpoints +* Always document all endpoints and model schemas in swagger. OpenAPI 3.0 is used as a specification, in JSON formatting, and is written manually. The specification is split to a file per controller and then merged by the server through a controller action at `/swagger/v1/swagger.json`. +* All `JsonConverter` usage should be registered through attributes within the model itself. +* `decimal` and `long` and other similar types should be serialized to a string and able to deserialize from the original type and a string. +* Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. +* Use HTTP methods according to REST principles when possible. This means: + * `POST` - Create or custom action + * `PUT` - Update full model + * `PATCH` - Update partially + * `DELETE` - Delete or Archive +* When returning an error response, we should differentiate from 2 possible scenarios: + * Model validation - an error on the request was found - [Status Code 422](https://httpstatuses.com/422) with the model [ValidationProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.validationproblemdetails?view=aspnetcore-3.1). + * Generic request error - an error resulting from the business logic unable to handle the specified request - [Status Code 400](https://httpstatuses.com/400) with the model [ProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-3.1). ## Updating existing API endpoints @@ -23,20 +36,22 @@ Removing a property on a model is a breaking change. ### Scenario 3: Adding a property on the model Adding a property on a model can potentially be a breaking change. It is a breaking change if: -* the property is required -* the property has no default value +* the property is required. +* the property has no default value. #### Solutions * Check if the payload has the property present. If not, either set to the default value (in the case of a`POST`) or set to the model's current value. See [Detecting missing properties in a JSON model](#missing-properties-detect) for how to achieve this. #### Alternatives considered -* Bump the version of the endpoint +* Bump the version of the endpoint. * Assume the property is always sent and let the value be set to the default if not ( in the case of nullable types, this may be problematic when calling update endpoints). * Use [`[JsonExtensionData]AdditionalData`](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonExtensionDataAttribute.htm) so that clients receive the full payload even after updating only the server. This is problematic as it only fixes clients which implement this opinionated flow (this is not a standard or common way of doing API calls) . + + ## Technical specifics ### Detecting missing properties in a JSON model. Possible solutions: * Read the raw JSON object in the controller action and and search for the lack of a specific property. -* Use [`JSON.NET Serialization Callabacks`](https://www.newtonsoft.com/json/help/html/SerializationCallbacks.htm) to set a `List MissingProperties;` variable +* Use [`JSON.NET Serialization Callabacks`](https://www.newtonsoft.com/json/help/html/SerializationCallbacks.htm) to set a `List MissingProperties;` variable. From 34239dc3839187853f4d7b7f0f55950828a308d9 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Thu, 4 Jun 2020 12:03:56 +0200 Subject: [PATCH 03/61] Update docs/greenfield-development.md Co-authored-by: Dennis Reimann --- docs/greenfield-development.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index 894d5052d..444c4831d 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -4,7 +4,7 @@ * Always document all endpoints and model schemas in swagger. OpenAPI 3.0 is used as a specification, in JSON formatting, and is written manually. The specification is split to a file per controller and then merged by the server through a controller action at `/swagger/v1/swagger.json`. * All `JsonConverter` usage should be registered through attributes within the model itself. * `decimal` and `long` and other similar types should be serialized to a string and able to deserialize from the original type and a string. -* Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. +* Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. * Use HTTP methods according to REST principles when possible. This means: * `POST` - Create or custom action * `PUT` - Update full model From 6c828a29ec1066693dd94517ab59531f25d46bde Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Fri, 12 Jun 2020 14:10:43 +0200 Subject: [PATCH 04/61] Update greenfield-development.md --- docs/greenfield-development.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index 444c4831d..dde987916 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -1,3 +1,4 @@ + # GreenField API Development Documentation ## Adding new API endpoints @@ -11,8 +12,22 @@ * `PATCH` - Update partially * `DELETE` - Delete or Archive * When returning an error response, we should differentiate from 2 possible scenarios: - * Model validation - an error on the request was found - [Status Code 422](https://httpstatuses.com/422) with the model [ValidationProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.validationproblemdetails?view=aspnetcore-3.1). - * Generic request error - an error resulting from the business logic unable to handle the specified request - [Status Code 400](https://httpstatuses.com/400) with the model [ProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-3.1). + * Model validation - an error or errors on the request was found - [Status Code 422](https://httpstatuses.com/422) with the model: + ```json + [ + { + "path": "prop-name", + "message": "human readable message" + } + ] + ``` + * Generic request error - an error resulting from the business logic unable to handle the specified request - [Status Code 400](https://httpstatuses.com/400) with the model: + ```json + { + "code": "unique-error-code", + "message":"a human readable message" + } + ``` ## Updating existing API endpoints From 6729827645d401e129276385a0aacd0f08880979 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Mon, 15 Jun 2020 12:45:05 +0200 Subject: [PATCH 05/61] Update greenfield-development.md --- docs/greenfield-development.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index dde987916..51e291b0b 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -4,7 +4,7 @@ * Always document all endpoints and model schemas in swagger. OpenAPI 3.0 is used as a specification, in JSON formatting, and is written manually. The specification is split to a file per controller and then merged by the server through a controller action at `/swagger/v1/swagger.json`. * All `JsonConverter` usage should be registered through attributes within the model itself. -* `decimal` and `long` and other similar types should be serialized to a string and able to deserialize from the original type and a string. +* `decimal` and `long` and other similar types, if there is a need for decimal precision or has the possibility of an overflow issue, should be serialized to a string and able to deserialize from the original type and a string. * Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. * Use HTTP methods according to REST principles when possible. This means: * `POST` - Create or custom action From 565dbd88ff0d03e286970abf76704887c0d0ff32 Mon Sep 17 00:00:00 2001 From: Kukks Date: Wed, 19 Aug 2020 13:21:05 +0200 Subject: [PATCH 06/61] fix notification page issue fixes #1820 --- BTCPayServer/Controllers/NotificationsController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BTCPayServer/Controllers/NotificationsController.cs b/BTCPayServer/Controllers/NotificationsController.cs index b8c01f687..92e6b2a39 100644 --- a/BTCPayServer/Controllers/NotificationsController.cs +++ b/BTCPayServer/Controllers/NotificationsController.cs @@ -99,9 +99,9 @@ namespace BTCPayServer.Controllers Skip = skip, Count = count, Items = _db.Notifications + .Where(a => a.ApplicationUserId == userId) .OrderByDescending(a => a.Created) .Skip(skip).Take(count) - .Where(a => a.ApplicationUserId == userId) .Select(a => _notificationManager.ToViewModel(a)) .ToList(), Total = _db.Notifications.Count(a => a.ApplicationUserId == userId) From 68686cd2494d55dbd90930bb48599e79a8b83fc1 Mon Sep 17 00:00:00 2001 From: Kukks Date: Wed, 19 Aug 2020 13:31:28 +0200 Subject: [PATCH 07/61] Add debug notifications This adds a test notif only available in debug mode + endpoint `/Notifications/GenerateJunk` that generates x amount of notifications. --- .../Controllers/NotificationsController.cs | 11 +++++++++++ BTCPayServer/Hosting/BTCPayServerServices.cs | 5 ++++- .../Notifications/Blobs/JunkNotification.cs | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 BTCPayServer/Services/Notifications/Blobs/JunkNotification.cs diff --git a/BTCPayServer/Controllers/NotificationsController.cs b/BTCPayServer/Controllers/NotificationsController.cs index b8c01f687..8b9c40c17 100644 --- a/BTCPayServer/Controllers/NotificationsController.cs +++ b/BTCPayServer/Controllers/NotificationsController.cs @@ -87,7 +87,18 @@ namespace BTCPayServer.Controllers return new EmptyResult(); } +#if DEBUG + [HttpGet] + public async Task GenerateJunk(int x = 100) + { + for (int i = 0; i < x; i++) + { + await _notificationSender.SendNotification(new AdminScope(), new JunkNotification()); + } + return RedirectToAction("Index"); + } +#endif [HttpGet] public IActionResult Index(int skip = 0, int count = 50, int timezoneOffset = 0) { diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index 1cadb52b0..04fb29eeb 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -246,7 +246,10 @@ namespace BTCPayServer.Hosting services.AddSingleton(); services.AddSingleton(); - + +#if DEBUG + services.AddSingleton(); +#endif services.TryAddSingleton(); services.TryAddSingleton(o => { diff --git a/BTCPayServer/Services/Notifications/Blobs/JunkNotification.cs b/BTCPayServer/Services/Notifications/Blobs/JunkNotification.cs new file mode 100644 index 000000000..eeecb3df4 --- /dev/null +++ b/BTCPayServer/Services/Notifications/Blobs/JunkNotification.cs @@ -0,0 +1,19 @@ +#if DEBUG +using BTCPayServer.Models.NotificationViewModels; + +namespace BTCPayServer.Services.Notifications.Blobs +{ + internal class JunkNotification + { + internal class Handler : NotificationHandler + { + public override string NotificationType => "junk"; + + protected override void FillViewModel(JunkNotification notification, NotificationViewModel vm) + { + vm.Body = $"All your junk r belong to us!"; + } + } + } +} +#endif From a249a164f73ae18a2acae5fa2ca3442662690058 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Thu, 20 Aug 2020 13:15:25 +0900 Subject: [PATCH 08/61] Fix HitBTC again --- BTCPayServer.Rating/Providers/HitBTCRateProvider.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/BTCPayServer.Rating/Providers/HitBTCRateProvider.cs b/BTCPayServer.Rating/Providers/HitBTCRateProvider.cs index deaa90a0c..1a98e96b5 100644 --- a/BTCPayServer.Rating/Providers/HitBTCRateProvider.cs +++ b/BTCPayServer.Rating/Providers/HitBTCRateProvider.cs @@ -24,13 +24,21 @@ namespace BTCPayServer.Rating var jarray = await response.Content.ReadAsAsync(cancellationToken); return jarray .Children() - .Where(p => CurrencyPair.TryParse(p["symbol"].Value(), out _)) - .Select(p => new PairRate(CurrencyPair.Parse(p["symbol"].Value()), CreateBidAsk(p))) + .Select(p => + { + CurrencyPair.TryParse(p["symbol"].Value(), out var currency); + var bidask = CreateBidAsk(p); + return (currency, bidask); + }) + .Where(p => p.currency != null && p.bidask != null) + .Select(p => new PairRate(p.currency, p.bidask)) .ToArray(); } private BidAsk CreateBidAsk(JObject p) { + if (p["bid"].Type != JTokenType.String || p["ask"].Type != JTokenType.String) + return null; var bid = p["bid"].Value(); var ask = p["ask"].Value(); return new BidAsk(bid, ask); From e7ea8ac40f5e364764dc927f904a19d076279c32 Mon Sep 17 00:00:00 2001 From: Dennis Reimann Date: Mon, 24 Aug 2020 06:57:07 +0200 Subject: [PATCH 09/61] Improve invoices list view (#1815) * Improve invoices list view * Pager: Only show options that make sense * ListInvoices formatting * Add separator for dropdown toggle split * Minor ListInvoices improvement * Improve payment requests list view * Distinguish empty and filtered lists * Properly align invoice details * Add payment symbols to invoices list * Improve payment symbols in invoices list * Always display search on list pages * Inline variable * Move display logic to pager https://github.com/btcpayserver/btcpayserver/commit/e5040ede5535f5691fd5a0358be2307fd9d4e1c7#commitcomment-41698272 --- BTCPayServer/Components/Pager/Default.cshtml | 57 +- .../Controllers/InvoiceController.UI.cs | 1 - .../Models/InvoicingModels/InvoicesModel.cs | 1 - .../Views/Invoice/ListInvoices.cshtml | 584 +++++++++--------- .../PaymentRequest/GetPaymentRequests.cshtml | 142 +++-- BTCPayServer/wwwroot/main/site.css | 5 + 6 files changed, 428 insertions(+), 362 deletions(-) diff --git a/BTCPayServer/Components/Pager/Default.cshtml b/BTCPayServer/Components/Pager/Default.cshtml index 2234f55cf..a9e0fc4c7 100644 --- a/BTCPayServer/Components/Pager/Default.cshtml +++ b/BTCPayServer/Components/Pager/Default.cshtml @@ -1,8 +1,8 @@ @model BasePagingViewModel - +} @{ string NavigatePages(int prevNext, int count) { diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs index 2a8ae27da..4afb92f57 100644 --- a/BTCPayServer/Controllers/InvoiceController.UI.cs +++ b/BTCPayServer/Controllers/InvoiceController.UI.cs @@ -623,7 +623,6 @@ namespace BTCPayServer.Controllers model.Invoices.Add(new InvoiceModel() { Status = invoice.Status, - StatusString = state.ToString(), ShowCheckout = invoice.Status == InvoiceStatus.New, Date = invoice.InvoiceTime, InvoiceId = invoice.Id, diff --git a/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs b/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs index ef2d03fd2..1e109a706 100644 --- a/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs +++ b/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs @@ -19,7 +19,6 @@ namespace BTCPayServer.Models.InvoicingModels public string InvoiceId { get; set; } public InvoiceStatus Status { get; set; } - public string StatusString { get; set; } public bool CanMarkComplete { get; set; } public bool CanMarkInvalid { get; set; } public bool CanMarkStatus => CanMarkComplete || CanMarkInvalid; diff --git a/BTCPayServer/Views/Invoice/ListInvoices.cshtml b/BTCPayServer/Views/Invoice/ListInvoices.cshtml index 81bb2b967..cf6cf7815 100644 --- a/BTCPayServer/Views/Invoice/ListInvoices.cshtml +++ b/BTCPayServer/Views/Invoice/ListInvoices.cshtml @@ -1,322 +1,351 @@ +@using BTCPayServer.Payments @model InvoicesModel @{ ViewData["Title"] = "Invoices"; + var storeIds = string.Join("", Model.StoreIds.Select(storeId => $",storeid:{storeId}")); } @section HeadScripts { } - @Html.HiddenFor(a => a.Count)
@if (TempData.HasStatusMessage()) { -
-
- +
+
+ +
-
}

@ViewData["Title"]


-

Create, search or pay an invoice. (Help)

-
-

- You can search for invoice Id, deposit address, price, order id, store id, any buyer information and any product information.
- Be sure to split your search parameters with comma, for example: startdate:2019-04-25 13:00:00, status:paid
- You can also apply filters to your search by searching for filtername:value, here is a list of supported filters -

-
    -
  • storeid:id for filtering a specific store
  • -
  • orderid:id for filtering a specific order
  • -
  • itemcode:code for filtering a specific type of item purchased through the pos or crowdfund apps
  • -
  • status:(expired|invalid|complete|confirmed|paid|new) for filtering a specific status
  • -
  • exceptionstatus:(paidover|paidlate|paidpartial) for filtering a specific exception state
  • -
  • unusual:(true|false) for filtering invoices which might requires merchant attention (those invalid or with an exceptionstatus)
  • -
  • startdate:yyyy-MM-dd HH:mm:ss getting invoices that were created after certain date
  • -
  • enddate:yyyy-MM-dd HH:mm:ss getting invoices that were created before certain date
  • -
-

- If you want all confirmed and complete invoices, you can duplicate a filter status:confirmed, status:complete. -

-
+

Create, search or pay an invoice.

-
- -
- - -
- - - - @{ - var storeIds = string.Join( - "", - Model.StoreIds.Select(storeId => $",storeid:{storeId}") - ); - } - - -
+
+ - - - -
-
-
- Create a new invoice - - - -