Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2019 The RoboZonky Project
*
* Licensed 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 com.github.robozonky.common.remote;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.ws.rs.core.Response;
import com.github.robozonky.api.remote.CollectionsApi;
import com.github.robozonky.api.remote.ControlApi;
import com.github.robozonky.api.remote.LoanApi;
import com.github.robozonky.api.remote.ParticipationApi;
import com.github.robozonky.api.remote.PortfolioApi;
import com.github.robozonky.api.remote.ReservationApi;
import com.github.robozonky.api.remote.TransactionApi;
import com.github.robozonky.api.remote.WalletApi;
import com.github.robozonky.api.remote.entities.BlockedAmount;
import com.github.robozonky.api.remote.entities.LastPublishedLoan;
import com.github.robozonky.api.remote.entities.Participation;
import com.github.robozonky.api.remote.entities.PurchaseRequest;
import com.github.robozonky.api.remote.entities.RawDevelopment;
import com.github.robozonky.api.remote.entities.RawInvestment;
import com.github.robozonky.api.remote.entities.RawLoan;
import com.github.robozonky.api.remote.entities.ReservationPreferences;
import com.github.robozonky.api.remote.entities.ResolutionRequest;
import com.github.robozonky.api.remote.entities.Resolutions;
import com.github.robozonky.api.remote.entities.Restrictions;
import com.github.robozonky.api.remote.entities.SellRequest;
import com.github.robozonky.api.remote.entities.Statistics;
import com.github.robozonky.api.remote.entities.Transaction;
import com.github.robozonky.api.remote.entities.Wallet;
import com.github.robozonky.api.remote.entities.ZonkyApiToken;
import com.github.robozonky.api.remote.entities.sanitized.Development;
import com.github.robozonky.api.remote.entities.sanitized.Investment;
import com.github.robozonky.api.remote.entities.sanitized.Loan;
import com.github.robozonky.api.remote.entities.sanitized.MarketplaceLoan;
import com.github.robozonky.api.remote.entities.sanitized.Reservation;
import com.github.robozonky.api.remote.enums.Resolution;
import com.github.robozonky.api.remote.enums.TransactionCategory;
import com.github.robozonky.internal.api.Settings;
import com.github.robozonky.internal.test.DateUtil;
import com.github.rutledgepaulv.pagingstreams.PagingStreams;
import jdk.jfr.Event;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Represents an instance of Zonky API that is fully authenticated and ready to perform operations on behalf of the
* user. Consider {@link #logout()} when done.
*/
public class Zonky {
private static final Logger LOGGER = LogManager.getLogger(Zonky.class);
private final Api controlApi;
private final Api exports;
private final Api reservationApi;
private final PaginatedApi loanApi;
private final PaginatedApi participationApi;
private final PaginatedApi portfolioApi;
private final PaginatedApi walletApi;
private final PaginatedApi transactions;
private final PaginatedApi collectionsApi;
Zonky(final ApiProvider api, final Supplier tokenSupplier) {
this.controlApi = api.control(tokenSupplier);
this.exports = api.exports(tokenSupplier);
this.loanApi = api.marketplace(tokenSupplier);
this.reservationApi = api.reservations(tokenSupplier);
this.participationApi = api.secondaryMarketplace(tokenSupplier);
this.portfolioApi = api.portfolio(tokenSupplier);
this.walletApi = api.wallet(tokenSupplier);
this.transactions = api.transactions(tokenSupplier);
this.collectionsApi = api.collections(tokenSupplier);
}
private static Stream getStream(final PaginatedApi api, final Function> function) {
return getStream(api, function, Settings.INSTANCE.getDefaultApiPageSize());
}
private static Stream getStream(final PaginatedApi api, final Function> function,
final int pageSize) {
return getStream(api, function, new Select(), pageSize);
}
private static Stream getStream(final PaginatedApi api, final Function> function,
final Select select) {
return getStream(api, function, select, Settings.INSTANCE.getDefaultApiPageSize());
}
private static Stream getStream(final PaginatedApi api, final Function> function,
final Select select, final int pageSize) {
return PagingStreams.streamBuilder(new EntityCollectionPageSource<>(api, function, select, pageSize))
.pageSize(pageSize)
.build();
}
public void invest(final Investment investment) {
LOGGER.debug("Investing into loan #{}.", investment.getLoanId());
controlApi.run(api -> api.invest(new RawInvestment(investment)));
}
public void cancel(final Investment investment) {
LOGGER.debug("Cancelling offer to sell investment in loan #{}.", investment.getLoanId());
controlApi.run(api -> api.cancel(investment.getId()));
}
public void purchase(final Participation participation) {
LOGGER.debug("Purchasing participation #{} in loan #{}.", participation.getId(), participation.getLoanId());
controlApi.run(api -> api.purchase(participation.getId(), new PurchaseRequest(participation)));
}
public void sell(final Investment investment) {
LOGGER.debug("Offering to sell investment in loan #{}.", investment.getLoanId());
controlApi.run(api -> api.offer(new SellRequest(new RawInvestment(investment))));
}
public void accept(final Reservation reservation) {
final ResolutionRequest r = new ResolutionRequest(reservation.getMyReservation().getId(), Resolution.ACCEPTED);
final Resolutions rs = new Resolutions(Collections.singleton(r));
controlApi.run(c -> c.accept(rs));
}
public void setReservationPreferences(final ReservationPreferences preferences) {
controlApi.run(c -> c.setReservationPreferences(preferences));
}
public Wallet getWallet() {
return walletApi.execute(WalletApi::wallet);
}
/**
* Retrieve reservations that the user has to either accept or reject.
* @return All items from the remote API, lazy-loaded.
*/
public Stream getPendingReservations() {
return reservationApi.call(ReservationApi::items)
.getReservations()
.stream()
.map(Reservation::sanitized);
}
/**
* Retrieve blocked amounts from user's wallet via {@link WalletApi}.
* @return All items from the remote API, lazy-loaded.
*/
public Stream getBlockedAmounts() {
return getStream(walletApi, WalletApi::items);
}
/**
* Retrieve investments from user's portfolio via {@link PortfolioApi}, in a given order.
* @param select Rules to filter the selection by.
* @return All items from the remote API, lazy-loaded.
*/
public Stream getInvestments(final Select select) {
final Function investmentDateSupplier = i -> {
/*
* Zonky makes it very difficult to figure out when any particular investment was made. this code attempts
* to figure it out.
*
* we find the first payment from past transactions. if there's no first payment, we use the expected date
* or days past due.
*
* we subtract a month from that value to find out the approximate date when this loan was created.
*/
final Supplier expectedPayment = () -> i.getNextPaymentDate()
.map(OffsetDateTime::toLocalDate)
.orElse(DateUtil.localNow().toLocalDate()).minusDays(i.getDaysPastDue());
final LocalDate lastPayment = getTransactions(i)
.filter(t -> t.getCategory() == TransactionCategory.PAYMENT)
.map(Transaction::getTransactionDate)
.sorted()
.findFirst()
.orElseGet(expectedPayment);
final LocalDate d = lastPayment.minusMonths(1);
LOGGER.debug("Date for investment #{} (loan #{}) was determined to be {}.", i.getId(), i.getLoanId(), d);
return d;
};
return getStream(portfolioApi, PortfolioApi::items, select)
.map(raw -> Investment.sanitized(raw, investmentDateSupplier));
}
public Stream getDelinquentInvestments() {
return getInvestments(new Select()
.in("loan.status", "ACTIVE", "PAID_OFF")
.equals("loan.unpaidLastInst", "true")
.equals("status", "ACTIVE"));
}
public Loan getLoan(final int id) {
final Event event = new ZonkyLoanCallJfrEvent(); // may be triggered by the strategy and we want to measure that
try {
event.begin();
return Loan.sanitized(loanApi.execute(api -> api.item(id)));
} finally {
event.commit();
}
}
public Optional getInvestment(final long id) {
final Select s = new Select().equals("id", id);
return getInvestments(s).findFirst();
}
public Optional getInvestmentByLoanId(final int loanId) {
final Select s = new Select().equals("loan.id", loanId);
return getInvestments(s).findFirst();
}
public LastPublishedLoan getLastPublishedLoanInfo() {
return loanApi.execute(LoanApi::lastPublished);
}
/**
* Retrieve loans from marketplace via {@link LoanApi}.
* @param select Rules to filter the selection by.
* @return All items from the remote API, lazy-loaded.
*/
public Stream getAvailableLoans(final Select select) {
return getStream(loanApi, LoanApi::items, select).map(MarketplaceLoan::sanitized);
}
/**
* Retrieve transactions from the wallet via {@link TransactionApi}.
* @param select Rules to filter the selection by.
* @return All items from the remote API, lazy-loaded, filtered.
*/
public Stream getTransactions(final Select select) {
return getStream(transactions, TransactionApi::items, select);
}
/**
* Retrieve transactions from the wallet via {@link TransactionApi}.
* @param investment Investment to filter the selection by.
* @return All items from the remote API, lazy-loaded, filtered for the specific investment.
*/
public Stream getTransactions(final Investment investment) {
final Select select = new Select().equals("investment.id", investment.getId());
return getTransactions(select);
}
/**
* Retrieve loan collections information via {@link CollectionsApi}.
* @param loanId Loan in question.
* @return All items from the remote API, lazy-loaded.
*/
public Stream getDevelopments(final int loanId) {
return getStream(collectionsApi, a -> a.items(loanId)).map(Development::sanitized);
}
/**
* Retrieve participations from secondary marketplace via {@link ParticipationApi}.
* @param select Rules to filter the selection by.
* @return All items from the remote API, lazy-loaded.
*/
public Stream getAvailableParticipations(final Select select) {
return getStream(participationApi, ParticipationApi::items, select);
}
public ReservationPreferences getReservationPreferences() {
return reservationApi.call(ReservationApi::preferences);
}
public void requestWalletExport() {
exports.run(ExportApi::requestWalletExport);
}
public void requestInvestmentsExport() {
exports.run(ExportApi::requestInvestmentsExport);
}
public Response downloadWalletExport() {
return exports.call(ExportApi::downloadWalletExport);
}
public Response downloadInvestmentsExport() {
return exports.call(ExportApi::downloadInvestmentsExport);
}
public Restrictions getRestrictions() {
return controlApi.call(ControlApi::restrictions);
}
public Statistics getStatistics() {
return portfolioApi.execute(PortfolioApi::item);
}
public void logout() {
controlApi.run(ControlApi::logout);
}
}