fineract-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From al...@apache.org
Subject [fineract] branch develop updated: client: GSON TypeAdapter<LocalDate> Fineract API Format [2009, 1, 1] (FINERACT-1220)
Date Wed, 21 Oct 2020 12:42:21 GMT
This is an automated email from the ASF dual-hosted git repository.

aleks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new b75ced5  client: GSON TypeAdapter<LocalDate> Fineract API Format [2009,1,1]
(FINERACT-1220)
b75ced5 is described below

commit b75ced50dfbc5e49faa4de2195446e1a01f19ce9
Author: Michael Vorburger <mike@vorburger.ch>
AuthorDate: Tue Oct 20 23:30:17 2020 +0200

    client: GSON TypeAdapter<LocalDate> Fineract API Format [2009,1,1] (FINERACT-1220)
---
 .../fineract/client/util/FineractClient.java       | 250 +++++++++---------
 .../java/org/apache/fineract/client/util/JSON.java | 288 +++++++++++++++++++++
 .../fineract/client/test/FineractClientTest.java   |   8 +-
 3 files changed, 425 insertions(+), 121 deletions(-)

diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
index c062cef..41c77b9 100644
--- a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
+++ b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
@@ -28,6 +28,7 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
+import okhttp3.OkHttpClient;
 import okhttp3.logging.HttpLoggingInterceptor;
 import okhttp3.logging.HttpLoggingInterceptor.Level;
 import org.apache.fineract.client.ApiClient;
@@ -136,6 +137,9 @@ import org.apache.fineract.client.services.TellerCashManagementApi;
 import org.apache.fineract.client.services.UserGeneratedDocumentsApi;
 import org.apache.fineract.client.services.UsersApi;
 import org.apache.fineract.client.services.WorkingDaysApi;
+import org.apache.fineract.client.util.JSON.GsonCustomConverterFactory;
+import retrofit2.Retrofit;
+import retrofit2.converter.scalars.ScalarsConverterFactory;
 
 /**
  * Fineract Client Java SDK API entry point. Use this instead of the {@link ApiClient}.
@@ -144,7 +148,7 @@ import org.apache.fineract.client.services.WorkingDaysApi;
  */
 public final class FineractClient {
 
-    private final ApiClient api;
+    private final Retrofit retrofit;
 
     public final AccountingClosureApi glClosures;
     public final AccountingRulesApi accountingRules;
@@ -250,125 +254,135 @@ public final class FineractClient {
     public final UsersApi users;
     public final WorkingDaysApi workingDays;
 
-    private FineractClient(ApiClient apiClient) {
-        api = apiClient;
+    private FineractClient(Retrofit retrofit) {
+        this.retrofit = retrofit;
 
-        glClosures = apiClient.createService(AccountingClosureApi.class);
-        accountingRules = apiClient.createService(AccountingRulesApi.class);
-        accountNumberFormats = apiClient.createService(AccountNumberFormatApi.class);
-        accountTransfers = apiClient.createService(AccountTransfersApi.class);
-        adhocQuery = apiClient.createService(AdhocQueryApiApi.class);
-        audits = apiClient.createService(AuditsApi.class);
-        authentication = apiClient.createService(AuthenticationHttpBasicApi.class);
-        batches = apiClient.createService(BatchApiApi.class);
-        caches = apiClient.createService(CacheApi.class);
-        cashiersJournal = apiClient.createService(CashierJournalsApi.class);
-        cashiers = apiClient.createService(CashiersApi.class);
-        centers = apiClient.createService(CentersApi.class);
-        charges = apiClient.createService(ChargesApi.class);
-        clients = apiClient.createService(ClientApi.class);
-        clientCharges = apiClient.createService(ClientChargesApi.class);
-        clientIdentifiers = apiClient.createService(ClientIdentifierApi.class);
-        clientAddresses = apiClient.createService(ClientsAddressApi.class);
-        clientTransactions = apiClient.createService(ClientTransactionApi.class);
-        codes = apiClient.createService(CodesApi.class);
-        codeValues = apiClient.createService(CodeValuesApi.class);
-        currencies = apiClient.createService(CurrencyApi.class);
-        dataTables = apiClient.createService(DataTablesApi.class);
-        legacy = apiClient.createService(DefaultApi.class);
-        documents = apiClient.createService(DocumentsApi.class);
-        entityDatatableChecks = apiClient.createService(EntityDataTableApi.class);
-        entityFieldConfigurations = apiClient.createService(EntityFieldConfigurationApi.class);
-        externalServices = apiClient.createService(ExternalServicesApi.class);
-        userDetails = apiClient.createService(FetchAuthenticatedUserDetailsApi.class);
-        fixedDepositAccounts = apiClient.createService(FixedDepositAccountApi.class);
-        fixedDepositProducts = apiClient.createService(FixedDepositProductApi.class);
-        floatingRates = apiClient.createService(FloatingRatesApi.class);
-        glAccounts = apiClient.createService(GeneralLedgerAccountApi.class);
-        globalConfigurations = apiClient.createService(GlobalConfigurationApi.class);
-        groups = apiClient.createService(GroupsApi.class);
-        holidays = apiClient.createService(HolidaysApi.class);
-        hooks = apiClient.createService(HooksApi.class);
-        interestRateCharts = apiClient.createService(InterestRateChartApi.class);
-        interestRateChartLabs = apiClient.createService(InterestRateSlabAKAInterestBandsApi.class);
-        journalEntries = apiClient.createService(JournalEntriesApi.class);
-        reportMailings = apiClient.createService(ListReportMailingJobHistoryApi.class);
-        loanCharges = apiClient.createService(LoanChargesApi.class);
-        loanCollaterals = apiClient.createService(LoanCollateralApi.class);
-        loanProducts = apiClient.createService(LoanProductsApi.class);
-        loanSchedules = apiClient.createService(LoanReschedulingApi.class);
-        loans = apiClient.createService(LoansApi.class);
-        loanTransactions = apiClient.createService(LoanTransactionsApi.class);
-        makerCheckers = apiClient.createService(MakerCheckerOr4EyeFunctionalityApi.class);
-        financialActivyAccountMappings = apiClient.createService(MappingFinancialActivitiesToAccountsApi.class);
-        jobs = apiClient.createService(MifosxBatchJobsApi.class);
-        mixMappings = apiClient.createService(MixMappingApi.class);
-        mixReports = apiClient.createService(MixReportApi.class);
-        mixTaxonomies = apiClient.createService(MixTaxonomyApi.class);
-        notes = apiClient.createService(NotesApi.class);
-        notifications = apiClient.createService(NotificationApi.class);
-        offices = apiClient.createService(OfficesApi.class);
-        passwordPreferences = apiClient.createService(PasswordPreferencesApi.class);
-        paymentTypes = apiClient.createService(PaymentTypeApi.class);
-        periodicAccrualAccounting = apiClient.createService(PeriodicAccrualAccountingApi.class);
-        permissions = apiClient.createService(PermissionsApi.class);
-        selfPockets = apiClient.createService(PocketApi.class);
-        provisioningCategories = apiClient.createService(ProvisioningCategoryApi.class);
-        provisioningCriterias = apiClient.createService(ProvisioningCriteriaApi.class);
-        provisioningEntries = apiClient.createService(ProvisioningEntriesApi.class);
-        recurringDepositAccounts = apiClient.createService(RecurringDepositAccountApi.class);
-        recurringDepositAccountTransactions = apiClient.createService(RecurringDepositAccountTransactionsApi.class);
-        recurringDepositProducts = apiClient.createService(RecurringDepositProductApi.class);
-        reportMailingJobs = apiClient.createService(ReportMailingJobsApi.class);
-        reports = apiClient.createService(ReportsApi.class);
-        roles = apiClient.createService(RolesApi.class);
-        reportsRun = apiClient.createService(RunReportsApi.class);
-        savingsAccounts = apiClient.createService(SavingsAccountApi.class);
-        savingsAccountCharges = apiClient.createService(SavingsChargesApi.class);
-        savingsProducts = apiClient.createService(SavingsProductApi.class);
-        jobsScheduler = apiClient.createService(SchedulerApi.class);
-        surveyScorecards = apiClient.createService(ScoreCardApi.class);
-        search = apiClient.createService(SearchApiApi.class);
-        selfAccountTransfers = apiClient.createService(SelfAccountTransferApi.class);
-        selfAuthentication = apiClient.createService(SelfAuthenticationApi.class);
-        selfClients = apiClient.createService(SelfClientApi.class);
-        selfShareProducts = apiClient.createService(SelfDividendApi.class);
-        selfLoanProducts = apiClient.createService(SelfLoanProductsApi.class);
-        selfLoans = apiClient.createService(SelfLoansApi.class);
-        selfReportsRun = apiClient.createService(SelfRunReportApi.class);
-        selfSavingsAccounts = apiClient.createService(SelfSavingsAccountApi.class);
-        selfSurveyScorecards = apiClient.createService(SelfScoreCardApi.class);
-        selfRegistration = apiClient.createService(SelfServiceRegistrationApi.class);
-        selfShareAccounts = apiClient.createService(SelfShareAccountsApi.class);
-        selfSurveys = apiClient.createService(SelfSpmApi.class);
-        selfThirdPartyBeneficiaries = apiClient.createService(SelfThirdPartyTransferApi.class);
-        selfUser = apiClient.createService(SelfUserApi.class);
-        selfUserDetails = apiClient.createService(SelfUserDetailsApi.class);
-        shareAccounts = apiClient.createService(ShareAccountApi.class);
-        surveyLookupTables = apiClient.createService(SpmApiLookUpTableApi.class);
-        surveys = apiClient.createService(SpmSurveysApi.class);
-        staff = apiClient.createService(StaffApi.class);
-        standingInstructions = apiClient.createService(StandingInstructionsApi.class);
-        standingInstructionsHistory = apiClient.createService(StandingInstructionsHistoryApi.class);
-        taxComponents = apiClient.createService(TaxComponentsApi.class);
-        taxGroups = apiClient.createService(TaxGroupApi.class);
-        tellers = apiClient.createService(TellerCashManagementApi.class);
-        templates = apiClient.createService(UserGeneratedDocumentsApi.class);
-        users = apiClient.createService(UsersApi.class);
-        workingDays = apiClient.createService(WorkingDaysApi.class);
+        glClosures = retrofit.create(AccountingClosureApi.class);
+        accountingRules = retrofit.create(AccountingRulesApi.class);
+        accountNumberFormats = retrofit.create(AccountNumberFormatApi.class);
+        accountTransfers = retrofit.create(AccountTransfersApi.class);
+        adhocQuery = retrofit.create(AdhocQueryApiApi.class);
+        audits = retrofit.create(AuditsApi.class);
+        authentication = retrofit.create(AuthenticationHttpBasicApi.class);
+        batches = retrofit.create(BatchApiApi.class);
+        caches = retrofit.create(CacheApi.class);
+        cashiersJournal = retrofit.create(CashierJournalsApi.class);
+        cashiers = retrofit.create(CashiersApi.class);
+        centers = retrofit.create(CentersApi.class);
+        charges = retrofit.create(ChargesApi.class);
+        clients = retrofit.create(ClientApi.class);
+        clientCharges = retrofit.create(ClientChargesApi.class);
+        clientIdentifiers = retrofit.create(ClientIdentifierApi.class);
+        clientAddresses = retrofit.create(ClientsAddressApi.class);
+        clientTransactions = retrofit.create(ClientTransactionApi.class);
+        codes = retrofit.create(CodesApi.class);
+        codeValues = retrofit.create(CodeValuesApi.class);
+        currencies = retrofit.create(CurrencyApi.class);
+        dataTables = retrofit.create(DataTablesApi.class);
+        legacy = retrofit.create(DefaultApi.class);
+        documents = retrofit.create(DocumentsApi.class);
+        entityDatatableChecks = retrofit.create(EntityDataTableApi.class);
+        entityFieldConfigurations = retrofit.create(EntityFieldConfigurationApi.class);
+        externalServices = retrofit.create(ExternalServicesApi.class);
+        userDetails = retrofit.create(FetchAuthenticatedUserDetailsApi.class);
+        fixedDepositAccounts = retrofit.create(FixedDepositAccountApi.class);
+        fixedDepositProducts = retrofit.create(FixedDepositProductApi.class);
+        floatingRates = retrofit.create(FloatingRatesApi.class);
+        glAccounts = retrofit.create(GeneralLedgerAccountApi.class);
+        globalConfigurations = retrofit.create(GlobalConfigurationApi.class);
+        groups = retrofit.create(GroupsApi.class);
+        holidays = retrofit.create(HolidaysApi.class);
+        hooks = retrofit.create(HooksApi.class);
+        interestRateCharts = retrofit.create(InterestRateChartApi.class);
+        interestRateChartLabs = retrofit.create(InterestRateSlabAKAInterestBandsApi.class);
+        journalEntries = retrofit.create(JournalEntriesApi.class);
+        reportMailings = retrofit.create(ListReportMailingJobHistoryApi.class);
+        loanCharges = retrofit.create(LoanChargesApi.class);
+        loanCollaterals = retrofit.create(LoanCollateralApi.class);
+        loanProducts = retrofit.create(LoanProductsApi.class);
+        loanSchedules = retrofit.create(LoanReschedulingApi.class);
+        loans = retrofit.create(LoansApi.class);
+        loanTransactions = retrofit.create(LoanTransactionsApi.class);
+        makerCheckers = retrofit.create(MakerCheckerOr4EyeFunctionalityApi.class);
+        financialActivyAccountMappings = retrofit.create(MappingFinancialActivitiesToAccountsApi.class);
+        jobs = retrofit.create(MifosxBatchJobsApi.class);
+        mixMappings = retrofit.create(MixMappingApi.class);
+        mixReports = retrofit.create(MixReportApi.class);
+        mixTaxonomies = retrofit.create(MixTaxonomyApi.class);
+        notes = retrofit.create(NotesApi.class);
+        notifications = retrofit.create(NotificationApi.class);
+        offices = retrofit.create(OfficesApi.class);
+        passwordPreferences = retrofit.create(PasswordPreferencesApi.class);
+        paymentTypes = retrofit.create(PaymentTypeApi.class);
+        periodicAccrualAccounting = retrofit.create(PeriodicAccrualAccountingApi.class);
+        permissions = retrofit.create(PermissionsApi.class);
+        selfPockets = retrofit.create(PocketApi.class);
+        provisioningCategories = retrofit.create(ProvisioningCategoryApi.class);
+        provisioningCriterias = retrofit.create(ProvisioningCriteriaApi.class);
+        provisioningEntries = retrofit.create(ProvisioningEntriesApi.class);
+        recurringDepositAccounts = retrofit.create(RecurringDepositAccountApi.class);
+        recurringDepositAccountTransactions = retrofit.create(RecurringDepositAccountTransactionsApi.class);
+        recurringDepositProducts = retrofit.create(RecurringDepositProductApi.class);
+        reportMailingJobs = retrofit.create(ReportMailingJobsApi.class);
+        reports = retrofit.create(ReportsApi.class);
+        roles = retrofit.create(RolesApi.class);
+        reportsRun = retrofit.create(RunReportsApi.class);
+        savingsAccounts = retrofit.create(SavingsAccountApi.class);
+        savingsAccountCharges = retrofit.create(SavingsChargesApi.class);
+        savingsProducts = retrofit.create(SavingsProductApi.class);
+        jobsScheduler = retrofit.create(SchedulerApi.class);
+        surveyScorecards = retrofit.create(ScoreCardApi.class);
+        search = retrofit.create(SearchApiApi.class);
+        selfAccountTransfers = retrofit.create(SelfAccountTransferApi.class);
+        selfAuthentication = retrofit.create(SelfAuthenticationApi.class);
+        selfClients = retrofit.create(SelfClientApi.class);
+        selfShareProducts = retrofit.create(SelfDividendApi.class);
+        selfLoanProducts = retrofit.create(SelfLoanProductsApi.class);
+        selfLoans = retrofit.create(SelfLoansApi.class);
+        selfReportsRun = retrofit.create(SelfRunReportApi.class);
+        selfSavingsAccounts = retrofit.create(SelfSavingsAccountApi.class);
+        selfSurveyScorecards = retrofit.create(SelfScoreCardApi.class);
+        selfRegistration = retrofit.create(SelfServiceRegistrationApi.class);
+        selfShareAccounts = retrofit.create(SelfShareAccountsApi.class);
+        selfSurveys = retrofit.create(SelfSpmApi.class);
+        selfThirdPartyBeneficiaries = retrofit.create(SelfThirdPartyTransferApi.class);
+        selfUser = retrofit.create(SelfUserApi.class);
+        selfUserDetails = retrofit.create(SelfUserDetailsApi.class);
+        shareAccounts = retrofit.create(ShareAccountApi.class);
+        surveyLookupTables = retrofit.create(SpmApiLookUpTableApi.class);
+        surveys = retrofit.create(SpmSurveysApi.class);
+        staff = retrofit.create(StaffApi.class);
+        standingInstructions = retrofit.create(StandingInstructionsApi.class);
+        standingInstructionsHistory = retrofit.create(StandingInstructionsHistoryApi.class);
+        taxComponents = retrofit.create(TaxComponentsApi.class);
+        taxGroups = retrofit.create(TaxGroupApi.class);
+        tellers = retrofit.create(TellerCashManagementApi.class);
+        templates = retrofit.create(UserGeneratedDocumentsApi.class);
+        users = retrofit.create(UsersApi.class);
+        workingDays = retrofit.create(WorkingDaysApi.class);
     }
 
     public static Builder builder() {
         return new Builder();
     }
 
+    /**
+     * Create an implementation of the API endpoints defined by the {@code service} interface,
using
+     * {@link Retrofit#create(Class)}. This method is typically not required to be invoked
for standard API usage, but
+     * can be a handy back door for non-trivial advanced customizations of the API client
if you have extended Fineract
+     * with your own REST APIs.
+     */
     public <S> S createService(Class<S> serviceClass) {
-        return api.createService(serviceClass);
+        return retrofit.create(serviceClass);
     }
 
     public static final class Builder {
 
-        private final ApiClient apiClient = new ApiClient();
+        private final JSON json = new JSON();
+        private final OkHttpClient.Builder okBuilder = new OkHttpClient.Builder();
+        private final Retrofit.Builder retrofitBuilder = new Retrofit.Builder().addConverterFactory(ScalarsConverterFactory.create())
+                .addConverterFactory(GsonCustomConverterFactory.create(json.getGson()));
+
         private String baseURL;
         private String tenant;
         private String username;
@@ -395,7 +409,7 @@ public final class FineractClient {
         public Builder logging(Level level) {
             HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
             logging.setLevel(level);
-            apiClient.getOkBuilder().addInterceptor(logging);
+            okBuilder.addInterceptor(logging);
             return this;
         }
 
@@ -411,7 +425,7 @@ public final class FineractClient {
             // org.apache.fineract.infrastructure.hooks.processor.ProcessorHelper
             if (insecure) {
                 HostnameVerifier insecureHostnameVerifier = (hostname, session) -> true;
-                apiClient.getOkBuilder().hostnameVerifier(insecureHostnameVerifier);
+                okBuilder.hostnameVerifier(insecureHostnameVerifier);
 
                 try {
                     X509TrustManager insecureX509TrustManager = new X509TrustManager() {
@@ -432,7 +446,7 @@ public final class FineractClient {
                     sslContext.init(null, new TrustManager[] { insecureX509TrustManager },
new SecureRandom());
                     SSLSocketFactory insecureSslSocketFactory = sslContext.getSocketFactory();
 
-                    apiClient.getOkBuilder().sslSocketFactory(insecureSslSocketFactory, insecureX509TrustManager);
+                    okBuilder.sslSocketFactory(insecureSslSocketFactory, insecureX509TrustManager);
                 } catch (NoSuchAlgorithmException | KeyManagementException e) {
                     throw new IllegalStateException("insecure() SSL configuration failed",
e);
                 }
@@ -442,19 +456,21 @@ public final class FineractClient {
 
         public FineractClient build() {
             // URL
-            apiClient.getAdapterBuilder().baseUrl(has("baseURL", baseURL));
+            retrofitBuilder.baseUrl(has("baseURL", baseURL));
 
             // Tenant
             ApiKeyAuth tenantAuth = new ApiKeyAuth("header", "fineract-platform-tenantid");
             tenantAuth.setApiKey(has("tenant", tenant));
-            apiClient.addAuthorization("tenantid", tenantAuth);
+            okBuilder.addInterceptor(tenantAuth);
 
             // BASIC Auth
             HttpBasicAuth basicAuth = new HttpBasicAuth();
             basicAuth.setCredentials(has("username", username), has("password", password));
-            apiClient.addAuthorization("basicAuth", basicAuth);
+            okBuilder.addInterceptor(basicAuth);
+
+            retrofitBuilder.client(okBuilder.build());
 
-            return new FineractClient(apiClient);
+            return new FineractClient(retrofitBuilder.build());
         }
 
         /**
@@ -464,7 +480,7 @@ public final class FineractClient {
          * @return the {@link ApiClient} which {@link #build()} will use.
          */
         public retrofit2.Retrofit.Builder getRetrofitBuilder() {
-            return apiClient.getAdapterBuilder();
+            return retrofitBuilder;
         }
 
         /**
@@ -474,7 +490,7 @@ public final class FineractClient {
          * @return the {@link ApiClient} which {@link #build()} will use.
          */
         public okhttp3.OkHttpClient.Builder getOkBuilder() {
-            return apiClient.getOkBuilder();
+            return okBuilder;
         }
 
         private <T> T has(String propertyName, T value) throws IllegalStateException
{
diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
new file mode 100644
index 0000000..034c8b3
--- /dev/null
+++ b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
@@ -0,0 +1,288 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.client.util;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonParseException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.internal.bind.util.ISO8601Utils;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import io.gsonfire.GsonFireBuilder;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import okhttp3.RequestBody;
+import okhttp3.ResponseBody;
+import retrofit2.Converter;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+// This class was originally generated by OpenAPI Generator (https://openapi-generator.tech),
+// but then had to be manually edited by Michael Vorburger.ch to fix https://issues.apache.org/jira/browse/FINERACT-1220.
+// It is intentionally NOT public, as it's considered an "implementation detail".
+// The original JSON class is deleted during the build (see FINERACT-1231).
+class JSON {
+
+    private final Gson gson;
+    private final DateTypeAdapter dateTypeAdapter = new DateTypeAdapter();
+    private final SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter();
+    private final OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter();
+    private final LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter();
+
+    JSON() {
+        gson = new GsonFireBuilder().createGsonBuilder().registerTypeAdapter(Date.class,
dateTypeAdapter)
+                .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter)
+                .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter)
+                .registerTypeAdapter(LocalDate.class, localDateTypeAdapter).create();
+    }
+
+    Gson getGson() {
+        return gson;
+    }
+
+    /**
+     * GSON TypeAdapter for JSR-310 OffsetDateTime type.
+     */
+    public static class OffsetDateTimeTypeAdapter extends TypeAdapter<OffsetDateTime>
{
+
+        private final DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+
+        @Override
+        public void write(JsonWriter out, OffsetDateTime date) throws IOException {
+            if (date == null) {
+                out.nullValue();
+            } else {
+                out.value(formatter.format(date));
+            }
+        }
+
+        @Override
+        public OffsetDateTime read(JsonReader in) throws IOException {
+            switch (in.peek()) {
+                case NULL:
+                    in.nextNull();
+                    return null;
+                default:
+                    String date = in.nextString();
+                    if (date.endsWith("+0000")) {
+                        date = date.substring(0, date.length() - 5) + "Z";
+                    }
+                    return OffsetDateTime.parse(date, formatter);
+            }
+        }
+    }
+
+    /**
+     * GSON TypeAdapter for JSR-310 LocalDate type in Fineract API Format (<tt>[2009,1,1]</tt>).
+     */
+    public static class LocalDateTypeAdapter extends TypeAdapter<LocalDate> {
+
+        // Now unused: private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
+
+        @Override
+        public void write(JsonWriter out, LocalDate date) throws IOException {
+            if (date == null) {
+                out.nullValue();
+            } else {
+                out.beginArray();
+                out.value(date.getYear());
+                out.value(date.getMonthValue());
+                out.value(date.getDayOfMonth());
+                out.endArray();
+            }
+        }
+
+        @Override
+        public LocalDate read(JsonReader in) throws IOException {
+            switch (in.peek()) {
+                case NULL:
+                    in.nextNull();
+                    return null;
+                case BEGIN_ARRAY:
+                    in.beginArray();
+                    int year = in.nextInt();
+                    int month = in.nextInt();
+                    int day = in.nextInt();
+                    in.endArray();
+                    return LocalDate.of(year, month, day);
+                default:
+                    throw new JsonParseException(
+                            "Fineract's API normally always sends LocalDate as e.g. [2009,1,1]..
or does it not?! (FINERACT-1220)");
+            }
+        }
+    }
+
+    /**
+     * GSON TypeAdapter for java.sql.Date type. If the dateFormat is null, a simple "yyyy-MM-dd"
format will be used
+     * (more efficient than SimpleDateFormat).
+     */
+    public static class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
+
+        private final DateFormat dateFormat = null; // TODO FINERACT-1220
+
+        @Override
+        public void write(JsonWriter out, java.sql.Date date) throws IOException {
+            if (date == null) {
+                out.nullValue();
+            } else {
+                String value;
+                if (dateFormat != null) {
+                    value = dateFormat.format(date);
+                } else {
+                    value = date.toString();
+                }
+                out.value(value);
+            }
+        }
+
+        @Override
+        public java.sql.Date read(JsonReader in) throws IOException {
+            switch (in.peek()) {
+                case NULL:
+                    in.nextNull();
+                    return null;
+                default:
+                    String date = in.nextString();
+                    try {
+                        if (dateFormat != null) {
+                            return new java.sql.Date(dateFormat.parse(date).getTime());
+                        }
+                        return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime());
+                    } catch (ParseException e) {
+                        throw new JsonParseException(e);
+                    }
+            }
+        }
+    }
+
+    /**
+     * GSON TypeAdapter for java.util.Date type. If the dateFormat is null, ISO8601Utils
will be used.
+     */
+    public static class DateTypeAdapter extends TypeAdapter<Date> {
+
+        private final DateFormat dateFormat = null; // TODO FINERACT-1220
+
+        @Override
+        public void write(JsonWriter out, Date date) throws IOException {
+            if (date == null) {
+                out.nullValue();
+            } else {
+                String value;
+                if (dateFormat != null) {
+                    value = dateFormat.format(date);
+                } else {
+                    value = ISO8601Utils.format(date, true);
+                }
+                out.value(value);
+            }
+        }
+
+        @Override
+        public Date read(JsonReader in) throws IOException {
+            try {
+                switch (in.peek()) {
+                    case NULL:
+                        in.nextNull();
+                        return null;
+                    default:
+                        String date = in.nextString();
+                        try {
+                            if (dateFormat != null) {
+                                return dateFormat.parse(date);
+                            }
+                            return ISO8601Utils.parse(date, new ParsePosition(0));
+                        } catch (ParseException e) {
+                            throw new JsonParseException(e);
+                        }
+                }
+            } catch (IllegalArgumentException e) {
+                throw new JsonParseException(e);
+            }
+        }
+    }
+
+    // The following is not from the generated JSON class, but from the original generated
ApiClient.
+    // The original class ApiClass, just like the JSON class, is deleted during the build
(see FINERACT-1231).
+
+    /**
+     * This wrapper is to take care of this case: when the deserialization fails due to JsonParseException
and the
+     * expected type is String, then just return the body string.
+     */
+    public static class GsonResponseBodyConverterToString<T> implements Converter<ResponseBody,
T> {
+
+        private final Gson gson;
+        private final Type type;
+
+        GsonResponseBodyConverterToString(Gson gson, Type type) {
+            this.gson = gson;
+            this.type = type;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public T convert(ResponseBody value) throws IOException {
+            String returned = value.string();
+            try {
+                return gson.fromJson(returned, type);
+            } catch (JsonParseException e) {
+                return (T) returned;
+            }
+        }
+    }
+
+    public static final class GsonCustomConverterFactory extends Converter.Factory {
+
+        private final Gson gson;
+        private final GsonConverterFactory gsonConverterFactory;
+
+        public static GsonCustomConverterFactory create(Gson gson) {
+            return new GsonCustomConverterFactory(gson);
+        }
+
+        private GsonCustomConverterFactory(Gson gson) {
+            if (gson == null) {
+                throw new NullPointerException("gson == null");
+            }
+            this.gson = gson;
+            this.gsonConverterFactory = GsonConverterFactory.create(gson);
+        }
+
+        @Override
+        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[]
annotations, Retrofit retrofit) {
+            if (type.equals(String.class)) {
+                return new GsonResponseBodyConverterToString<>(gson, type);
+            }
+            return gsonConverterFactory.responseBodyConverter(type, annotations, retrofit);
+        }
+
+        @Override
+        public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[]
parameterAnnotations, Annotation[] methodAnnotations,
+                Retrofit retrofit) {
+            return gsonConverterFactory.requestBodyConverter(type, parameterAnnotations,
methodAnnotations, retrofit);
+        }
+    }
+}
diff --git a/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientTest.java
b/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientTest.java
index 34e0ed3..9afb89a 100644
--- a/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientTest.java
+++ b/fineract-client/src/test/java/org/apache/fineract/client/test/FineractClientTest.java
@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
 import static org.apache.fineract.client.util.Calls.ok;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import okhttp3.logging.HttpLoggingInterceptor.Level;
 import org.apache.fineract.client.util.FineractClient;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -41,7 +42,7 @@ public class FineractClientTest {
     @Test
     void testRetrieveAllClientsFromFineractDev() {
         FineractClient fineract = FineractClient.builder().baseURL("https://demo.fineract.dev/fineract-provider/api/v1/").tenant("default")
-                .basicAuth("mifos", "password").build();
+                .basicAuth("mifos", "password").logging(Level.BODY).build();
         checkClients(fineract);
     }
 
@@ -53,11 +54,10 @@ public class FineractClientTest {
         checkClients(fineract);
     }
 
-    @Test
-    @Disabled // TODO FINERACT-1220
+    @Test // FINERACT-1220
     void testOfficesDateFormat() {
         FineractClient fineract = FineractClient.builder().baseURL("https://demo.fineract.dev/fineract-provider/api/v1/").tenant("default")
-                .basicAuth("mifos", "password").insecure(true).build();
+                .basicAuth("mifos", "password").insecure(true).logging(Level.BODY).build();
         ok(fineract.offices.retrieveOffices(true, null, null));
     }
 


Mime
View raw message