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:
Umar Bolatov 2022-06-26 18:14:16 -07:00 committed by GitHub
parent 61c6a2ab57
commit 95b9e4dfd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 173 additions and 5 deletions

View file

@ -19,5 +19,25 @@ namespace BTCPayServer.Client
method: HttpMethod.Post), token);
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);
}
}
}

View file

@ -189,18 +189,42 @@ namespace BTCPayServer.Tests
[Fact(Timeout = TestTimeout)]
[Trait("Integration", "Integration")]
public async Task CanCreatePointOfSaleAppViaAPI()
public async Task CanCreateReadAndDeletePointOfSaleApp()
{
using var tester = CreateServerTester();
await tester.StartAsync();
var user = tester.NewAccount();
await user.RegisterDerivationSchemeAsync("BTC");
var client = await user.CreateClient();
// Test creating a POS app
var app = await client.CreatePointOfSaleApp(user.StoreId, new CreatePointOfSaleAppRequest() { AppName = "test app from API" });
Assert.Equal("test app from API", app.Name);
Assert.Equal(user.StoreId, app.StoreId);
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)]

View file

@ -86,6 +86,38 @@ namespace BTCPayServer.Controllers.Greenfield
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)
{
return new PointOfSaleAppData

View file

@ -20,11 +20,9 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NBitcoin;
using NBXplorer.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
using Language = BTCPayServer.Client.Models.Language;
@ -75,6 +73,8 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly GreenfieldStoreAutomatedLightningPayoutProcessorsController
_greenfieldStoreAutomatedLightningPayoutProcessorsController;
private readonly GreenfieldAppsController _greenFieldAppsController;
private readonly IServiceProvider _serviceProvider;
public BTCPayServerClientFactory(StoreRepository storeRepository,
@ -106,6 +106,7 @@ namespace BTCPayServer.Controllers.Greenfield
greenfieldStoreAutomatedOnChainPayoutProcessorsController,
GreenfieldStoreAutomatedLightningPayoutProcessorsController
greenfieldStoreAutomatedLightningPayoutProcessorsController,
GreenfieldAppsController greenFieldAppsController,
IServiceProvider serviceProvider)
{
_storeRepository = storeRepository;
@ -137,6 +138,7 @@ namespace BTCPayServer.Controllers.Greenfield
greenfieldStoreAutomatedOnChainPayoutProcessorsController;
_greenfieldStoreAutomatedLightningPayoutProcessorsController =
greenfieldStoreAutomatedLightningPayoutProcessorsController;
_greenFieldAppsController = greenFieldAppsController;
_serviceProvider = serviceProvider;
}
@ -214,6 +216,7 @@ namespace BTCPayServer.Controllers.Greenfield
_greenfieldPayoutProcessorsController,
_greenfieldStoreAutomatedOnChainPayoutProcessorsController,
_greenfieldStoreAutomatedLightningPayoutProcessorsController,
_greenFieldAppsController,
new LocalHttpContextAccessor() {HttpContext = context}
);
}
@ -259,6 +262,8 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly GreenfieldStoreUsersController _greenfieldStoreUsersController;
private readonly GreenfieldAppsController _greenFieldAppsController;
public LocalBTCPayServerClient(
IServiceProvider serviceProvider,
GreenfieldStoreOnChainPaymentMethodsController chainPaymentMethodsController,
@ -287,6 +292,7 @@ namespace BTCPayServer.Controllers.Greenfield
greenfieldStoreAutomatedOnChainPayoutProcessorsController,
GreenfieldStoreAutomatedLightningPayoutProcessorsController
greenfieldStoreAutomatedLightningPayoutProcessorsController,
GreenfieldAppsController greenFieldAppsController,
IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "")
{
_chainPaymentMethodsController = chainPaymentMethodsController;
@ -315,6 +321,7 @@ namespace BTCPayServer.Controllers.Greenfield
greenfieldStoreAutomatedOnChainPayoutProcessorsController;
_greenfieldStoreAutomatedLightningPayoutProcessorsController =
greenfieldStoreAutomatedLightningPayoutProcessorsController;
_greenFieldAppsController = greenFieldAppsController;
var controllers = new[]
{
@ -1278,5 +1285,24 @@ namespace BTCPayServer.Controllers.Greenfield
await _greenfieldPullPaymentController
.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));
}
}
}

View file

@ -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": {
"schemas": {
"PointOfSaleAppData": {
"allOf": [
{
"$ref": "#/components/schemas/BasicAppData"
},
{
}
]
},
"BasicAppData": {
"type": "object",
"properties": {
"id": {
@ -200,7 +266,7 @@
"appType": {
"type": "string",
"example": "PointOfSale",
"description": "Type of the app which was created (will always \"PointOfSale\" in this case"
"description": "Type of the app which was created"
}
}
}