blob: 5d18a54f1a9e9b726b54b14c46ea320f6a95c420 [file] [log] [blame]
/**
* 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 java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
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.auth.ApiKeyAuth;
import org.apache.fineract.client.auth.HttpBasicAuth;
import org.apache.fineract.client.services.AccountNumberFormatApi;
import org.apache.fineract.client.services.AccountTransfersApi;
import org.apache.fineract.client.services.AccountingClosureApi;
import org.apache.fineract.client.services.AccountingRulesApi;
import org.apache.fineract.client.services.AdhocQueryApiApi;
import org.apache.fineract.client.services.AuditsApi;
import org.apache.fineract.client.services.AuthenticationHttpBasicApi;
import org.apache.fineract.client.services.BatchApiApi;
import org.apache.fineract.client.services.CacheApi;
import org.apache.fineract.client.services.CashierJournalsApi;
import org.apache.fineract.client.services.CashiersApi;
import org.apache.fineract.client.services.CentersApi;
import org.apache.fineract.client.services.ChargesApi;
import org.apache.fineract.client.services.ClientApi;
import org.apache.fineract.client.services.ClientChargesApi;
import org.apache.fineract.client.services.ClientIdentifierApi;
import org.apache.fineract.client.services.ClientTransactionApi;
import org.apache.fineract.client.services.ClientsAddressApi;
import org.apache.fineract.client.services.CodeValuesApi;
import org.apache.fineract.client.services.CodesApi;
import org.apache.fineract.client.services.CurrencyApi;
import org.apache.fineract.client.services.DataTablesApi;
import org.apache.fineract.client.services.DefaultApi;
import org.apache.fineract.client.services.DocumentsApiFixed;
import org.apache.fineract.client.services.EntityDataTableApi;
import org.apache.fineract.client.services.EntityFieldConfigurationApi;
import org.apache.fineract.client.services.ExternalServicesApi;
import org.apache.fineract.client.services.FetchAuthenticatedUserDetailsApi;
import org.apache.fineract.client.services.FixedDepositAccountApi;
import org.apache.fineract.client.services.FixedDepositProductApi;
import org.apache.fineract.client.services.FloatingRatesApi;
import org.apache.fineract.client.services.GeneralLedgerAccountApi;
import org.apache.fineract.client.services.GlobalConfigurationApi;
import org.apache.fineract.client.services.GroupsApi;
import org.apache.fineract.client.services.HolidaysApi;
import org.apache.fineract.client.services.HooksApi;
import org.apache.fineract.client.services.ImagesApi;
import org.apache.fineract.client.services.InterestRateChartApi;
import org.apache.fineract.client.services.InterestRateSlabAKAInterestBandsApi;
import org.apache.fineract.client.services.JournalEntriesApi;
import org.apache.fineract.client.services.ListReportMailingJobHistoryApi;
import org.apache.fineract.client.services.LoanChargesApi;
import org.apache.fineract.client.services.LoanCollateralApi;
import org.apache.fineract.client.services.LoanProductsApi;
import org.apache.fineract.client.services.LoanReschedulingApi;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.services.MakerCheckerOr4EyeFunctionalityApi;
import org.apache.fineract.client.services.MappingFinancialActivitiesToAccountsApi;
import org.apache.fineract.client.services.MifosxBatchJobsApi;
import org.apache.fineract.client.services.MixMappingApi;
import org.apache.fineract.client.services.MixReportApi;
import org.apache.fineract.client.services.MixTaxonomyApi;
import org.apache.fineract.client.services.NotesApi;
import org.apache.fineract.client.services.NotificationApi;
import org.apache.fineract.client.services.OfficesApi;
import org.apache.fineract.client.services.PasswordPreferencesApi;
import org.apache.fineract.client.services.PaymentTypeApi;
import org.apache.fineract.client.services.PeriodicAccrualAccountingApi;
import org.apache.fineract.client.services.PermissionsApi;
import org.apache.fineract.client.services.PocketApi;
import org.apache.fineract.client.services.ProvisioningCategoryApi;
import org.apache.fineract.client.services.ProvisioningCriteriaApi;
import org.apache.fineract.client.services.ProvisioningEntriesApi;
import org.apache.fineract.client.services.RecurringDepositAccountApi;
import org.apache.fineract.client.services.RecurringDepositAccountTransactionsApi;
import org.apache.fineract.client.services.RecurringDepositProductApi;
import org.apache.fineract.client.services.ReportMailingJobsApi;
import org.apache.fineract.client.services.ReportsApi;
import org.apache.fineract.client.services.RolesApi;
import org.apache.fineract.client.services.RunReportsApi;
import org.apache.fineract.client.services.SavingsAccountApi;
import org.apache.fineract.client.services.SavingsChargesApi;
import org.apache.fineract.client.services.SavingsProductApi;
import org.apache.fineract.client.services.SchedulerApi;
import org.apache.fineract.client.services.ScoreCardApi;
import org.apache.fineract.client.services.SearchApiApi;
import org.apache.fineract.client.services.SelfAccountTransferApi;
import org.apache.fineract.client.services.SelfAuthenticationApi;
import org.apache.fineract.client.services.SelfClientApi;
import org.apache.fineract.client.services.SelfDividendApi;
import org.apache.fineract.client.services.SelfLoanProductsApi;
import org.apache.fineract.client.services.SelfLoansApi;
import org.apache.fineract.client.services.SelfRunReportApi;
import org.apache.fineract.client.services.SelfSavingsAccountApi;
import org.apache.fineract.client.services.SelfScoreCardApi;
import org.apache.fineract.client.services.SelfServiceRegistrationApi;
import org.apache.fineract.client.services.SelfShareAccountsApi;
import org.apache.fineract.client.services.SelfSpmApi;
import org.apache.fineract.client.services.SelfThirdPartyTransferApi;
import org.apache.fineract.client.services.SelfUserApi;
import org.apache.fineract.client.services.SelfUserDetailsApi;
import org.apache.fineract.client.services.ShareAccountApi;
import org.apache.fineract.client.services.SpmApiLookUpTableApi;
import org.apache.fineract.client.services.SpmSurveysApi;
import org.apache.fineract.client.services.StaffApi;
import org.apache.fineract.client.services.StandingInstructionsApi;
import org.apache.fineract.client.services.StandingInstructionsHistoryApi;
import org.apache.fineract.client.services.TaxComponentsApi;
import org.apache.fineract.client.services.TaxGroupApi;
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}.
*
* @author Michael Vorburger.ch
*/
public final class FineractClient {
/**
* Constant to be used in requests where Fineract's API requires a dateFormat to be given. This matches the format
* in which LocalDate instances are serialized. (BTW: In a Java client API, it seems weird to have strong LocalDate
* (not String) instances, and then have to specify its format, see
* https://issues.apache.org/jira/browse/FINERACT-1233.)
*/
// Matching org.apache.fineract.client.util.JSON.LocalDateTypeAdapter.formatter
public static final String DATE_FORMAT = "yyyy-MM-dd";
private final Retrofit retrofit;
public final AccountingClosureApi glClosures;
public final AccountingRulesApi accountingRules;
public final AccountNumberFormatApi accountNumberFormats;
public final AccountTransfersApi accountTransfers;
public final AdhocQueryApiApi adhocQuery;
public final AuditsApi audits;
public final AuthenticationHttpBasicApi authentication;
public final BatchApiApi batches;
public final CacheApi caches;
public final CashierJournalsApi cashiersJournal;
public final CashiersApi cashiers;
public final CentersApi centers;
public final ChargesApi charges;
public final ClientApi clients;
public final ClientChargesApi clientCharges;
public final ClientIdentifierApi clientIdentifiers;
public final ClientsAddressApi clientAddresses;
public final ClientTransactionApi clientTransactions;
public final CodesApi codes;
public final CodeValuesApi codeValues;
public final CurrencyApi currencies;
public final DataTablesApi dataTables;
public final @Deprecated DefaultApi legacy; // TODO FINERACT-1222
public final DocumentsApiFixed documents;
public final EntityDataTableApi entityDatatableChecks;
public final EntityFieldConfigurationApi entityFieldConfigurations;
public final ExternalServicesApi externalServices;
public final FetchAuthenticatedUserDetailsApi userDetails;
public final FixedDepositAccountApi fixedDepositAccounts;
public final FixedDepositProductApi fixedDepositProducts;
public final FloatingRatesApi floatingRates;
public final GeneralLedgerAccountApi glAccounts;
public final GlobalConfigurationApi globalConfigurations;
public final GroupsApi groups;
public final HolidaysApi holidays;
public final HooksApi hooks;
public final ImagesApi images;
public final InterestRateChartApi interestRateCharts;
public final InterestRateSlabAKAInterestBandsApi interestRateChartLabs;
public final JournalEntriesApi journalEntries;
public final ListReportMailingJobHistoryApi reportMailings;
public final LoanChargesApi loanCharges;
public final LoanCollateralApi loanCollaterals;
public final LoanProductsApi loanProducts;
public final LoanReschedulingApi loanSchedules;
public final LoansApi loans;
public final LoanTransactionsApi loanTransactions;
public final MakerCheckerOr4EyeFunctionalityApi makerCheckers;
public final MappingFinancialActivitiesToAccountsApi financialActivyAccountMappings;
public final MifosxBatchJobsApi jobs;
public final MixMappingApi mixMappings;
public final MixReportApi mixReports;
public final MixTaxonomyApi mixTaxonomies;
public final NotesApi notes;
public final NotificationApi notifications;
public final OfficesApi offices;
public final PasswordPreferencesApi passwordPreferences;
public final PaymentTypeApi paymentTypes;
public final PeriodicAccrualAccountingApi periodicAccrualAccounting;
public final PermissionsApi permissions;
public final PocketApi selfPockets;
public final ProvisioningCategoryApi provisioningCategories;
public final ProvisioningCriteriaApi provisioningCriterias;
public final ProvisioningEntriesApi provisioningEntries;
public final RecurringDepositAccountApi recurringDepositAccounts;
public final RecurringDepositAccountTransactionsApi recurringDepositAccountTransactions;
public final RecurringDepositProductApi recurringDepositProducts;
public final ReportMailingJobsApi reportMailingJobs;
public final ReportsApi reports;
public final RolesApi roles;
public final RunReportsApi reportsRun;
public final SavingsAccountApi savingsAccounts;
public final SavingsChargesApi savingsAccountCharges;
public final SavingsProductApi savingsProducts;
public final SchedulerApi jobsScheduler;
public final ScoreCardApi surveyScorecards;
public final SearchApiApi search;
public final SelfAccountTransferApi selfAccountTransfers;
public final SelfAuthenticationApi selfAuthentication;
public final SelfClientApi selfClients;
public final SelfDividendApi selfShareProducts;
public final SelfLoanProductsApi selfLoanProducts;
public final SelfLoansApi selfLoans;
public final SelfRunReportApi selfReportsRun;
public final SelfSavingsAccountApi selfSavingsAccounts;
public final SelfScoreCardApi selfSurveyScorecards;
public final SelfServiceRegistrationApi selfRegistration;
public final SelfShareAccountsApi selfShareAccounts;
public final SelfSpmApi selfSurveys;
public final SelfThirdPartyTransferApi selfThirdPartyBeneficiaries;
public final SelfUserApi selfUser;
public final SelfUserDetailsApi selfUserDetails;
public final ShareAccountApi shareAccounts;
public final SpmApiLookUpTableApi surveyLookupTables;
public final SpmSurveysApi surveys;
public final StaffApi staff;
public final StandingInstructionsApi standingInstructions;
public final StandingInstructionsHistoryApi standingInstructionsHistory;
public final TaxComponentsApi taxComponents;
public final TaxGroupApi taxGroups;
public final TellerCashManagementApi tellers;
public final UserGeneratedDocumentsApi templates;
public final UsersApi users;
public final WorkingDaysApi workingDays;
private FineractClient(Retrofit retrofit) {
this.retrofit = retrofit;
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(DocumentsApiFixed.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);
images = retrofit.create(ImagesApi.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 retrofit.create(serviceClass);
}
public static final class Builder {
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;
private String password;
private Builder() {}
public Builder baseURL(String baseURL) {
this.baseURL = baseURL;
return this;
}
public Builder tenant(String tenant) {
this.tenant = tenant;
return this;
}
public Builder basicAuth(String username, String password) {
this.username = username;
this.password = password;
return this;
}
public Builder logging(Level level) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(level);
okBuilder.addInterceptor(logging);
return this;
}
/**
* Skip Fineract API host SSL certificate verification. DO NOT USE THIS when invoking a production server's API!
* This is intended for https://localhost:8443/ testing of development servers with self-signed certificates,
* only. If you do not understand what this is, do not use it. You WILL cause a security issue in your
* application due to the possibility of a "man in the middle" attack when this is enabled.
*/
@SuppressWarnings("unused")
public Builder insecure(boolean insecure) {
// Nota bene: Similar code to this is also in Fineract Provider's
// org.apache.fineract.infrastructure.hooks.processor.ProcessorHelper
if (insecure) {
HostnameVerifier insecureHostnameVerifier = (hostname, session) -> true;
okBuilder.hostnameVerifier(insecureHostnameVerifier);
try {
X509TrustManager insecureX509TrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { insecureX509TrustManager }, new SecureRandom());
SSLSocketFactory insecureSslSocketFactory = sslContext.getSocketFactory();
okBuilder.sslSocketFactory(insecureSslSocketFactory, insecureX509TrustManager);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new IllegalStateException("insecure() SSL configuration failed", e);
}
}
return this;
}
public FineractClient build() {
// URL
retrofitBuilder.baseUrl(has("baseURL", baseURL));
// Tenant
ApiKeyAuth tenantAuth = new ApiKeyAuth("header", "fineract-platform-tenantid");
tenantAuth.setApiKey(has("tenant", tenant));
okBuilder.addInterceptor(tenantAuth);
// BASIC Auth
HttpBasicAuth basicAuth = new HttpBasicAuth();
basicAuth.setCredentials(has("username", username), has("password", password));
okBuilder.addInterceptor(basicAuth);
retrofitBuilder.client(okBuilder.build());
return new FineractClient(retrofitBuilder.build());
}
/**
* Obtain the internal Retrofit Builder. This method is typically not required to be invoked for simple API
* usages, but can be a handy back door for non-trivial advanced customizations of the API client.
*
* @return the {@link ApiClient} which {@link #build()} will use.
*/
public retrofit2.Retrofit.Builder getRetrofitBuilder() {
return retrofitBuilder;
}
/**
* Obtain the internal OkHttp Builder. This method is typically not required to be invoked for simple API
* usages, but can be a handy back door for non-trivial advanced customizations of the API client.
*
* @return the {@link ApiClient} which {@link #build()} will use.
*/
public okhttp3.OkHttpClient.Builder getOkBuilder() {
return okBuilder;
}
private <T> T has(String propertyName, T value) throws IllegalStateException {
if (value == null) {
throw new IllegalStateException("Must call " + propertyName + "(...) to create valid Builder");
}
return value;
}
}
}