From 5443e41881214703f8e685ed27cb4cb5e77599c2 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 5 Nov 2020 13:13:17 -0300 Subject: [PATCH 1/8] Add is instanceof CountryBasedPaymentAccount check to PaymentAccount (Ask the account instance what it is.) --- core/src/main/java/bisq/core/payment/PaymentAccount.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/bisq/core/payment/PaymentAccount.java b/core/src/main/java/bisq/core/payment/PaymentAccount.java index b38649ef942..f94811585f1 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccount.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccount.java @@ -173,6 +173,10 @@ public String getOwnerId() { return paymentAccountPayload.getOwnerId(); } + public boolean isCountryBasedPaymentAccount() { + return this instanceof CountryBasedPaymentAccount; + } + public boolean isHalCashAccount() { return this instanceof HalCashAccount; } From 0f3b61e2fa5425f6565a0e63bec2db48f5f93249 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 5 Nov 2020 13:15:34 -0300 Subject: [PATCH 2/8] Add ReflectionUtils to :common.util pkg --- .../bisq/common/util/ReflectionUtils.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 common/src/main/java/bisq/common/util/ReflectionUtils.java diff --git a/common/src/main/java/bisq/common/util/ReflectionUtils.java b/common/src/main/java/bisq/common/util/ReflectionUtils.java new file mode 100644 index 00000000000..e70162ecb23 --- /dev/null +++ b/common/src/main/java/bisq/common/util/ReflectionUtils.java @@ -0,0 +1,108 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.util; + + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import static java.util.Arrays.stream; +import static org.apache.commons.lang3.StringUtils.capitalize; + +public class ReflectionUtils { + + /** + * Recursively loads a list of fields for a given class and its superclasses, + * using a filter predicate to exclude any unwanted fields. + * + * @param fields The list of fields being loaded for a class hierarchy. + * @param clazz The lowest level class in a hierarchy; excluding Object.class. + * @param isExcludedField The field exclusion predicate. + */ + public static void loadFieldListForClassHierarchy(List fields, + Class clazz, + Predicate isExcludedField) { + fields.addAll(stream(clazz.getDeclaredFields()) + .filter(f -> !isExcludedField.test(f)) + .collect(Collectors.toList())); + + Class superclass = clazz.getSuperclass(); + if (!Objects.equals(superclass, Object.class)) + loadFieldListForClassHierarchy(fields, + superclass, + isExcludedField); + } + + /** + * Returns an Optional of a setter method for a given field and a class hierarchy, + * or Optional.empty() if it does not exist. + * + * @param field The field used to find a setter method. + * @param clazz The lowest level class in a hierarchy; excluding Object.class. + * @return Optional of the setter method for a field in the class hierarchy, + * or Optional.empty() if it does not exist. + */ + public static Optional getSetterMethodForFieldInClassHierarchy(Field field, + Class clazz) { + Optional setter = stream(clazz.getDeclaredMethods()) + .filter((m) -> isSetterForField(m, field)) + .findFirst(); + + if (setter.isPresent()) + return setter; + + Class superclass = clazz.getSuperclass(); + if (!Objects.equals(superclass, Object.class)) { + setter = getSetterMethodForFieldInClassHierarchy(field, superclass); + if (setter.isPresent()) + return setter; + } + + return Optional.empty(); + } + + public static boolean isSetterForField(Method m, Field f) { + return m.getName().startsWith("set") + && m.getName().endsWith(capitalize(f.getName())) + && m.getReturnType().getName().equals("void") + && m.getParameterCount() == 1 + && m.getParameterTypes()[0].getName().equals(f.getType().getName()); + } + + public static boolean isSetterOnClass(Method setter, Class clazz) { + return clazz.equals(setter.getDeclaringClass()); + } + + public static String getVisibilityModifierAsString(Field field) { + if (Modifier.isPrivate(field.getModifiers())) + return "private"; + else if (Modifier.isProtected(field.getModifiers())) + return "protected"; + else if (Modifier.isPublic(field.getModifiers())) + return "public"; + else + return ""; + } +} From b9dbd3452326d9d9b1578aacd4a36519550a8aed Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 5 Nov 2020 13:16:18 -0300 Subject: [PATCH 3/8] Add gson type adapter for PaymentAccount serialization & de-serialization --- .../api/model/PaymentAccountTypeAdapter.java | 323 ++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 core/src/main/java/bisq/core/api/model/PaymentAccountTypeAdapter.java diff --git a/core/src/main/java/bisq/core/api/model/PaymentAccountTypeAdapter.java b/core/src/main/java/bisq/core/api/model/PaymentAccountTypeAdapter.java new file mode 100644 index 00000000000..a4265f1bbfe --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/PaymentAccountTypeAdapter.java @@ -0,0 +1,323 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.api.model; + + +import bisq.core.locale.Country; +import bisq.core.locale.FiatCurrency; +import bisq.core.payment.CountryBasedPaymentAccount; +import bisq.core.payment.PaymentAccount; +import bisq.core.payment.payload.PaymentAccountPayload; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import lombok.extern.slf4j.Slf4j; + +import static bisq.common.util.ReflectionUtils.getSetterMethodForFieldInClassHierarchy; +import static bisq.common.util.ReflectionUtils.getVisibilityModifierAsString; +import static bisq.common.util.ReflectionUtils.isSetterOnClass; +import static bisq.common.util.ReflectionUtils.loadFieldListForClassHierarchy; +import static bisq.core.locale.CountryUtil.findCountryByCode; +import static bisq.core.locale.CurrencyUtil.getCurrencyByCountryCode; +import static java.lang.String.format; +import static java.util.Comparator.comparing; + +@Slf4j +class PaymentAccountTypeAdapter extends TypeAdapter { + + private final Class paymentAccountType; + private final Class paymentAccountPayloadType; + private final Map> fieldSettersMap; + private final Predicate isExcludedField; + + /** + * Constructor used when de-serializing a json payment account from into a + * PaymentAccount instance. + * + * @param paymentAccountType the PaymentAccount subclass being instantiated + */ + public PaymentAccountTypeAdapter(Class paymentAccountType) { + this(paymentAccountType, new String[]{}); + } + + /** + * Constructor used when serializing a PaymentAccount subclass instance into a json + * payment account json form. + * + * @param paymentAccountType the PaymentAccount subclass being serialized + * @param excludedFields a string array of field names to exclude from the serialized + * payment account json form. + */ + public PaymentAccountTypeAdapter(Class paymentAccountType, String[] excludedFields) { + this.paymentAccountType = paymentAccountType; + this.paymentAccountPayloadType = getPaymentAccountPayloadType(); + this.isExcludedField = (f) -> Arrays.stream(excludedFields).anyMatch(e -> e.equals(f.getName())); + this.fieldSettersMap = getFieldSetterMap(); + } + + @Override + public void write(JsonWriter out, PaymentAccount account) throws IOException { + // We just write a blank payment acct from for a payment method id. + // We're not serializing a real payment account instance here. + if (log.isDebugEnabled()) + log.debug("Writing PaymentAccount json form for fields with accessors..."); + + out.beginObject(); + writeCommonFields(out, account); + fieldSettersMap.forEach((field, value) -> { + if (log.isDebugEnabled()) + log.debug("Append form with settable field: {} {} {} setter: {}", + getVisibilityModifierAsString(field), + field.getType().getSimpleName(), + field.getName(), + value); + try { + // Write out a json element if there is a @Setter for this field. + if (value.isPresent()) { + String fieldName = field.getName(); + out.name(fieldName); + out.value("Your " + fieldName.toLowerCase()); + } + } catch (Exception ex) { + throw new IllegalStateException( + format("Could not serialize a %s to json", account.getClass().getSimpleName()), ex); + } + }); + out.endObject(); + if (log.isDebugEnabled()) + log.debug("Done writing PaymentAccount json form."); + } + + @Override + public PaymentAccount read(JsonReader in) throws IOException { + if (log.isDebugEnabled()) + log.debug("De-serializing json to new {} ...", paymentAccountType.getSimpleName()); + + PaymentAccount account = initNewPaymentAccount(); + in.beginObject(); + while (in.hasNext()) { + String currentFieldName = in.nextName(); + + // Some of the fields are common to all payment account types. + if (didReadCommonField(in, account, currentFieldName)) + continue; + + // If the account is a subclass of CountryBasedPaymentAccount, set the + // account's Country, and use the Country to derive and set the account's + // FiatCurrency. + if (didReadCountryField(in, account, currentFieldName)) + continue; + + try { + Optional field = fieldSettersMap.keySet().stream() + .filter(k -> k.getName().equals(currentFieldName)).findFirst(); + + field.ifPresentOrElse((f) -> invokeSetterMethod(account, f, in), () -> { + throw new IllegalStateException( + format("Could not de-serialize json to a '%s' because there is no %s field.", + account.getClass().getSimpleName(), + currentFieldName)); + }); + } catch (Exception ex) { + throw new IllegalStateException( + format("Could not de-serialize json to a '%s'.", + account.getClass().getSimpleName()), ex); + } + } + in.endObject(); + if (log.isDebugEnabled()) + log.debug("Done de-serializing json."); + + return account; + } + + private void invokeSetterMethod(PaymentAccount account, Field field, JsonReader jsonReader) { + Optional setter = fieldSettersMap.get(field); + if (setter.isPresent()) { + try { + // The setter might be on the PaymentAccount instance, or its + // PaymentAccountPayload instance. + if (isSetterOnPaymentAccountClass(setter.get(), account)) { + setter.get().invoke(account, nextStringOrNull(jsonReader)); + } else if (isSetterOnPaymentAccountPayloadClass(setter.get(), account)) { + setter.get().invoke(account.getPaymentAccountPayload(), nextStringOrNull(jsonReader)); + } else { + String exMsg = format("Could not de-serialize json to a '%s' using reflection" + + " because the setter's declaring class was not found.", + account.getClass().getSimpleName()); + throw new IllegalStateException(exMsg); + } + } catch (IllegalAccessException | InvocationTargetException ex) { + throw new IllegalStateException( + format("Could not de-serialize json to a '%s' due to reflection error.", + account.getClass().getSimpleName()), ex); + } + } else { + throw new IllegalStateException( + format("Could not de-serialize json to a '%s' because there is no setter for field %s.", + account.getClass().getSimpleName(), + field.getName())); + } + } + + private boolean isSetterOnPaymentAccountClass(Method setter, PaymentAccount account) { + return isSetterOnClass(setter, account.getClass()); + } + + private boolean isSetterOnPaymentAccountPayloadClass(Method setter, PaymentAccount account) { + return isSetterOnClass(setter, account.getPaymentAccountPayload().getClass()) + || isSetterOnClass(setter, account.getPaymentAccountPayload().getClass().getSuperclass()); + } + + private Map> getFieldSetterMap() { + List orderedFields = getOrderedFields(); + Map> map = new LinkedHashMap<>(); + for (Field field : orderedFields) { + Optional setter = getSetterMethodForFieldInClassHierarchy(field, paymentAccountType) + .or(() -> getSetterMethodForFieldInClassHierarchy(field, paymentAccountPayloadType)); + map.put(field, setter); + } + return Collections.unmodifiableMap(map); + } + + private List getOrderedFields() { + List fields = new ArrayList<>(); + loadFieldListForClassHierarchy(fields, paymentAccountType, isExcludedField); + loadFieldListForClassHierarchy(fields, paymentAccountPayloadType, isExcludedField); + fields.sort(comparing(Field::getName)); + return fields; + } + + private String nextStringOrNull(JsonReader in) { + try { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + return in.nextString(); + } + } catch (IOException ex) { + throw new IllegalStateException("Could not peek at next String value in JsonReader.", ex); + } + } + + @SuppressWarnings("unused") + private Long nextLongOrNull(JsonReader in) { + try { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + return in.nextLong(); + } + } catch (IOException ex) { + throw new IllegalStateException("Could not peek at next Long value in JsonReader.", ex); + } + } + + private void writeCommonFields(JsonWriter out, PaymentAccount account) throws IOException { + out.name("_COMMENT_"); + out.value("Please do not edit the paymentMethodId field."); + + out.name("paymentMethodId"); + out.value(account.getPaymentMethod().getId()); + } + + private boolean didReadCommonField(JsonReader in, PaymentAccount account, String fieldName) { + switch (fieldName) { + case "_COMMENT_": + case "paymentMethodId": + // skip + nextStringOrNull(in); + return true; + case "accountName": + account.setAccountName(nextStringOrNull(in)); + return true; + default: + return false; + } + } + + private boolean didReadCountryField(JsonReader in, PaymentAccount account, String fieldName) { + if (account.isCountryBasedPaymentAccount() && fieldName.equals("country")) { + // Read the country code, and use it to set the account's country and single + // trade currency fields. + String countryCode = nextStringOrNull(in); + Optional country = findCountryByCode(countryCode); + if (country.isPresent()) { + ((CountryBasedPaymentAccount) account).setCountry(country.get()); + FiatCurrency fiatCurrency = getCurrencyByCountryCode(Objects.requireNonNull(countryCode)); + account.setSingleTradeCurrency(fiatCurrency); + return true; + } else { + throw new IllegalStateException( + format("Could not de-serialize json to a '%s' because %s is an invalid country code.", + account.getClass().getSimpleName(), countryCode)); + } + } else { + return false; + } + } + + private Class getPaymentAccountPayloadType() { + try { + Package pkg = PaymentAccountPayload.class.getPackage(); + //noinspection unchecked + return (Class) Class.forName(pkg.getName() + + "." + paymentAccountType.getSimpleName() + "Payload"); + } catch (Exception ex) { + throw new IllegalStateException( + format("Could not get payload class for %s", paymentAccountType.getSimpleName()), ex); + } + } + + private PaymentAccount initNewPaymentAccount() { + try { + Constructor constructor = paymentAccountType.getDeclaredConstructor(); + PaymentAccount paymentAccount = (PaymentAccount) constructor.newInstance(); + paymentAccount.init(); + return paymentAccount; + } catch (NoSuchMethodException ex) { + throw new IllegalStateException(format("No default declared constructor found for class %s", + paymentAccountType.getSimpleName()), ex); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException ex) { + throw new IllegalStateException(format("Could not instantiate class %s", + paymentAccountType.getSimpleName()), ex); + } + } +} From b84473fbd41172db915cba3ca73e258a3154ba43 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 5 Nov 2020 13:17:17 -0300 Subject: [PATCH 4/8] Add PaymentAccountForm for writing empty and submitting filled fiat acct json forms --- .../core/api/model/PaymentAccountForm.java | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 core/src/main/java/bisq/core/api/model/PaymentAccountForm.java diff --git a/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java new file mode 100644 index 00000000000..7afaf98abdb --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java @@ -0,0 +1,135 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.api.model; + +import bisq.core.payment.PaymentAccount; +import bisq.core.payment.PaymentAccountFactory; +import bisq.core.payment.payload.PaymentMethod; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import javax.inject.Singleton; + +import java.net.URI; +import java.net.URISyntaxException; + +import java.nio.file.Files; +import java.nio.file.Paths; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import java.util.Map; + +import java.lang.reflect.Type; + +import lombok.extern.slf4j.Slf4j; + +import static bisq.core.payment.payload.PaymentMethod.getPaymentMethodById; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.io.File.separatorChar; +import static java.lang.String.format; +import static java.lang.System.getProperty; +import static java.nio.charset.StandardCharsets.UTF_8; + +@Singleton +@Slf4j +public class PaymentAccountForm { + + private final GsonBuilder gsonBuilder = new GsonBuilder() + .setPrettyPrinting() + .serializeNulls(); + + // Names of PaymentAccount fields to exclude from json forms. + private final String[] excludedFields = new String[]{ + "log", + "id", + "acceptedCountryCodes", + "countryCode", + "creationDate", + "excludeFromJsonDataMap", + "maxTradePeriod", + "paymentAccountPayload", + "paymentMethod", + "paymentMethodId", + "selectedTradeCurrency", + "tradeCurrencies", + "HOLDER_NAME", + "SALT" + }; + + public File getPaymentAccountForm(String paymentMethodId) { + PaymentMethod paymentMethod = getPaymentMethodById(paymentMethodId); + File file = new File(getProperty("java.io.tmpdir") + separatorChar + + paymentMethod.getId().toLowerCase() + "_form.json"); + try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file, false), UTF_8)) { + PaymentAccount paymentAccount = PaymentAccountFactory.getPaymentAccount(paymentMethod); + Class clazz = paymentAccount.getClass(); + Gson gson = gsonBuilder.registerTypeAdapter(clazz, new PaymentAccountTypeAdapter(clazz, excludedFields)).create(); + String json = gson.toJson(paymentAccount); // serializes target to json + outputStreamWriter.write(json); + } catch (Exception ex) { + log.error(format("Could not export json file for %s account.", paymentMethod.getShortName()), ex); + } + return file; + } + + public PaymentAccount toPaymentAccount(File jsonForm) { + String json = toJsonString(jsonForm); + Class clazz = getPaymentAccountClassFromJson(json); + Gson gson = gsonBuilder.registerTypeAdapter(clazz, new PaymentAccountTypeAdapter(clazz)).create(); + return gson.fromJson(json, clazz); + } + + public String toJsonString(File jsonFile) { + try { + checkNotNull(jsonFile, "json file cannot be null"); + return new String(Files.readAllBytes(Paths.get(jsonFile.getAbsolutePath()))); + } catch (IOException ex) { + throw new IllegalStateException(format("Could not read content from file '%s'", + jsonFile.getAbsolutePath()), ex); + } + } + + public URI getClickableURI(File jsonForm) { + try { + return new URI("file", + "", + jsonForm.toURI().getPath(), + null, + null); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException("", ex); + } + } + + private Class getPaymentAccountClassFromJson(String json) { + Map jsonMap = gsonBuilder.create().fromJson(json, (Type) Object.class); + String paymentMethodId = checkNotNull((String) jsonMap.get("paymentMethodId"), + format("Could not find a paymentMethodId in the json string: %s", json)); + return getPaymentAccountClass(paymentMethodId); + } + + private Class getPaymentAccountClass(String paymentMethodId) { + PaymentMethod paymentMethod = PaymentMethod.getPaymentMethodById(paymentMethodId); + return PaymentAccountFactory.getPaymentAccount(paymentMethod).getClass(); + } +} From d3be0487a6dd52ea00a1310154906b777a3d3aad Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 5 Nov 2020 13:19:30 -0300 Subject: [PATCH 5/8] Test PaymentAccount / Json serialization and de-serialization --- .../api/model/PaymentAccountFormTest.java | 411 ++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java diff --git a/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java b/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java new file mode 100644 index 00000000000..ceb5c319f2c --- /dev/null +++ b/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java @@ -0,0 +1,411 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.api.model; + +import bisq.core.locale.Res; +import bisq.core.payment.ChaseQuickPayAccount; +import bisq.core.payment.ClearXchangeAccount; +import bisq.core.payment.F2FAccount; +import bisq.core.payment.HalCashAccount; +import bisq.core.payment.JapanBankAccount; +import bisq.core.payment.NationalBankAccount; +import bisq.core.payment.PaymentAccount; +import bisq.core.payment.SepaAccount; +import bisq.core.payment.USPostalMoneyOrderAccount; +import bisq.core.payment.payload.BankAccountPayload; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonWriter; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.Before; +import org.junit.Test; + +import static bisq.core.payment.payload.PaymentMethod.*; +import static java.lang.String.format; +import static java.lang.System.getProperty; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@Slf4j +public class PaymentAccountFormTest { + + private static final String PROPERTY_NAME_COMMENT = "_COMMENT_"; + private static final String PROPERTY_VALUE_COMMENT = "Please do not edit the paymentMethodId field."; + + private static final String PROPERTY_NAME_PAYMENT_METHOD_ID = "paymentMethodId"; + + private static final String PROPERTY_NAME_ACCOUNT_NAME = "accountName"; + private static final String PROPERTY_NAME_ACCOUNT_NR = "accountNr"; + private static final String PROPERTY_NAME_ACCOUNT_TYPE = "accountType"; + private static final String PROPERTY_NAME_BANK_ACCOUNT_NAME = "bankAccountName"; + private static final String PROPERTY_NAME_BANK_ACCOUNT_NUMBER = "bankAccountNumber"; + private static final String PROPERTY_NAME_BANK_ACCOUNT_TYPE = "bankAccountType"; + private static final String PROPERTY_NAME_BANK_BRANCH_CODE = "bankBranchCode"; + private static final String PROPERTY_NAME_BANK_BRANCH_NAME = "bankBranchName"; + private static final String PROPERTY_NAME_BANK_CODE = "bankCode"; + @SuppressWarnings("unused") + private static final String PROPERTY_NAME_BANK_ID = "bankId"; + private static final String PROPERTY_NAME_BANK_NAME = "bankName"; + private static final String PROPERTY_NAME_BRANCH_ID = "branchId"; + private static final String PROPERTY_NAME_BIC = "bic"; + private static final String PROPERTY_NAME_COUNTRY = "country"; + private static final String PROPERTY_NAME_CITY = "city"; + private static final String PROPERTY_NAME_CONTACT = "contact"; + private static final String PROPERTY_NAME_EMAIL = "email"; + private static final String PROPERTY_NAME_EMAIL_OR_MOBILE_NR = "emailOrMobileNr"; + private static final String PROPERTY_NAME_EXTRA_INFO = "extraInfo"; + private static final String PROPERTY_NAME_HOLDER_NAME = "holderName"; + private static final String PROPERTY_NAME_HOLDER_TAX_ID = "holderTaxId"; + private static final String PROPERTY_NAME_IBAN = "iban"; + private static final String PROPERTY_NAME_MOBILE_NR = "mobileNr"; + private static final String PROPERTY_NAME_NATIONAL_ACCOUNT_ID = "nationalAccountId"; + private static final String PROPERTY_NAME_POSTAL_ADDRESS = "postalAddress"; + + private static final Gson gson = new GsonBuilder() + .setPrettyPrinting() + .serializeNulls() + .create(); + + + // The payment account serializer / deserializer. + private static final PaymentAccountForm paymentAccountForm = new PaymentAccountForm(); + + private static final Map EXPECTED_FORM = new HashMap<>(); + + @Before + public void setup() { + Res.setup(); + EXPECTED_FORM.clear(); + } + + + @Test + public void testBrazilNationalBankAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(NATIONAL_BANK_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + NATIONAL_BANK_ID, + PROPERTY_NAME_ACCOUNT_NR, + PROPERTY_NAME_ACCOUNT_TYPE, + PROPERTY_NAME_BANK_NAME, + PROPERTY_NAME_BRANCH_ID, + PROPERTY_NAME_COUNTRY, + PROPERTY_NAME_HOLDER_NAME, + PROPERTY_NAME_HOLDER_TAX_ID, + PROPERTY_NAME_NATIONAL_ACCOUNT_ID); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, NATIONAL_BANK_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "Banco do Brasil"); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NR, "456789-87"); + // No BankId is required for BR. + EXPECTED_FORM.put(PROPERTY_NAME_BANK_NAME, "Banco do Brasil"); + EXPECTED_FORM.put(PROPERTY_NAME_BRANCH_ID, "456789-10"); + EXPECTED_FORM.put(PROPERTY_NAME_COUNTRY, "BR"); + EXPECTED_FORM.put(PROPERTY_NAME_HOLDER_NAME, "Joao da Silva"); + EXPECTED_FORM.put(PROPERTY_NAME_HOLDER_TAX_ID, "123456789"); + EXPECTED_FORM.put(PROPERTY_NAME_NATIONAL_ACCOUNT_ID, "123456789"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + NationalBankAccount paymentAccount = (NationalBankAccount) paymentAccountForm.toPaymentAccount(completedForm); + verifyAccountFiatCurrency(paymentAccount, "BRL"); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_COUNTRY), + Objects.requireNonNull(paymentAccount.getCountry()).code); + + BankAccountPayload payload = (BankAccountPayload) paymentAccount.getPaymentAccountPayload(); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_ACCOUNT_NR), payload.getAccountNr()); + // When no BankId is required, getBankId() returns bankName. + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_NAME), payload.getBankId()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_NAME), payload.getBankName()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BRANCH_ID), payload.getBranchId()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_HOLDER_NAME), payload.getHolderName()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_HOLDER_TAX_ID), payload.getHolderTaxId()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_NATIONAL_ACCOUNT_ID), payload.getNationalAccountId()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + } + + @Test + public void testChaseQuickPayAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(CHASE_QUICK_PAY_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + CHASE_QUICK_PAY_ID, + PROPERTY_NAME_EMAIL, + PROPERTY_NAME_HOLDER_NAME); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, CHASE_QUICK_PAY_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "Quick Pay Acct"); + EXPECTED_FORM.put(PROPERTY_NAME_EMAIL, "johndoe@quickpay.com"); + EXPECTED_FORM.put(PROPERTY_NAME_HOLDER_NAME, "John Doe"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + ChaseQuickPayAccount paymentAccount = (ChaseQuickPayAccount) paymentAccountForm.toPaymentAccount(completedForm); + verifyAccountFiatCurrency(paymentAccount, "USD"); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + } + + @Test + public void testClearXChangeAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(CLEAR_X_CHANGE_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + CLEAR_X_CHANGE_ID, + PROPERTY_NAME_EMAIL_OR_MOBILE_NR, + PROPERTY_NAME_HOLDER_NAME); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, CLEAR_X_CHANGE_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "USD Zelle Account"); + EXPECTED_FORM.put(PROPERTY_NAME_EMAIL_OR_MOBILE_NR, "jane@doe.com"); + EXPECTED_FORM.put(PROPERTY_NAME_HOLDER_NAME, "Jane Doe"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + ClearXchangeAccount paymentAccount = (ClearXchangeAccount) paymentAccountForm.toPaymentAccount(completedForm); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_EMAIL_OR_MOBILE_NR), paymentAccount.getEmailOrMobileNr()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + verifyAccountFiatCurrency(paymentAccount, "USD"); + } + + @Test + public void testF2FAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(F2F_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + F2F_ID, + PROPERTY_NAME_COUNTRY, + PROPERTY_NAME_CITY, + PROPERTY_NAME_CONTACT, + PROPERTY_NAME_EXTRA_INFO); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, F2F_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "Conta Cara a Cara"); + EXPECTED_FORM.put(PROPERTY_NAME_COUNTRY, "BR"); + EXPECTED_FORM.put(PROPERTY_NAME_CITY, "Rio de Janeiro"); + EXPECTED_FORM.put(PROPERTY_NAME_CONTACT, "Freddy Beira Mar"); + EXPECTED_FORM.put(PROPERTY_NAME_EXTRA_INFO, "So fim de semana"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + F2FAccount paymentAccount = (F2FAccount) paymentAccountForm.toPaymentAccount(completedForm); + verifyAccountFiatCurrency(paymentAccount, "BRL"); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_COUNTRY), + Objects.requireNonNull(paymentAccount.getCountry()).code); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_CITY), paymentAccount.getCity()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_CONTACT), paymentAccount.getContact()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_EXTRA_INFO), paymentAccount.getExtraInfo()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + } + + @Test + public void testHalCashAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(HAL_CASH_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + HAL_CASH_ID, + PROPERTY_NAME_MOBILE_NR); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, HAL_CASH_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "Hal Cash Acct"); + EXPECTED_FORM.put(PROPERTY_NAME_MOBILE_NR, "798 123 456"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + HalCashAccount paymentAccount = (HalCashAccount) paymentAccountForm.toPaymentAccount(completedForm); + verifyAccountFiatCurrency(paymentAccount, "EUR"); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_MOBILE_NR), paymentAccount.getMobileNr()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + } + + @Test + public void testJapanBankAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(JAPAN_BANK_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + JAPAN_BANK_ID, + PROPERTY_NAME_BANK_NAME, + PROPERTY_NAME_BANK_CODE, + PROPERTY_NAME_BANK_BRANCH_CODE, + PROPERTY_NAME_BANK_BRANCH_NAME, + PROPERTY_NAME_BANK_ACCOUNT_NAME, + PROPERTY_NAME_BANK_ACCOUNT_TYPE, + PROPERTY_NAME_BANK_ACCOUNT_NUMBER); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, JAPAN_BANK_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "Fukuoka Account"); + EXPECTED_FORM.put(PROPERTY_NAME_BANK_NAME, "Bank of Kyoto"); + EXPECTED_FORM.put(PROPERTY_NAME_BANK_CODE, "FKBKJPJT"); + EXPECTED_FORM.put(PROPERTY_NAME_BANK_BRANCH_CODE, "8100-8727"); + EXPECTED_FORM.put(PROPERTY_NAME_BANK_BRANCH_NAME, "Fukuoka Branch"); + EXPECTED_FORM.put(PROPERTY_NAME_BANK_ACCOUNT_NAME, "Fukuoka Account"); + EXPECTED_FORM.put(PROPERTY_NAME_BANK_ACCOUNT_TYPE, "Yen Account"); + EXPECTED_FORM.put(PROPERTY_NAME_BANK_ACCOUNT_NUMBER, "8100-8727-0000"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + JapanBankAccount paymentAccount = (JapanBankAccount) paymentAccountForm.toPaymentAccount(completedForm); + verifyAccountFiatCurrency(paymentAccount, "JPY"); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_CODE), paymentAccount.getBankCode()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_NAME), paymentAccount.getBankName()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_BRANCH_CODE), paymentAccount.getBankBranchCode()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_BRANCH_NAME), paymentAccount.getBankBranchName()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_ACCOUNT_NAME), paymentAccount.getBankAccountName()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_ACCOUNT_TYPE), paymentAccount.getBankAccountType()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BANK_ACCOUNT_NUMBER), paymentAccount.getBankAccountNumber()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + } + + + @Test + public void testSepaAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(SEPA_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + SEPA_ID, + PROPERTY_NAME_COUNTRY, + PROPERTY_NAME_HOLDER_NAME, + PROPERTY_NAME_IBAN, + PROPERTY_NAME_BIC); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, SEPA_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "Conta Sepa"); + EXPECTED_FORM.put(PROPERTY_NAME_COUNTRY, "PT"); + EXPECTED_FORM.put(PROPERTY_NAME_HOLDER_NAME, "Jose da Silva"); + EXPECTED_FORM.put(PROPERTY_NAME_IBAN, "909-909"); + EXPECTED_FORM.put(PROPERTY_NAME_BIC, "909"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + SepaAccount paymentAccount = (SepaAccount) paymentAccountForm.toPaymentAccount(completedForm); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_COUNTRY), Objects.requireNonNull(paymentAccount.getCountry()).code); + verifyAccountFiatCurrency(paymentAccount, "EUR"); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_IBAN), paymentAccount.getIban()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BIC), paymentAccount.getBic()); + // bankId == bic + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_BIC), paymentAccount.getBankId()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + } + + @Test + public void testUSPostalMoneyOrderAccountForm() { + File emptyForm = paymentAccountForm.getPaymentAccountForm(US_POSTAL_MONEY_ORDER_ID); + log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); + verifyEmptyForm(emptyForm, + US_POSTAL_MONEY_ORDER_ID, + PROPERTY_NAME_HOLDER_NAME, + PROPERTY_NAME_POSTAL_ADDRESS); + + EXPECTED_FORM.put(PROPERTY_NAME_PAYMENT_METHOD_ID, US_POSTAL_MONEY_ORDER_ID); + EXPECTED_FORM.put(PROPERTY_NAME_ACCOUNT_NAME, "Bubba's Acct"); + EXPECTED_FORM.put(PROPERTY_NAME_HOLDER_NAME, "Bubba"); + EXPECTED_FORM.put(PROPERTY_NAME_POSTAL_ADDRESS, "100 Westwood Terrace Austin, TX 78701"); + + File completedForm = fillPaymentAccountForm(); + log.info("Completed form: {}", paymentAccountForm.toJsonString(completedForm)); + + USPostalMoneyOrderAccount paymentAccount = (USPostalMoneyOrderAccount) paymentAccountForm.toPaymentAccount(completedForm); + verifyAccountFiatCurrency(paymentAccount, "USD"); + verifyCommonFormEntries(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName()); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_POSTAL_ADDRESS), paymentAccount.getPostalAddress()); + // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); + } + + + // Private + + private void verifyCommonFormEntries(PaymentAccount paymentAccount) { + assertNotNull(paymentAccount); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_PAYMENT_METHOD_ID), paymentAccount.getPaymentMethod().getId()); + assertTrue(paymentAccount.getCreationDate().getTime() > 0); + assertEquals(EXPECTED_FORM.get(PROPERTY_NAME_ACCOUNT_NAME), paymentAccount.getAccountName()); + } + + private void verifyEmptyForm(File jsonForm, String paymentMethodId, String... fields) { + @SuppressWarnings("unchecked") + Map emptyForm = (Map) gson.fromJson( + paymentAccountForm.toJsonString(jsonForm), + Object.class); + assertNotNull(emptyForm); + assertEquals(PROPERTY_VALUE_COMMENT, emptyForm.get(PROPERTY_NAME_COMMENT)); + assertEquals(paymentMethodId, emptyForm.get(PROPERTY_NAME_PAYMENT_METHOD_ID)); + assertEquals("Your accountname", emptyForm.get(PROPERTY_NAME_ACCOUNT_NAME)); + for (String field : fields) { + assertEquals("Your " + field.toLowerCase(), emptyForm.get(field)); + } + } + + private void verifyAccountFiatCurrency(PaymentAccount paymentAccount, String expectedCurrencyCode) { + assertEquals(expectedCurrencyCode, Objects.requireNonNull(paymentAccount.getSingleTradeCurrency()).getCode()); + } + + private File fillPaymentAccountForm() { + File f = new File(getProperty("java.io.tmpdir"), "tmp.json"); + try { + JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream(f), UTF_8)); + writer.beginObject(); + writer.name(PROPERTY_NAME_COMMENT); + writer.value(PROPERTY_VALUE_COMMENT); + for (Map.Entry entry : EXPECTED_FORM.entrySet()) { + String k = entry.getKey(); + Object v = entry.getValue(); + writer.name(k); + writer.value(v.toString()); + } + writer.endObject(); + writer.close(); + } catch (IOException ex) { + log.error("", ex); + fail(format("Could not write json file from form entries %s", EXPECTED_FORM)); + } + return f; + } +} From 61827afe49d1be6be5434399d3d68e708e84e087 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 5 Nov 2020 13:47:27 -0300 Subject: [PATCH 6/8] Fix import problem --- .../src/main/java/bisq/core/api/model/PaymentAccountForm.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java index 7afaf98abdb..836abd4e59e 100644 --- a/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java +++ b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java @@ -43,7 +43,7 @@ import lombok.extern.slf4j.Slf4j; -import static bisq.core.payment.payload.PaymentMethod.getPaymentMethodById; +import static bisq.core.payment.payload.PaymentMethod.*; import static com.google.common.base.Preconditions.checkNotNull; import static java.io.File.separatorChar; import static java.lang.String.format; @@ -129,7 +129,7 @@ private Class getPaymentAccountClassFromJson(String js } private Class getPaymentAccountClass(String paymentMethodId) { - PaymentMethod paymentMethod = PaymentMethod.getPaymentMethodById(paymentMethodId); + PaymentMethod paymentMethod = getPaymentMethodById(paymentMethodId); return PaymentAccountFactory.getPaymentAccount(paymentMethod).getClass(); } } From a2f8f1efe31995c2c53968e560e65c7b68d53545 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 5 Nov 2020 18:13:12 -0300 Subject: [PATCH 7/8] Remove extra white spaces, force travis ci builds --- .../test/java/bisq/core/api/model/PaymentAccountFormTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java b/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java index ceb5c319f2c..f315870fa72 100644 --- a/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java +++ b/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java @@ -108,7 +108,6 @@ public void setup() { EXPECTED_FORM.clear(); } - @Test public void testBrazilNationalBankAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(NATIONAL_BANK_ID); @@ -300,7 +299,6 @@ public void testJapanBankAccountForm() { // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); } - @Test public void testSepaAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(SEPA_ID); @@ -359,7 +357,6 @@ public void testUSPostalMoneyOrderAccountForm() { // log.info("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount); } - // Private private void verifyCommonFormEntries(PaymentAccount paymentAccount) { From 190656643b516440964737a1b343504359ecbafc Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Fri, 6 Nov 2020 13:07:08 -0300 Subject: [PATCH 8/8] Give all files created in tmp dirs a unique name Delete tmp files upon test jvm exit. --- .../core/api/model/PaymentAccountForm.java | 18 ++++++++++++----- .../api/model/PaymentAccountFormTest.java | 20 ++++++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java index 836abd4e59e..268d5ab9083 100644 --- a/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java +++ b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java @@ -43,9 +43,8 @@ import lombok.extern.slf4j.Slf4j; -import static bisq.core.payment.payload.PaymentMethod.*; +import static bisq.core.payment.payload.PaymentMethod.getPaymentMethodById; import static com.google.common.base.Preconditions.checkNotNull; -import static java.io.File.separatorChar; import static java.lang.String.format; import static java.lang.System.getProperty; import static java.nio.charset.StandardCharsets.UTF_8; @@ -78,9 +77,17 @@ public class PaymentAccountForm { public File getPaymentAccountForm(String paymentMethodId) { PaymentMethod paymentMethod = getPaymentMethodById(paymentMethodId); - File file = new File(getProperty("java.io.tmpdir") + separatorChar - + paymentMethod.getId().toLowerCase() + "_form.json"); - try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file, false), UTF_8)) { + + File file = null; + try { + file = File.createTempFile(paymentMethod.getId().toLowerCase() + "_form_", + ".json", + Paths.get(getProperty("java.io.tmpdir")).toFile()); + } catch (IOException ex) { + log.error("", ex); + } + + try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(checkNotNull(file), false), UTF_8)) { PaymentAccount paymentAccount = PaymentAccountFactory.getPaymentAccount(paymentMethod); Class clazz = paymentAccount.getClass(); Gson gson = gsonBuilder.registerTypeAdapter(clazz, new PaymentAccountTypeAdapter(clazz, excludedFields)).create(); @@ -89,6 +96,7 @@ public File getPaymentAccountForm(String paymentMethodId) { } catch (Exception ex) { log.error(format("Could not export json file for %s account.", paymentMethod.getShortName()), ex); } + return file; } diff --git a/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java b/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java index f315870fa72..611e366a477 100644 --- a/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java +++ b/core/src/test/java/bisq/core/api/model/PaymentAccountFormTest.java @@ -33,6 +33,8 @@ import com.google.gson.GsonBuilder; import com.google.gson.stream.JsonWriter; +import java.nio.file.Paths; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -111,6 +113,7 @@ public void setup() { @Test public void testBrazilNationalBankAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(NATIONAL_BANK_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, NATIONAL_BANK_ID, @@ -158,6 +161,7 @@ public void testBrazilNationalBankAccountForm() { @Test public void testChaseQuickPayAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(CHASE_QUICK_PAY_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, CHASE_QUICK_PAY_ID, @@ -183,6 +187,7 @@ public void testChaseQuickPayAccountForm() { @Test public void testClearXChangeAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(CLEAR_X_CHANGE_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, CLEAR_X_CHANGE_ID, @@ -208,6 +213,7 @@ public void testClearXChangeAccountForm() { @Test public void testF2FAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(F2F_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, F2F_ID, @@ -240,6 +246,7 @@ public void testF2FAccountForm() { @Test public void testHalCashAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(HAL_CASH_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, HAL_CASH_ID, @@ -262,6 +269,7 @@ public void testHalCashAccountForm() { @Test public void testJapanBankAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(JAPAN_BANK_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, JAPAN_BANK_ID, @@ -302,6 +310,7 @@ public void testJapanBankAccountForm() { @Test public void testSepaAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(SEPA_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, SEPA_ID, @@ -335,6 +344,7 @@ public void testSepaAccountForm() { @Test public void testUSPostalMoneyOrderAccountForm() { File emptyForm = paymentAccountForm.getPaymentAccountForm(US_POSTAL_MONEY_ORDER_ID); + emptyForm.deleteOnExit(); log.info("Empty form saved to {}", paymentAccountForm.getClickableURI(emptyForm)); verifyEmptyForm(emptyForm, US_POSTAL_MONEY_ORDER_ID, @@ -385,9 +395,13 @@ private void verifyAccountFiatCurrency(PaymentAccount paymentAccount, String exp } private File fillPaymentAccountForm() { - File f = new File(getProperty("java.io.tmpdir"), "tmp.json"); + File tmpJsonForm = null; try { - JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream(f), UTF_8)); + tmpJsonForm = File.createTempFile("temp_acct_form_", + ".json", + Paths.get(getProperty("java.io.tmpdir")).toFile()); + tmpJsonForm.deleteOnExit(); + JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream(tmpJsonForm), UTF_8)); writer.beginObject(); writer.name(PROPERTY_NAME_COMMENT); writer.value(PROPERTY_VALUE_COMMENT); @@ -403,6 +417,6 @@ private File fillPaymentAccountForm() { log.error("", ex); fail(format("Could not write json file from form entries %s", EXPECTED_FORM)); } - return f; + return tmpJsonForm; } }