mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 14:04:26 +01:00
Add basic Greenfield API Get and Delete operations for apps (#3894)
* Add basic Greenfield API Get and Delete operations for apps Will follow-up with PATCH and also with GET which returns more than just basic data later. This sets up the basic stuff first. * Add methods to LocalBTCPayServerClient
This commit is contained in:
@@ -19,5 +19,25 @@ namespace BTCPayServer.Client
|
|||||||
method: HttpMethod.Post), token);
|
method: HttpMethod.Post), token);
|
||||||
return await HandleResponse<PointOfSaleAppData>(response);
|
return await HandleResponse<PointOfSaleAppData>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual async Task<AppDataBase> GetApp(string appId, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (appId == null)
|
||||||
|
throw new ArgumentNullException(nameof(appId));
|
||||||
|
var response = await _httpClient.SendAsync(
|
||||||
|
CreateHttpRequest($"api/v1/apps/{appId}",
|
||||||
|
method: HttpMethod.Get), token);
|
||||||
|
return await HandleResponse<AppDataBase>(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task DeleteApp(string appId, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (appId == null)
|
||||||
|
throw new ArgumentNullException(nameof(appId));
|
||||||
|
var response = await _httpClient.SendAsync(
|
||||||
|
CreateHttpRequest($"api/v1/apps/{appId}",
|
||||||
|
method: HttpMethod.Delete), token);
|
||||||
|
await HandleResponse(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,18 +189,42 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task CanCreatePointOfSaleAppViaAPI()
|
public async Task CanCreateReadAndDeletePointOfSaleApp()
|
||||||
{
|
{
|
||||||
using var tester = CreateServerTester();
|
using var tester = CreateServerTester();
|
||||||
await tester.StartAsync();
|
await tester.StartAsync();
|
||||||
var user = tester.NewAccount();
|
var user = tester.NewAccount();
|
||||||
await user.RegisterDerivationSchemeAsync("BTC");
|
await user.RegisterDerivationSchemeAsync("BTC");
|
||||||
var client = await user.CreateClient();
|
var client = await user.CreateClient();
|
||||||
|
|
||||||
|
// Test creating a POS app
|
||||||
var app = await client.CreatePointOfSaleApp(user.StoreId, new CreatePointOfSaleAppRequest() { AppName = "test app from API" });
|
var app = await client.CreatePointOfSaleApp(user.StoreId, new CreatePointOfSaleAppRequest() { AppName = "test app from API" });
|
||||||
|
|
||||||
Assert.Equal("test app from API", app.Name);
|
Assert.Equal("test app from API", app.Name);
|
||||||
Assert.Equal(user.StoreId, app.StoreId);
|
Assert.Equal(user.StoreId, app.StoreId);
|
||||||
Assert.Equal("PointOfSale", app.AppType);
|
Assert.Equal("PointOfSale", app.AppType);
|
||||||
|
|
||||||
|
// Make sure we return a 404 if we try to get an app that doesn't exist
|
||||||
|
await AssertHttpError(404, async () => {
|
||||||
|
await client.GetApp("some random ID lol");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test that we can retrieve the app data
|
||||||
|
var retrievedApp = await client.GetApp(app.Id);
|
||||||
|
Assert.Equal(app.Name, retrievedApp.Name);
|
||||||
|
Assert.Equal(app.StoreId, retrievedApp.StoreId);
|
||||||
|
Assert.Equal(app.AppType, retrievedApp.AppType);
|
||||||
|
|
||||||
|
// Make sure we return a 404 if we try to delete an app that doesn't exist
|
||||||
|
await AssertHttpError(404, async () =>
|
||||||
|
{
|
||||||
|
await client.DeleteApp("some random ID lol");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test deleting the newly created app
|
||||||
|
await client.DeleteApp(retrievedApp.Id);
|
||||||
|
await AssertHttpError(404, async () => {
|
||||||
|
await client.GetApp(retrievedApp.Id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
|
|||||||
@@ -86,6 +86,38 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
return Ok(ToModel(appData));
|
return Ok(ToModel(appData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("~/api/v1/apps/{appId}")]
|
||||||
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
|
public async Task<IActionResult> GetApp(string appId)
|
||||||
|
{
|
||||||
|
var app = await _appService.GetApp(appId, AppType.PointOfSale);
|
||||||
|
if (app == null)
|
||||||
|
{
|
||||||
|
return AppNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(ToModel(app));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("~/api/v1/apps/{appId}")]
|
||||||
|
public async Task<IActionResult> DeleteApp(string appId)
|
||||||
|
{
|
||||||
|
var app = await _appService.GetApp(appId, null);
|
||||||
|
if (app == null)
|
||||||
|
{
|
||||||
|
return AppNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _appService.DeleteApp(app);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActionResult AppNotFound()
|
||||||
|
{
|
||||||
|
return this.CreateAPIError(404, "app-not-found", "The app with specified ID was not found");
|
||||||
|
}
|
||||||
|
|
||||||
private PointOfSaleAppData ToModel(AppData appData)
|
private PointOfSaleAppData ToModel(AppData appData)
|
||||||
{
|
{
|
||||||
return new PointOfSaleAppData
|
return new PointOfSaleAppData
|
||||||
|
|||||||
@@ -20,11 +20,9 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBXplorer.Models;
|
using NBXplorer.Models;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
|
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
|
||||||
using Language = BTCPayServer.Client.Models.Language;
|
using Language = BTCPayServer.Client.Models.Language;
|
||||||
@@ -75,6 +73,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
private readonly GreenfieldStoreAutomatedLightningPayoutProcessorsController
|
private readonly GreenfieldStoreAutomatedLightningPayoutProcessorsController
|
||||||
_greenfieldStoreAutomatedLightningPayoutProcessorsController;
|
_greenfieldStoreAutomatedLightningPayoutProcessorsController;
|
||||||
|
|
||||||
|
private readonly GreenfieldAppsController _greenFieldAppsController;
|
||||||
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
public BTCPayServerClientFactory(StoreRepository storeRepository,
|
public BTCPayServerClientFactory(StoreRepository storeRepository,
|
||||||
@@ -106,6 +106,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
greenfieldStoreAutomatedOnChainPayoutProcessorsController,
|
greenfieldStoreAutomatedOnChainPayoutProcessorsController,
|
||||||
GreenfieldStoreAutomatedLightningPayoutProcessorsController
|
GreenfieldStoreAutomatedLightningPayoutProcessorsController
|
||||||
greenfieldStoreAutomatedLightningPayoutProcessorsController,
|
greenfieldStoreAutomatedLightningPayoutProcessorsController,
|
||||||
|
GreenfieldAppsController greenFieldAppsController,
|
||||||
IServiceProvider serviceProvider)
|
IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
_storeRepository = storeRepository;
|
_storeRepository = storeRepository;
|
||||||
@@ -137,6 +138,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
greenfieldStoreAutomatedOnChainPayoutProcessorsController;
|
greenfieldStoreAutomatedOnChainPayoutProcessorsController;
|
||||||
_greenfieldStoreAutomatedLightningPayoutProcessorsController =
|
_greenfieldStoreAutomatedLightningPayoutProcessorsController =
|
||||||
greenfieldStoreAutomatedLightningPayoutProcessorsController;
|
greenfieldStoreAutomatedLightningPayoutProcessorsController;
|
||||||
|
_greenFieldAppsController = greenFieldAppsController;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +216,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
_greenfieldPayoutProcessorsController,
|
_greenfieldPayoutProcessorsController,
|
||||||
_greenfieldStoreAutomatedOnChainPayoutProcessorsController,
|
_greenfieldStoreAutomatedOnChainPayoutProcessorsController,
|
||||||
_greenfieldStoreAutomatedLightningPayoutProcessorsController,
|
_greenfieldStoreAutomatedLightningPayoutProcessorsController,
|
||||||
|
_greenFieldAppsController,
|
||||||
new LocalHttpContextAccessor() {HttpContext = context}
|
new LocalHttpContextAccessor() {HttpContext = context}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -259,6 +262,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
|
|
||||||
private readonly GreenfieldStoreUsersController _greenfieldStoreUsersController;
|
private readonly GreenfieldStoreUsersController _greenfieldStoreUsersController;
|
||||||
|
|
||||||
|
private readonly GreenfieldAppsController _greenFieldAppsController;
|
||||||
|
|
||||||
public LocalBTCPayServerClient(
|
public LocalBTCPayServerClient(
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
GreenfieldStoreOnChainPaymentMethodsController chainPaymentMethodsController,
|
GreenfieldStoreOnChainPaymentMethodsController chainPaymentMethodsController,
|
||||||
@@ -287,6 +292,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
greenfieldStoreAutomatedOnChainPayoutProcessorsController,
|
greenfieldStoreAutomatedOnChainPayoutProcessorsController,
|
||||||
GreenfieldStoreAutomatedLightningPayoutProcessorsController
|
GreenfieldStoreAutomatedLightningPayoutProcessorsController
|
||||||
greenfieldStoreAutomatedLightningPayoutProcessorsController,
|
greenfieldStoreAutomatedLightningPayoutProcessorsController,
|
||||||
|
GreenfieldAppsController greenFieldAppsController,
|
||||||
IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "")
|
IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "")
|
||||||
{
|
{
|
||||||
_chainPaymentMethodsController = chainPaymentMethodsController;
|
_chainPaymentMethodsController = chainPaymentMethodsController;
|
||||||
@@ -315,6 +321,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
greenfieldStoreAutomatedOnChainPayoutProcessorsController;
|
greenfieldStoreAutomatedOnChainPayoutProcessorsController;
|
||||||
_greenfieldStoreAutomatedLightningPayoutProcessorsController =
|
_greenfieldStoreAutomatedLightningPayoutProcessorsController =
|
||||||
greenfieldStoreAutomatedLightningPayoutProcessorsController;
|
greenfieldStoreAutomatedLightningPayoutProcessorsController;
|
||||||
|
_greenFieldAppsController = greenFieldAppsController;
|
||||||
|
|
||||||
var controllers = new[]
|
var controllers = new[]
|
||||||
{
|
{
|
||||||
@@ -1278,5 +1285,24 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
await _greenfieldPullPaymentController
|
await _greenfieldPullPaymentController
|
||||||
.GetStorePayouts(storeId, includeCancelled));
|
.GetStorePayouts(storeId, includeCancelled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<PointOfSaleAppData> CreatePointOfSaleApp(
|
||||||
|
string storeId,
|
||||||
|
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return GetFromActionResult<PointOfSaleAppData>(
|
||||||
|
await _greenFieldAppsController.CreatePointOfSaleApp(storeId, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<AppDataBase> GetApp(string appId, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return GetFromActionResult<AppDataBase>(
|
||||||
|
await _greenFieldAppsController.GetApp(appId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task DeleteApp(string appId, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
HandleActionResult(await _greenFieldAppsController.DeleteApp(appId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,11 +170,77 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/apps/{appId}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Apps"
|
||||||
|
],
|
||||||
|
"operationId": "Apps_GetPointOfSaleApp",
|
||||||
|
"summary": "Get basic app data",
|
||||||
|
"description": "Returns basic app data shared between all types of apps",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Basic app data",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/BasicAppData"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "App with specified ID was not found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API_Key": [
|
||||||
|
"btcpay.store.canmodifystoresettings"
|
||||||
|
],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"Apps"
|
||||||
|
],
|
||||||
|
"operationId": "Apps_DeletePointOfSaleApp",
|
||||||
|
"summary": "Delete app",
|
||||||
|
"description": "Deletes apps with specified ID",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "App was deleted"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "App with specified ID was not found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API_Key": [
|
||||||
|
"btcpay.store.canmodifystoresettings"
|
||||||
|
],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"schemas": {
|
"schemas": {
|
||||||
"PointOfSaleAppData": {
|
"PointOfSaleAppData": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/BasicAppData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"BasicAppData": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -200,7 +266,7 @@
|
|||||||
"appType": {
|
"appType": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "PointOfSale",
|
"example": "PointOfSale",
|
||||||
"description": "Type of the app which was created (will always \"PointOfSale\" in this case"
|
"description": "Type of the app which was created"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user