mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-21 14:04:12 +01:00
Fix form value setter (#5387)
* Fix form value setter * Fix test parallelization --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
parent
314a1352ec
commit
99a0b70cfa
8 changed files with 74 additions and 38 deletions
|
@ -105,31 +105,7 @@ public class Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetValues(JObject values)
|
|
||||||
{
|
|
||||||
var fields = GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
|
|
||||||
SetValues(fields, new List<string>(), values);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
|
|
||||||
{
|
|
||||||
foreach (var prop in values.Properties())
|
|
||||||
{
|
|
||||||
List<string> propPath = new List<string>(path.Count + 1);
|
|
||||||
propPath.AddRange(path);
|
|
||||||
propPath.Add(prop.Name);
|
|
||||||
if (prop.Value.Type == JTokenType.Object)
|
|
||||||
{
|
|
||||||
SetValues(fields, propPath, (JObject)prop.Value);
|
|
||||||
}
|
|
||||||
else if (prop.Value.Type == JTokenType.String)
|
|
||||||
{
|
|
||||||
var fullName = string.Join('_', propPath.Where(s => !string.IsNullOrEmpty(s)));
|
|
||||||
if (fields.TryGetValue(fullName, out var f) && !f.Constant)
|
|
||||||
f.Value = prop.Value.Value<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Form;
|
using BTCPayServer.Abstractions.Form;
|
||||||
using BTCPayServer.Forms;
|
using BTCPayServer.Forms;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
@ -10,16 +11,25 @@ using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace BTCPayServer.Tests;
|
namespace BTCPayServer.Tests;
|
||||||
|
|
||||||
[Trait("Fast", "Fast")]
|
[Collection(nameof(NonParallelizableCollectionDefinition))]
|
||||||
|
[Trait("Integration", "Integration")]
|
||||||
public class FormTests : UnitTestBase
|
public class FormTests : UnitTestBase
|
||||||
{
|
{
|
||||||
public FormTests(ITestOutputHelper helper) : base(helper)
|
public FormTests(ITestOutputHelper helper) : base(helper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CanParseForm()
|
[Fact(Timeout = TestUtils.TestTimeout)]
|
||||||
|
[Trait("Integration", "Integration")]
|
||||||
|
public async Task CanParseForm()
|
||||||
{
|
{
|
||||||
|
using var tester = CreateServerTester();
|
||||||
|
await tester.StartAsync();
|
||||||
|
var user = tester.NewAccount();
|
||||||
|
user.GrantAccess();
|
||||||
|
var service = tester.PayTester.GetService<FormDataService>();
|
||||||
|
|
||||||
var form = new Form()
|
var form = new Form()
|
||||||
{
|
{
|
||||||
Fields = new List<Field>
|
Fields = new List<Field>
|
||||||
|
@ -40,8 +50,6 @@ public class FormTests : UnitTestBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var providers = new FormComponentProviders(new List<IFormComponentProvider>());
|
|
||||||
var service = new FormDataService(null, providers);
|
|
||||||
Assert.False(service.IsFormSchemaValid(form.ToString(), out _, out _));
|
Assert.False(service.IsFormSchemaValid(form.ToString(), out _, out _));
|
||||||
form = new Form
|
form = new Form
|
||||||
{
|
{
|
||||||
|
@ -164,7 +172,7 @@ public class FormTests : UnitTestBase
|
||||||
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
||||||
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
||||||
Clear(form);
|
Clear(form);
|
||||||
form.SetValues(obj);
|
service.SetValues(form, obj);
|
||||||
obj = service.GetValues(form);
|
obj = service.GetValues(form);
|
||||||
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
||||||
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
||||||
|
@ -182,10 +190,12 @@ public class FormTests : UnitTestBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
form.SetValues(obj);
|
|
||||||
|
service.SetValues(form, obj);
|
||||||
obj = service.GetValues(form);
|
obj = service.GetValues(form);
|
||||||
Assert.Null(obj["test"].Value<string>());
|
Assert.Null(obj["test"].Value<string>());
|
||||||
form.SetValues(new JObject { ["test"] = "hello" });
|
|
||||||
|
service.SetValues(form, new JObject { ["test"] = "hello" });
|
||||||
obj = service.GetValues(form);
|
obj = service.GetValues(form);
|
||||||
Assert.Equal("hello", obj["test"].Value<string>());
|
Assert.Equal("hello", obj["test"].Value<string>());
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,7 +223,7 @@ namespace BTCPayServer.Controllers
|
||||||
|
|
||||||
var blob = custodianAccount.GetBlob();
|
var blob = custodianAccount.GetBlob();
|
||||||
var configForm = await custodian.GetConfigForm(blob, HttpContext.RequestAborted);
|
var configForm = await custodian.GetConfigForm(blob, HttpContext.RequestAborted);
|
||||||
configForm.SetValues(blob);
|
_formDataService.SetValues(configForm, blob);
|
||||||
|
|
||||||
var vm = new EditCustodianAccountViewModel();
|
var vm = new EditCustodianAccountViewModel();
|
||||||
vm.CustodianAccount = custodianAccount;
|
vm.CustodianAccount = custodianAccount;
|
||||||
|
@ -280,14 +280,14 @@ namespace BTCPayServer.Controllers
|
||||||
// First, we restore the previous form based on the previous blob that was
|
// First, we restore the previous form based on the previous blob that was
|
||||||
// stored in config
|
// stored in config
|
||||||
var form = await custodian.GetConfigForm(b, HttpContext.RequestAborted);
|
var form = await custodian.GetConfigForm(b, HttpContext.RequestAborted);
|
||||||
form.SetValues(b);
|
_formDataService.SetValues(form, b);
|
||||||
// Then we apply new values overriding the previous blob from the Form params
|
// Then we apply new values overriding the previous blob from the Form params
|
||||||
form.ApplyValuesFromForm(Request.Form);
|
form.ApplyValuesFromForm(Request.Form);
|
||||||
// We extract the new resulting blob, and request what is the next form based on it
|
// We extract the new resulting blob, and request what is the next form based on it
|
||||||
b = _formDataService.GetValues(form);
|
b = _formDataService.GetValues(form);
|
||||||
form = await custodian.GetConfigForm(_formDataService.GetValues(form), HttpContext.RequestAborted);
|
form = await custodian.GetConfigForm(_formDataService.GetValues(form), HttpContext.RequestAborted);
|
||||||
// We set all the values to this blob, and validate the form
|
// We set all the values to this blob, and validate the form
|
||||||
form.SetValues(b);
|
_formDataService.SetValues(form, b);
|
||||||
_formDataService.Validate(form, ModelState);
|
_formDataService.Validate(form, ModelState);
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
@ -600,7 +600,7 @@ namespace BTCPayServer.Controllers
|
||||||
catch (BadConfigException e)
|
catch (BadConfigException e)
|
||||||
{
|
{
|
||||||
Form configForm = await custodian.GetConfigForm(config);
|
Form configForm = await custodian.GetConfigForm(config);
|
||||||
configForm.SetValues(config);
|
_formDataService.SetValues(configForm, config);
|
||||||
string[] badConfigFields = new string[e.BadConfigKeys.Length];
|
string[] badConfigFields = new string[e.BadConfigKeys.Length];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (var oneField in configForm.GetAllFields())
|
foreach (var oneField in configForm.GetAllFields())
|
||||||
|
|
|
@ -31,4 +31,9 @@ public class FieldValueMirror : IFormComponentProvider
|
||||||
|
|
||||||
return rawValue;
|
return rawValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetValue(Field field, JToken value)
|
||||||
|
{
|
||||||
|
//ignored
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,4 +218,36 @@ public class FormDataService
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetValues(Form form, JObject values)
|
||||||
|
{
|
||||||
|
|
||||||
|
var fields = form.GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
|
||||||
|
SetValues(fields, new List<string>(), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
|
||||||
|
{
|
||||||
|
foreach (var prop in values.Properties())
|
||||||
|
{
|
||||||
|
List<string> propPath = new List<string>(path.Count + 1);
|
||||||
|
propPath.AddRange(path);
|
||||||
|
propPath.Add(prop.Name);
|
||||||
|
if (prop.Value.Type == JTokenType.Object)
|
||||||
|
{
|
||||||
|
SetValues(fields, propPath, (JObject)prop.Value);
|
||||||
|
}
|
||||||
|
else if (prop.Value.Type == JTokenType.String)
|
||||||
|
{
|
||||||
|
var fullName = string.Join('_', propPath.Where(s => !string.IsNullOrEmpty(s)));
|
||||||
|
if (fields.TryGetValue(fullName, out var f) && !f.Constant)
|
||||||
|
{
|
||||||
|
if (_formProviders.TypeToComponentProvider.TryGetValue(f.Type, out var formComponentProvider))
|
||||||
|
{
|
||||||
|
formComponentProvider.SetValue(f, prop.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using BTCPayServer.Abstractions.Form;
|
using BTCPayServer.Abstractions.Form;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace BTCPayServer.Forms;
|
namespace BTCPayServer.Forms;
|
||||||
|
|
||||||
|
@ -17,6 +18,11 @@ public class HtmlFieldsetFormProvider : IFormComponentProvider
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetValue(Field field, JToken value)
|
||||||
|
{
|
||||||
|
//ignored
|
||||||
|
}
|
||||||
|
|
||||||
public void Validate(Form form, Field field)
|
public void Validate(Form form, Field field)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using BTCPayServer.Abstractions.Form;
|
using BTCPayServer.Abstractions.Form;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace BTCPayServer.Forms;
|
namespace BTCPayServer.Forms;
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ public interface IFormComponentProvider
|
||||||
void Validate(Form form, Field field);
|
void Validate(Form form, Field field);
|
||||||
void Register(Dictionary<string, IFormComponentProvider> typeToComponentProvider);
|
void Register(Dictionary<string, IFormComponentProvider> typeToComponentProvider);
|
||||||
string GetValue(Form form, Field field);
|
string GetValue(Form form, Field field);
|
||||||
|
void SetValue(Field field, JToken value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class FormComponentProviderBase : IFormComponentProvider
|
public abstract class FormComponentProviderBase : IFormComponentProvider
|
||||||
|
@ -21,6 +23,11 @@ public abstract class FormComponentProviderBase : IFormComponentProvider
|
||||||
return field.Value;
|
return field.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetValue(Field field, JToken value)
|
||||||
|
{
|
||||||
|
field.Value = value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void Validate(Form form, Field field);
|
public abstract void Validate(Form form, Field field);
|
||||||
|
|
||||||
public void ValidateField<T>(Field field) where T : ValidationAttribute, new()
|
public void ValidateField<T>(Field field) where T : ValidationAttribute, new()
|
||||||
|
|
|
@ -277,7 +277,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||||
|
|
||||||
formResponseJObject = TryParseJObject(formResponse) ?? new JObject();
|
formResponseJObject = TryParseJObject(formResponse) ?? new JObject();
|
||||||
var form = Form.Parse(formData.Config);
|
var form = Form.Parse(formData.Config);
|
||||||
form.SetValues(formResponseJObject);
|
FormDataService.SetValues(form, formResponseJObject);
|
||||||
if (!FormDataService.Validate(form, ModelState))
|
if (!FormDataService.Validate(form, ModelState))
|
||||||
{
|
{
|
||||||
//someone tried to bypass validation
|
//someone tried to bypass validation
|
||||||
|
|
Loading…
Add table
Reference in a new issue