diff --git a/README.md b/README.md index 5de6a90..3be3b01 100644 --- a/README.md +++ b/README.md @@ -15,18 +15,18 @@ demonstrating the usage of Riko. Feel free to use it as a starter for your own p com.github.rishabh9 riko - 2.0.2-SNAPSHOT + 3.0.0-SNAPSHOT ``` ### For Gradle based project ```groovy dependencies { - implementation 'com.github.rishabh9:riko:2.0.2-SNAPSHOT' + implementation 'com.github.rishabh9:riko:3.0.0-SNAPSHOT' } ``` ### For SBT based project ```scala -libraryDependencies += "com.github.rishabh9" % "riko" % "2.0.2-SNAPSHOT" +libraryDependencies += "com.github.rishabh9" % "riko" % "3.0.0-SNAPSHOT" ``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index b4cc428..cbc9d40 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ dependencyManagement { // GroupId group = 'com.github.rishabh9' // Version -version = '2.0.2-SNAPSHOT' +version = '3.0.0-SNAPSHOT' archivesBaseName = 'riko' dependencies { @@ -56,7 +56,7 @@ dependencies { // These dependencies are used internally, and not exposed to consumers on their own compile classpath. // Developer's toolkit - implementation 'com.google.guava:guava:23.0' + implementation 'com.google.guava:guava:27.0.1-jre' implementation 'net.jodah:failsafe:1.1.1' // Making API calls implementation 'com.squareup.retrofit2:retrofit:2.4.0' diff --git a/src/main/java/com/github/rishabh9/riko/upstox/common/RetryPolicyFactory.java b/src/main/java/com/github/rishabh9/riko/upstox/common/RetryPolicyFactory.java new file mode 100644 index 0000000..46ac49c --- /dev/null +++ b/src/main/java/com/github/rishabh9/riko/upstox/common/RetryPolicyFactory.java @@ -0,0 +1,55 @@ +/* + * MIT License + * + * Copyright (c) 2018 Rishabh Joshi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.rishabh9.riko.upstox.common; + +import net.jodah.failsafe.RetryPolicy; + +import javax.annotation.Nonnull; +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; + +/** + * A simple factory to create policies for retrying when an API call fails. + * Riko uses the open source Failsafe + * library for implementing retries. + * + * @see RikoRetryPolicyFactory + */ +public interface RetryPolicyFactory { + + /** + * @return A {@link RetryPolicy} that expresses when retries should be performed. + * An {@code empty} {@link RetryPolicy} indicates retries should be disabled. + */ + @Nonnull + Optional createRetryPolicy(); + + /** + * @return A {@link ScheduledExecutorService} to allow for asynchronous executions. + * Cannot be {@code null}. + */ + @Nonnull + ScheduledExecutorService createExecutorService(); +} diff --git a/src/main/java/com/github/rishabh9/riko/upstox/common/RikoRetryPolicyFactory.java b/src/main/java/com/github/rishabh9/riko/upstox/common/RikoRetryPolicyFactory.java new file mode 100644 index 0000000..bbbe7cb --- /dev/null +++ b/src/main/java/com/github/rishabh9/riko/upstox/common/RikoRetryPolicyFactory.java @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (c) 2018 Rishabh Joshi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.rishabh9.riko.upstox.common; + +import net.jodah.failsafe.RetryPolicy; + +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * The Riko's default retry policy factory. + * + * @see RetryPolicyFactory + */ +public class RikoRetryPolicyFactory implements RetryPolicyFactory { + + /** + * @return The default {@link RetryPolicy} + */ + @Override + public Optional createRetryPolicy() { + return Optional.of(new RetryPolicy() + .retryOn(Throwable.class) + .withBackoff(1, 5, TimeUnit.SECONDS) + .withMaxRetries(3)); + } + + /** + * @return The default {@link ScheduledExecutorService} to be used by {@link RetryPolicy}. + */ + @Override + public ScheduledExecutorService createExecutorService() { + return Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); + } +} diff --git a/src/main/java/com/github/rishabh9/riko/upstox/common/Service.java b/src/main/java/com/github/rishabh9/riko/upstox/common/Service.java index a2df7f9..b787c0b 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/common/Service.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/common/Service.java @@ -27,11 +27,13 @@ import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; import com.github.rishabh9.riko.upstox.common.models.AuthHeaders; import com.github.rishabh9.riko.upstox.login.models.AccessToken; +import net.jodah.failsafe.RetryPolicy; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; /** * Parent class for every Service class. Holds common methods. @@ -41,13 +43,20 @@ public abstract class Service { private static final Logger log = LogManager.getLogger(Service.class); protected final UpstoxAuthService upstoxAuthService; + protected final RetryPolicy retryPolicy; + protected final ScheduledExecutorService retryExecutor; /** * @param upstoxAuthService The service to retrieve authentication details */ - public Service(@Nonnull final UpstoxAuthService upstoxAuthService) { + public Service(@Nonnull final UpstoxAuthService upstoxAuthService, + @Nonnull final RetryPolicyFactory retryPolicyFactory) { this.upstoxAuthService = Objects.requireNonNull(upstoxAuthService); + this.retryPolicy = Objects.requireNonNull(retryPolicyFactory).createRetryPolicy() + // The default behaviour is not to re-try. + .orElse(new RetryPolicy().withMaxRetries(0)); + this.retryExecutor = Objects.requireNonNull(retryPolicyFactory).createExecutorService(); } protected T prepareServiceApi(@Nonnull final Class type) { diff --git a/src/main/java/com/github/rishabh9/riko/upstox/common/ServiceGenerator.java b/src/main/java/com/github/rishabh9/riko/upstox/common/ServiceGenerator.java index 1f97d45..d0ceb73 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/common/ServiceGenerator.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/common/ServiceGenerator.java @@ -128,7 +128,7 @@ public void rebuildWithUrl(HttpUrl url) { public S createService(@Nonnull final Class serviceClass) { log.debug("Creating service without authentication"); - return createService(Objects.requireNonNull(serviceClass), null, null); + return createService(Objects.requireNonNull(serviceClass), null); } /** @@ -141,19 +141,14 @@ public S createService(@Nonnull final Class serviceClass) { * @return The retrofitted service */ public S createService(@Nonnull final Class serviceClass, - @Nullable final String username, - @Nullable final String password) { + @Nonnull final String username, + @Nonnull final String password) { - if (!Strings.isNullOrEmpty(username) - && !Strings.isNullOrEmpty(password)) { final String authToken = Credentials.basic(username, password); log.debug("Creating service with Basic authentication"); return createService( Objects.requireNonNull(serviceClass), - new AuthHeaders(authToken, username)); - } - // Setup request headers without any auth - return createService(Objects.requireNonNull(serviceClass), null); + new AuthHeaders(Objects.requireNonNull(authToken), Objects.requireNonNull(username))); } /** diff --git a/src/main/java/com/github/rishabh9/riko/upstox/common/constants/LiveFeedType.java b/src/main/java/com/github/rishabh9/riko/upstox/common/constants/LiveFeedType.java index 3a277ca..ca1e5a5 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/common/constants/LiveFeedType.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/common/constants/LiveFeedType.java @@ -25,6 +25,7 @@ package com.github.rishabh9.riko.upstox.common.constants; public final class LiveFeedType { - public static final String LTP = "LTP"; - public static final String FULL = "Full"; + public static final String LTP = "ltp"; + public static final String FULL = "full"; + public static final String ALL = "all"; } diff --git a/src/main/java/com/github/rishabh9/riko/upstox/common/constants/PropertyKeys.java b/src/main/java/com/github/rishabh9/riko/upstox/common/constants/PropertyKeys.java index e350943..aa449cd 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/common/constants/PropertyKeys.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/common/constants/PropertyKeys.java @@ -53,5 +53,5 @@ public class PropertyKeys { public static final int RIKO_WS_SERVER_PORT_DEFAULT = 443; public static final String RIKO_WS_RECONNECT = "riko.ws.reconnect"; - public static final String RIKO_WS_RECONNECT_DEFAULT = "true"; + public static final String RIKO_WS_RECONNECT_DEFAULT = "false"; } diff --git a/src/main/java/com/github/rishabh9/riko/upstox/common/constants/RateLimits.java b/src/main/java/com/github/rishabh9/riko/upstox/common/constants/RateLimits.java new file mode 100644 index 0000000..21f8d18 --- /dev/null +++ b/src/main/java/com/github/rishabh9/riko/upstox/common/constants/RateLimits.java @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2018 Rishabh Joshi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.rishabh9.riko.upstox.common.constants; + +public class RateLimits { + public static final double LIVE_FEED_RATE_LIMIT = 1.0D; + public static final double SUBSCRIBE_RATE_LIMIT = 1.0D; + public static final double UNSUBSCRIBE_RATE_LIMIT = 1.0D; + public static final double SYMBOLS_SUBSCRIBED_RATE_LIMIT = 1.0D; + public static final double HISTORICAL_RATE_LIMIT = 10.0D; + public static final double LOGIN_RATE_LIMIT = 1.0D; + public static final double ORDER_HISTORY_RATE_LIMIT = 1.0D; + public static final double ORDER_DETAILS_RATE_LIMIT = 1.0D; + public static final double TRADE_BOOK_RATE_LIMIT = 1.0D; + public static final double TRADE_HISTORY_RATE_LIMIT = 1.0D; + public static final double PLACE_ORDER_RATE_LIMIT = 10.0D; + public static final double MODIFY_ORDER_RATE_LIMIT = 1.0D; + public static final double CANCEL_ORDER_RATE_LIMIT = 1.0D; + public static final double PROFILE_RATE_LIMIT = 1.0D; + public static final double BALANCE_RATE_LIMIT = 1.0D; + public static final double POSITIONS_RATE_LIMIT = 1.0D; + public static final double HOLDINGS_RATE_LIMIT = 1.0D; + public static final double MASTER_CONTRACT_RATE_LIMIT = 1.0D; + public static final double WS_PARAMS_RATE_LIMIT = 1.0D; + public static final double WEB_SOCKET_RATE_LIMIT = 1.0D; +} diff --git a/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedApi.java b/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedApi.java index 7aa9a87..872a793 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedApi.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedApi.java @@ -26,6 +26,7 @@ import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; import com.github.rishabh9.riko.upstox.feed.models.Feed; +import com.github.rishabh9.riko.upstox.feed.models.Subscription; import com.github.rishabh9.riko.upstox.feed.models.SubscriptionResponse; import retrofit2.http.GET; import retrofit2.http.Path; @@ -76,4 +77,13 @@ CompletableFuture> subscribe(@Path("type") CompletableFuture> unsubscribe(@Path("type") String type, @Path("exchange") String exchange, @Query("symbol") String symbolsCsv); + + /** + * Get list of symbols subscribed. + * + * @param type 'all' or 'ltp' or 'full'. + * @return A CompletableFuture to execute the request (a)synchronously. + */ + @GET("/live/feed/{type}") + CompletableFuture> symbolsSubscribed(@Path("type") String type); } diff --git a/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedService.java b/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedService.java index cb9634f..ab2113f 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedService.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/feed/FeedService.java @@ -24,28 +24,41 @@ package com.github.rishabh9.riko.upstox.feed; +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; import com.github.rishabh9.riko.upstox.common.Service; import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; import com.github.rishabh9.riko.upstox.feed.models.Feed; +import com.github.rishabh9.riko.upstox.feed.models.Subscription; import com.github.rishabh9.riko.upstox.feed.models.SubscriptionResponse; import com.google.common.base.Strings; +import com.google.common.util.concurrent.RateLimiter; +import net.jodah.failsafe.Failsafe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import java.util.concurrent.CompletableFuture; +import static com.github.rishabh9.riko.upstox.common.constants.RateLimits.*; + +@SuppressWarnings("UnstableApiUsage") public class FeedService extends Service { private static final Logger log = LogManager.getLogger(FeedService.class); + private static final RateLimiter liveFeedRateLimiter = RateLimiter.create(LIVE_FEED_RATE_LIMIT); + private static final RateLimiter subscribeRateLimiter = RateLimiter.create(SUBSCRIBE_RATE_LIMIT); + private static final RateLimiter unsubscribeRateLimiter = RateLimiter.create(UNSUBSCRIBE_RATE_LIMIT); + private static final RateLimiter symbolsSubscribedRateLimiter = RateLimiter.create(SYMBOLS_SUBSCRIBED_RATE_LIMIT); + /** * @param upstoxAuthService The service to retrieve authentication details */ - public FeedService(@Nonnull final UpstoxAuthService upstoxAuthService) { + public FeedService(@Nonnull final UpstoxAuthService upstoxAuthService, + @Nonnull final RetryPolicyFactory retryPolicyFactory) { - super(upstoxAuthService); + super(upstoxAuthService, retryPolicyFactory); } /** @@ -67,7 +80,17 @@ public CompletableFuture> liveFeed(@Nonnull final String ex final FeedApi api = prepareServiceApi(FeedApi.class); log.debug("Making request - GET Live Feed"); - return api.liveFeed(exchange, symbol, type); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Live Feed.", failure)) + .onSuccess(response -> log.debug("GET Live Feed successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Live Feed, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + liveFeedRateLimiter.acquire(1); + return api.liveFeed(exchange, symbol, type); + }); } /** @@ -89,7 +112,17 @@ public CompletableFuture> subscribe(@Nonnul final FeedApi api = prepareServiceApi(FeedApi.class); log.debug("Making request - GET Subscribe"); - return api.subscribe(type, exchange, symbolsCsv); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Subscribe.", failure)) + .onSuccess(response -> log.debug("GET Subscribe successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Subscribe, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + subscribeRateLimiter.acquire(1); + return api.subscribe(type, exchange, symbolsCsv); + }); } /** @@ -111,7 +144,46 @@ public CompletableFuture> unsubscribe(@Nonn final FeedApi api = prepareServiceApi(FeedApi.class); log.debug("Making request - GET Unsubscribe"); - return api.unsubscribe(type, exchange, symbolsCsv); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Unsubscribe.", failure)) + .onSuccess(response -> log.debug("GET Unsubscribe successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Unsubscribe, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + unsubscribeRateLimiter.acquire(1); + return api.unsubscribe(type, exchange, symbolsCsv); + }); + } + + + /** + * Get list of symbols subscribed. + * + * @param type 'all' or 'ltp' or 'full'. Any. Mandatory. + * @return List of symbols subscribed + */ + public CompletableFuture> symbolsSubscribed(@Nonnull final String type) { + + log.debug("Validate parameters - GET Symbols Subscribed"); + validatePathParameter(type); + + log.debug("Preparing service - GET Symbols Subscribed"); + final FeedApi api = prepareServiceApi(FeedApi.class); + + log.debug("Making request - GET Symbols Subscribed"); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Symbols Subscribed.", failure)) + .onSuccess(response -> log.debug("GET Symbols Subscribed successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Symbols Subscribed, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + symbolsSubscribedRateLimiter.acquire(1); + return api.symbolsSubscribed(type); + }); } private void validatePathParameters(String... values) { @@ -125,4 +197,11 @@ private void validatePathParameters(String... values) { } } } + + private void validatePathParameter(String value) { + if (Strings.isNullOrEmpty(value)) { + log.error("Argument validation failed. Arguments 'type' is mandatory."); + throw new IllegalArgumentException("Argument validation failed. Arguments 'type' is mandatory."); + } + } } diff --git a/src/main/java/com/github/rishabh9/riko/upstox/feed/models/Subscription.java b/src/main/java/com/github/rishabh9/riko/upstox/feed/models/Subscription.java new file mode 100644 index 0000000..8b27a4c --- /dev/null +++ b/src/main/java/com/github/rishabh9/riko/upstox/feed/models/Subscription.java @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (c) 2018 Rishabh Joshi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.rishabh9.riko.upstox.feed.models; + +import com.google.common.base.MoreObjects; +import com.google.gson.annotations.SerializedName; + +import java.util.List; +import java.util.Objects; + +public class Subscription { + + @SerializedName("LTP") + private List ltp; + @SerializedName("FULL") + private List full; + + public List getLtp() { + return ltp; + } + + public void setLtp(List ltp) { + this.ltp = ltp; + } + + public List getFull() { + return full; + } + + public void setFull(List full) { + this.full = full; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Subscription that = (Subscription) o; + return Objects.equals(ltp, that.ltp) && + Objects.equals(full, that.full); + } + + @Override + public int hashCode() { + return Objects.hash(ltp, full); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("ltp", ltp) + .add("full", full) + .toString(); + } +} diff --git a/src/main/java/com/github/rishabh9/riko/upstox/feed/models/SymbolSubscribed.java b/src/main/java/com/github/rishabh9/riko/upstox/feed/models/SymbolSubscribed.java new file mode 100644 index 0000000..f505064 --- /dev/null +++ b/src/main/java/com/github/rishabh9/riko/upstox/feed/models/SymbolSubscribed.java @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2018 Rishabh Joshi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.rishabh9.riko.upstox.feed.models; + +import com.google.common.base.MoreObjects; + +import java.util.Objects; + +public class SymbolSubscribed { + + private String exchange; + private String symbol; + + public String getExchange() { + return exchange; + } + + public void setExchange(String exchange) { + this.exchange = exchange; + } + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SymbolSubscribed that = (SymbolSubscribed) o; + return Objects.equals(exchange, that.exchange) && + Objects.equals(symbol, that.symbol); + } + + @Override + public int hashCode() { + return Objects.hash(exchange, symbol); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("exchange", exchange) + .add("symbol", symbol) + .toString(); + } +} diff --git a/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalApi.java b/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalApi.java index 452a183..e3db144 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalApi.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalApi.java @@ -45,24 +45,26 @@ public interface HistoricalApi { * @param symbol Trading symbol. Mandatory. * @param interval Allowed Values: *
    - *
  • 1MINUTE
  • - *
  • 5MINUTE
  • - *
  • 10MINUTE
  • - *
  • 30MINUTE
  • - *
  • 60MINUTE
  • - *
  • 1DAY (default)
  • - *
  • 1WEEK
  • - *
  • 1MONTH
  • + *
  • 1
  • + *
  • 3
  • + *
  • 5
  • + *
  • 10
  • + *
  • 15
  • + *
  • 30
  • + *
  • 60
  • + *
  • day
  • + *
  • week
  • + *
  • month
  • *
- * @param startDate Date in the format: DD-MM-YYYY. Default value is 15 days before today. + * @param startDate Date in the format: DD-MM-YYYY. Default value is 7 days before today. * @param endDate Date in the format: DD-MM-YYYY. Default value is today. * @param format Response format - 'csv' or 'json'. * @return A Call to execute the request (a)synchronously. */ - @GET("/historical/ohlc/{exchange}/{symbol}") + @GET("/historical/ohlc/{exchange}/{symbol}/{interval}") CompletableFuture>> getOhlc(@Path("exchange") String exchange, @Path("symbol") String symbol, - @Query("interval") String interval, + @Path("interval") String interval, @Query("start_date") String startDate, @Query("end_date") String endDate, @Query("format") String format); diff --git a/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalService.java b/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalService.java index ffa7280..8038bcc 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalService.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/historical/HistoricalService.java @@ -24,11 +24,14 @@ package com.github.rishabh9.riko.upstox.historical; +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; import com.github.rishabh9.riko.upstox.common.Service; import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; import com.github.rishabh9.riko.upstox.historical.models.Candle; import com.google.common.base.Strings; +import com.google.common.util.concurrent.RateLimiter; +import net.jodah.failsafe.Failsafe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -36,16 +39,22 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import static com.github.rishabh9.riko.upstox.common.constants.RateLimits.HISTORICAL_RATE_LIMIT; + +@SuppressWarnings("UnstableApiUsage") public class HistoricalService extends Service { private static final Logger log = LogManager.getLogger(HistoricalService.class); + private static final RateLimiter historicalRateLimiter = RateLimiter.create(HISTORICAL_RATE_LIMIT); + /** * @param upstoxAuthService The service to retrieve authentication details */ - public HistoricalService(@Nonnull final UpstoxAuthService upstoxAuthService) { + public HistoricalService(@Nonnull final UpstoxAuthService upstoxAuthService, + @Nonnull final RetryPolicyFactory retryPolicyFactory) { - super(upstoxAuthService); + super(upstoxAuthService, retryPolicyFactory); } /** @@ -55,43 +64,55 @@ public HistoricalService(@Nonnull final UpstoxAuthService upstoxAuthService) { * @param symbol Trading symbol. Mandatory. * @param interval Allowed Values: *
    - *
  • 1MINUTE
  • - *
  • 5MINUTE
  • - *
  • 10MINUTE
  • - *
  • 30MINUTE
  • - *
  • 60MINUTE
  • - *
  • 1DAY (default)
  • - *
  • 1WEEK
  • - *
  • 1MONTH
  • + *
  • 1
  • + *
  • 3
  • + *
  • 5
  • + *
  • 10
  • + *
  • 15
  • + *
  • 30
  • + *
  • 60
  • + *
  • day
  • + *
  • week
  • + *
  • month
  • *
* @param startDate Date in the format: DD-MM-YYYY. - * Default value is 15 days before today. + * Default value is 7 days before today. * @param endDate Date in the format: DD-MM-YYYY. * Default value is today. * @return List of Candle */ public CompletableFuture>> getOhlc(@Nonnull final String exchange, @Nonnull final String symbol, - final String interval, + @Nonnull final String interval, final String startDate, final String endDate) { log.debug("Validate parameters - GET OHLC"); - validatePathParameters(exchange, symbol); + validatePathParameters(exchange, symbol, interval); log.debug("Preparing service - GET OHLC"); final HistoricalApi api = prepareServiceApi(HistoricalApi.class); log.debug("Making request - GET OHLC"); - return api.getOhlc(exchange, symbol, interval, startDate, endDate, "json"); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET OHLC.", failure)) + .onSuccess(response -> log.debug("GET OHLC successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET OHLC, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + historicalRateLimiter.acquire(1); + return api.getOhlc(exchange, symbol, interval, startDate, endDate, "json"); + }); } private void validatePathParameters(String... values) { for (String value : values) { if (Strings.isNullOrEmpty(value)) { - log.error("Argument validation failed. Arguments 'exchange' and 'symbol' are mandatory."); + log.error("Argument validation failed. Arguments 'exchange', 'symbol' and 'interval' are mandatory."); throw new IllegalArgumentException( - "Arguments 'exchange' and 'symbol' are mandatory. They cannot be null nor empty."); + "Arguments 'exchange', 'symbol' and 'interval' are mandatory. They cannot be null nor empty."); } } } diff --git a/src/main/java/com/github/rishabh9/riko/upstox/login/LoginService.java b/src/main/java/com/github/rishabh9/riko/upstox/login/LoginService.java index 3faf582..dc2beef 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/login/LoginService.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/login/LoginService.java @@ -24,11 +24,15 @@ package com.github.rishabh9.riko.upstox.login; +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; +import com.github.rishabh9.riko.upstox.common.Service; import com.github.rishabh9.riko.upstox.common.ServiceGenerator; import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; import com.github.rishabh9.riko.upstox.login.models.AccessToken; import com.github.rishabh9.riko.upstox.login.models.TokenRequest; import com.google.common.base.Strings; +import com.google.common.util.concurrent.RateLimiter; +import net.jodah.failsafe.Failsafe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -36,18 +40,22 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; -public class LoginService { +import static com.github.rishabh9.riko.upstox.common.constants.RateLimits.LOGIN_RATE_LIMIT; + +@SuppressWarnings("UnstableApiUsage") +public class LoginService extends Service { private static final Logger log = LogManager.getLogger(LoginService.class); - private final UpstoxAuthService upstoxAuthService; + private static final RateLimiter loginRateLimiter = RateLimiter.create(LOGIN_RATE_LIMIT); /** * @param upstoxAuthService The service to retrieve authentication details. */ - public LoginService(@Nonnull final UpstoxAuthService upstoxAuthService) { + public LoginService(@Nonnull final UpstoxAuthService upstoxAuthService, + @Nonnull final RetryPolicyFactory retryPolicyFactory) { - this.upstoxAuthService = Objects.requireNonNull(upstoxAuthService); + super(upstoxAuthService, retryPolicyFactory); } /** @@ -71,6 +79,16 @@ public CompletableFuture getAccessToken(@Nonnull final TokenRequest upstoxAuthService.getApiCredentials().getApiKey(), upstoxAuthService.getApiCredentials().getApiSecret()); - return loginApi.getAccessToken(request); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET AccessToken.", failure)) + .onSuccess(response -> log.debug("GET AccessToken successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET AccessToken, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + loginRateLimiter.acquire(1); + return loginApi.getAccessToken(request); + }); } } diff --git a/src/main/java/com/github/rishabh9/riko/upstox/orders/OrderService.java b/src/main/java/com/github/rishabh9/riko/upstox/orders/OrderService.java index af6c3a8..72f77c2 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/orders/OrderService.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/orders/OrderService.java @@ -24,6 +24,7 @@ package com.github.rishabh9.riko.upstox.orders; +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; import com.github.rishabh9.riko.upstox.common.Service; import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; @@ -31,6 +32,8 @@ import com.github.rishabh9.riko.upstox.orders.models.OrderRequest; import com.github.rishabh9.riko.upstox.orders.models.Trade; import com.google.common.base.Strings; +import com.google.common.util.concurrent.RateLimiter; +import net.jodah.failsafe.Failsafe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -38,16 +41,28 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import static com.github.rishabh9.riko.upstox.common.constants.RateLimits.*; + +@SuppressWarnings("UnstableApiUsage") public class OrderService extends Service { private static final Logger log = LogManager.getLogger(OrderService.class); + private static final RateLimiter orderHistoryRateLimiter = RateLimiter.create(ORDER_HISTORY_RATE_LIMIT); + private static final RateLimiter orderDetailsRateLimiter = RateLimiter.create(ORDER_DETAILS_RATE_LIMIT); + private static final RateLimiter tradeBookRateLimiter = RateLimiter.create(TRADE_BOOK_RATE_LIMIT); + private static final RateLimiter tradeHistoryRateLimiter = RateLimiter.create(TRADE_HISTORY_RATE_LIMIT); + private static final RateLimiter placeOrderRateLimiter = RateLimiter.create(PLACE_ORDER_RATE_LIMIT); + private static final RateLimiter modifyOrderRateLimiter = RateLimiter.create(MODIFY_ORDER_RATE_LIMIT); + private static final RateLimiter cancelOrderRateLimiter = RateLimiter.create(CANCEL_ORDER_RATE_LIMIT); + /** * @param upstoxAuthService The service to retrieve authentication details */ - public OrderService(@Nonnull final UpstoxAuthService upstoxAuthService) { + public OrderService(@Nonnull final UpstoxAuthService upstoxAuthService, + @Nonnull final RetryPolicyFactory retryPolicyFactory) { - super(upstoxAuthService); + super(upstoxAuthService, retryPolicyFactory); } /** @@ -61,7 +76,17 @@ public CompletableFuture>> getOrderHistory() { final OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - GET Order History"); - return api.getOrderHistory(); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Order History.", failure)) + .onSuccess(response -> log.debug("GET Order History successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Order History, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + orderHistoryRateLimiter.acquire(1); + return api.getOrderHistory(); + }); } /** @@ -79,7 +104,17 @@ public CompletableFuture>> getOrderDetails(@Nonnull f final OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - GET Order Details"); - return api.getOrderDetails(orderId); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Order Details. ", failure)) + .onSuccess(response -> log.debug("GET Order Details successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Order Details, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + orderDetailsRateLimiter.acquire(1); + return api.getOrderDetails(orderId); + }); } /** @@ -93,7 +128,17 @@ public CompletableFuture>> getTradeBook() { final OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - GET Trade Book"); - return api.getTradeBook(); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Trade Book. ", failure)) + .onSuccess(response -> log.debug("GET Trade Book successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Trade Book, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + tradeBookRateLimiter.acquire(1); + return api.getTradeBook(); + }); } /** @@ -111,7 +156,17 @@ public CompletableFuture>> getTradeHistory(@Nonnull f final OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - GET Trade History"); - return api.getTradeHistory(orderId); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Trade History. ", failure)) + .onSuccess(response -> log.debug("GET Trade History successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Trade History, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + tradeHistoryRateLimiter.acquire(1); + return api.getTradeHistory(orderId); + }); } /** @@ -129,7 +184,17 @@ public CompletableFuture> placeOrder(@Nonnull final OrderR final OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - POST Place Order"); - return api.placeOrder(request); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to POST Place Order.", failure)) + .onSuccess(response -> log.debug("POST Place Order successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to POST Place Order, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + placeOrderRateLimiter.acquire(1); + return api.placeOrder(request); + }); } /** @@ -150,7 +215,17 @@ public CompletableFuture> modifyOrder(@Nonnull final Strin final OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - PUT Modify Order"); - return api.modifyOrder(orderId, request); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to PUT Modify Order.", failure)) + .onSuccess(response -> log.debug("PUT Modify Order successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to PUT Modify Order, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + modifyOrderRateLimiter.acquire(1); + return api.modifyOrder(orderId, request); + }); } /** @@ -168,7 +243,17 @@ public CompletableFuture> cancelOrders(@Nonnull final Str final OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - DELETE Orders"); - return api.cancelOrders(orderIdsCsv); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to DELETE Orders.", failure)) + .onSuccess(response -> log.debug("DELETE Orders successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to DELETE Orders, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + cancelOrderRateLimiter.acquire(1); + return api.cancelOrders(orderIdsCsv); + }); } /** @@ -182,7 +267,17 @@ public CompletableFuture> cancelAllOrders() { OrderApi api = prepareServiceApi(OrderApi.class); log.debug("Making request - DELETE All Orders"); - return api.cancelAllOrders(); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to DELETE All Orders.", failure)) + .onSuccess(response -> log.debug("DELETE All Orders successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to DELETE All Orders, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + cancelOrderRateLimiter.acquire(1); + return api.cancelAllOrders(); + }); } private void validateOrderRequest(OrderRequest request) { diff --git a/src/main/java/com/github/rishabh9/riko/upstox/users/UserService.java b/src/main/java/com/github/rishabh9/riko/upstox/users/UserService.java index f1bd4d1..c96c1d5 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/users/UserService.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/users/UserService.java @@ -24,11 +24,14 @@ package com.github.rishabh9.riko.upstox.users; +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; import com.github.rishabh9.riko.upstox.common.Service; import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; import com.github.rishabh9.riko.upstox.users.models.*; import com.google.common.base.Strings; +import com.google.common.util.concurrent.RateLimiter; +import net.jodah.failsafe.Failsafe; import okhttp3.ResponseBody; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -39,16 +42,26 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import static com.github.rishabh9.riko.upstox.common.constants.RateLimits.*; + +@SuppressWarnings("UnstableApiUsage") public class UserService extends Service { private static final Logger log = LogManager.getLogger(UserService.class); + private static final RateLimiter profileRateLimiter = RateLimiter.create(PROFILE_RATE_LIMIT); + private static final RateLimiter balanceRateLimiter = RateLimiter.create(BALANCE_RATE_LIMIT); + private static final RateLimiter positionsRateLimiter = RateLimiter.create(POSITIONS_RATE_LIMIT); + private static final RateLimiter holdingsRateLimiter = RateLimiter.create(HOLDINGS_RATE_LIMIT); + private static final RateLimiter masterContractRateLimiter = RateLimiter.create(MASTER_CONTRACT_RATE_LIMIT); + /** * @param upstoxAuthService The service to retrieve authentication details */ - public UserService(@Nonnull final UpstoxAuthService upstoxAuthService) { + public UserService(@Nonnull final UpstoxAuthService upstoxAuthService, + @Nonnull final RetryPolicyFactory retryPolicyFactory) { - super(upstoxAuthService); + super(upstoxAuthService, retryPolicyFactory); } /** @@ -62,7 +75,17 @@ public CompletableFuture> getProfile() { final UsersApi api = prepareServiceApi(UsersApi.class); log.debug("Making request - GET Profile"); - return api.getProfile(); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Profile.", failure)) + .onSuccess(response -> log.debug("GET Profile successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Profile, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + profileRateLimiter.acquire(1); + return api.getProfile(); + }); } /** @@ -78,7 +101,17 @@ public CompletableFuture> getProfileBalance( final UsersApi api = prepareServiceApi(UsersApi.class); log.debug("Making request - GET Profile Balance"); - return api.getProfileBalance(accountType); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Profile Balance.", failure)) + .onSuccess(response -> log.debug("GET Profile Balance successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Profile Balance, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + balanceRateLimiter.acquire(1); + return api.getProfileBalance(accountType); + }); } /** @@ -92,7 +125,17 @@ public CompletableFuture>> getPositions() { final UsersApi api = prepareServiceApi(UsersApi.class); log.debug("Making request - GET Positions"); - return api.getPositions(); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Positions.", failure)) + .onSuccess(response -> log.debug("GET Positions successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Positions, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + positionsRateLimiter.acquire(1); + return api.getPositions(); + }); } /** @@ -106,7 +149,17 @@ public CompletableFuture>> getHoldings() { final UsersApi api = prepareServiceApi(UsersApi.class); log.debug("Making request - GET Holdings"); - return api.getHoldings(); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Holdings.", failure)) + .onSuccess(response -> log.debug("GET Holdings successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Holdings, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + holdingsRateLimiter.acquire(1); + return api.getHoldings(); + }); } /** @@ -139,7 +192,17 @@ public CompletableFuture getAllMasterContracts(@Nonnull final Strin final UsersApi api = prepareServiceApi(UsersApi.class); log.debug("Making request - GET All Contracts"); - return api.getAllMasterContracts(exchange) + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET All Contracts.", failure)) + .onSuccess(response -> log.debug("GET All Contracts successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET All Contracts, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + masterContractRateLimiter.acquire(1); + return api.getAllMasterContracts(exchange); + }) .thenApply(ResponseBody::byteStream); } @@ -164,8 +227,8 @@ public CompletableFuture getAllMasterContracts(@Nonnull final Strin * Also, if both symbol and token are null or empty. */ public CompletableFuture> getMasterContract(@Nonnull final String exchange, - @Nullable final String symbol, - @Nullable final String token) { + final String symbol, + final String token) { log.debug("Validate parameters - GET Contract"); validateExchange(exchange); @@ -175,7 +238,17 @@ public CompletableFuture> getMasterContract(@Nonnull fi final UsersApi api = prepareServiceApi(UsersApi.class); log.debug("Making request - GET Contract"); - return api.getMasterContract(exchange, symbol, token); + return Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to GET Contract.", failure)) + .onSuccess(response -> log.debug("GET Contract successful!", response)) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to GET Contract, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + masterContractRateLimiter.acquire(1); + return api.getMasterContract(exchange, symbol, token); + }); } private void validateSymbolAndToken(final String symbol, final String token) { diff --git a/src/main/java/com/github/rishabh9/riko/upstox/websockets/WebSocketService.java b/src/main/java/com/github/rishabh9/riko/upstox/websockets/WebSocketService.java index 786987c..c625612 100644 --- a/src/main/java/com/github/rishabh9/riko/upstox/websockets/WebSocketService.java +++ b/src/main/java/com/github/rishabh9/riko/upstox/websockets/WebSocketService.java @@ -24,6 +24,7 @@ package com.github.rishabh9.riko.upstox.websockets; +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; import com.github.rishabh9.riko.upstox.common.Service; import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; @@ -32,8 +33,8 @@ import com.github.rishabh9.riko.upstox.websockets.models.WebsocketParameters; import com.github.rishabh9.riko.upstox.websockets.models.WrappedWebSocket; import com.google.common.base.Strings; +import com.google.common.util.concurrent.RateLimiter; import net.jodah.failsafe.Failsafe; -import net.jodah.failsafe.RetryPolicy; import okhttp3.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -41,29 +42,27 @@ import javax.annotation.Nonnull; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static com.github.rishabh9.riko.upstox.common.constants.PropertyKeys.*; +import static com.github.rishabh9.riko.upstox.common.constants.RateLimits.WEB_SOCKET_RATE_LIMIT; +import static com.github.rishabh9.riko.upstox.common.constants.RateLimits.WS_PARAMS_RATE_LIMIT; +@SuppressWarnings("UnstableApiUsage") public class WebSocketService extends Service { private static final Logger log = LogManager.getLogger(WebSocketService.class); - private final RetryPolicy retryPolicy; - private final ScheduledExecutorService executorService; + private static final RateLimiter wsParamsRateLimiter = RateLimiter.create(WS_PARAMS_RATE_LIMIT); + private static final RateLimiter webSocketRateLimiter = RateLimiter.create(WEB_SOCKET_RATE_LIMIT); /** * @param upstoxAuthService The service to retrieve authentication details */ - public WebSocketService(@Nonnull final UpstoxAuthService upstoxAuthService) { - super(upstoxAuthService); - retryPolicy = new RetryPolicy() - .retryOn(Throwable.class) - .withBackoff(1, 180, TimeUnit.SECONDS) - .withMaxRetries(10); - this.executorService = Executors.newScheduledThreadPool(4); + public WebSocketService(@Nonnull final UpstoxAuthService upstoxAuthService, + @Nonnull final RetryPolicyFactory retryPolicyFactory) { + + super(upstoxAuthService, retryPolicyFactory); } private CompletableFuture> getWebsocketParameters() { @@ -91,9 +90,8 @@ public WrappedWebSocket connect(final List subscribers) throw throw new IllegalArgumentException("Subscribers not provided. Not connecting to the socket."); } - // Step 1: Retrieve the webSocket parameters before connecting, as per Upstox documentation. - final UpstoxResponse response = Failsafe.with(retryPolicy) - .with(executorService) + return Failsafe.with(retryPolicy) + .with(retryExecutor) .onFailure(failure -> { log.fatal("Failed completely to retrieve web-socket parameters. ", failure); }) @@ -103,19 +101,26 @@ public WrappedWebSocket connect(final List subscribers) throw }) .onRetry((c, f, ctx) -> log.warn("Failure #" + ctx.getExecutions() - + ". Unable to retrieve web-socket parameters, retrying.", f)) - .future(this::getWebsocketParameters) - .get(); - - // Step 2: Make connection - return Failsafe.with(retryPolicy) - .with(executorService) - .onFailure(failure -> log.fatal("Failed completely to make web-socket connection. ", failure)) - .onSuccess(connection -> log.info("WebSocket connection is successful!")) - .onRetry((c, f, ctx) -> - log.warn("Failure #" + ctx.getExecutions() + ". Unable to connect to web-socket, retrying.", f)) - .get(() -> this.makeConnection(response.getData(), subscribers)) - .get(); + + ". Unable to retrieve web-socket parameters, retrying. REASON: {}", f.getCause().getMessage())) + .future(() -> { + // Step 1: Retrieve the webSocket parameters before connecting, as per Upstox documentation. + wsParamsRateLimiter.acquire(1); + return getWebsocketParameters(); + }) + .thenApply(paramResponse -> Failsafe.with(retryPolicy) + .with(retryExecutor) + .onFailure(failure -> log.fatal("Failed completely to make web-socket connection. ", failure)) + .onSuccess(connection -> log.info("WebSocket connection is successful!")) + .onRetry((c, f, ctx) -> + log.warn("Failure #" + ctx.getExecutions() + + ". Unable to connect to web-socket, retrying. REASON: {}", f.getCause().getMessage())) + .get(() -> { + // Step 2: Make connection + webSocketRateLimiter.acquire(1); + return this.makeConnection(paramResponse.getData(), subscribers); + })) + .get() + .get(); // block until complete } private WrappedWebSocket makeConnection(final WebsocketParameters parameters, diff --git a/src/test/java/com/github/rishabh9/riko/upstox/BaseTest.java b/src/test/java/com/github/rishabh9/riko/upstox/BaseTest.java new file mode 100644 index 0000000..90eb7ff --- /dev/null +++ b/src/test/java/com/github/rishabh9/riko/upstox/BaseTest.java @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2018 Rishabh Joshi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.rishabh9.riko.upstox; + +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; +import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; +import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; +import com.github.rishabh9.riko.upstox.login.models.AccessToken; + +public class BaseTest { + + protected RetryPolicyFactory retryPolicyFactory = new TestRetryPolicyFactory(); + + protected UpstoxAuthService upstoxAuthService = new UpstoxAuthService() { + @Override + public ApiCredentials getApiCredentials() { + return new ApiCredentials("secretApiKey", "secret-secret"); + } + + @Override + public AccessToken getAccessToken() { + AccessToken token = new AccessToken(); + token.setExpiresIn(86400L); + token.setType("Bearer"); + token.setToken("access_token_123456789"); + return token; + } + }; +} diff --git a/src/test/java/com/github/rishabh9/riko/upstox/TestRetryPolicyFactory.java b/src/test/java/com/github/rishabh9/riko/upstox/TestRetryPolicyFactory.java new file mode 100644 index 0000000..6bc23d4 --- /dev/null +++ b/src/test/java/com/github/rishabh9/riko/upstox/TestRetryPolicyFactory.java @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2018 Rishabh Joshi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.rishabh9.riko.upstox; + +import com.github.rishabh9.riko.upstox.common.RetryPolicyFactory; +import net.jodah.failsafe.RetryPolicy; + +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * The Riko's default retry policy factory for running tests + * + * @see RetryPolicyFactory + */ +public class TestRetryPolicyFactory implements RetryPolicyFactory { + + /** + * @return The default {@link RetryPolicy} + */ + @Override + public Optional createRetryPolicy() { + return Optional.empty(); + } + + /** + * @return The default {@link ScheduledExecutorService} to be used by {@link RetryPolicy}. + */ + @Override + public ScheduledExecutorService createExecutorService() { + return Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); + } +} diff --git a/src/test/java/com/github/rishabh9/riko/upstox/feed/FeedServiceTest.java b/src/test/java/com/github/rishabh9/riko/upstox/feed/FeedServiceTest.java index 28532cc..e299acc 100644 --- a/src/test/java/com/github/rishabh9/riko/upstox/feed/FeedServiceTest.java +++ b/src/test/java/com/github/rishabh9/riko/upstox/feed/FeedServiceTest.java @@ -24,13 +24,13 @@ package com.github.rishabh9.riko.upstox.feed; +import com.github.rishabh9.riko.upstox.BaseTest; import com.github.rishabh9.riko.upstox.common.ServiceGenerator; -import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; -import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; import com.github.rishabh9.riko.upstox.feed.models.Feed; +import com.github.rishabh9.riko.upstox.feed.models.Subscription; import com.github.rishabh9.riko.upstox.feed.models.SubscriptionResponse; -import com.github.rishabh9.riko.upstox.login.models.AccessToken; +import com.github.rishabh9.riko.upstox.feed.models.SymbolSubscribed; import com.google.gson.Gson; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -39,31 +39,16 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.util.List; import java.util.concurrent.ExecutionException; import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AFTER_REQUEST; import static org.junit.jupiter.api.Assertions.*; -class FeedServiceTest { +class FeedServiceTest extends BaseTest { private static final Logger log = LogManager.getLogger(FeedServiceTest.class); - private UpstoxAuthService upstoxAuthService = new UpstoxAuthService() { - @Override - public ApiCredentials getApiCredentials() { - return new ApiCredentials("secretApiKey", "secret-secret"); - } - - @Override - public AccessToken getAccessToken() { - AccessToken token = new AccessToken(); - token.setExpiresIn(86400L); - token.setType("Bearer"); - token.setToken("access_token_123456789"); - return token; - } - }; - @Test void liveFeed_success_whenAllParametersAreCorrect() throws IOException { MockWebServer server = new MockWebServer(); @@ -82,11 +67,11 @@ void liveFeed_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse serverResponse = - service.liveFeed("NSE", "RELIANCE", "TYPE") + service.liveFeed("NSE", "RELIANCE", "FULL") .get(); assertEquals(response, serverResponse); } catch (ExecutionException | InterruptedException e) { @@ -113,10 +98,10 @@ void liveFeed_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, - service.liveFeed("NSE", "RELIANCE", "TYPE")::get); + service.liveFeed("NSE", "RELIANCE", "FULL")::get); server.shutdown(); } @@ -131,10 +116,10 @@ void liveFeed_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); try { - service.liveFeed("NSE", "RELIANCE", "TYPE").get(); + service.liveFeed("NSE", "RELIANCE", "FULL").get(); } catch (ExecutionException | InterruptedException e) { assertTrue(e.getCause() instanceof IOException); } finally { @@ -144,7 +129,7 @@ void liveFeed_failure_onNetworkError() throws IOException { @Test void liveFeed_throwIAE_whenRequiredParametersAreMissing() { - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.liveFeed(null, null, null), @@ -183,7 +168,7 @@ void liveFeed_throwIAE_whenRequiredParametersAreMissing() { void liveFeed_throwNPE_whenServiceParametersAreMissing() { assertThrows(NullPointerException.class, - () -> new FeedService(null), + () -> new FeedService(null, retryPolicyFactory), "Null check missing for 'UpstoxAuthService' from FeedService constructor"); } @@ -205,7 +190,7 @@ void subscribe_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse subResponse = @@ -236,7 +221,7 @@ void subscribe_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.subscribe("TYPE", "NSE", "ACC,RELIANCE")::get); @@ -254,7 +239,7 @@ void subscribe_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); try { service.subscribe("TYPE", "NSE", "RELIANCE,ACC").get(); @@ -267,7 +252,7 @@ void subscribe_failure_onNetworkError() throws IOException { @Test void subscribe_throwIAE_whenRequiredParametersAreMissing() { - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.subscribe(null, null, null), @@ -320,7 +305,7 @@ void unsubscribe_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse unsubResponse = @@ -351,7 +336,7 @@ void unsubscribe_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.unsubscribe("TYPE", "NSE", "ACC,RELIANCE")::get); @@ -369,7 +354,7 @@ void unsubscribe_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); try { service.unsubscribe("TYPE", "NSE", "RELIANCE,ACC").get(); @@ -382,7 +367,7 @@ void unsubscribe_failure_onNetworkError() throws IOException { @Test void unsubscribe_throwIAE_whenRequiredParametersAreMissing() { - FeedService service = new FeedService(upstoxAuthService); + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.unsubscribe(null, null, null), @@ -416,4 +401,96 @@ void unsubscribe_throwIAE_whenRequiredParametersAreMissing() { service.unsubscribe("TYPE", "NSE", ""), "Symbols cannot be empty. Mandatory validation missing."); } + + @Test + void symbolsSubscribed_success_whenAllParametersAreCorrect() throws IOException { + MockWebServer server = new MockWebServer(); + + Subscription subscription = new Subscription(); + SymbolSubscribed symbolSubscribed = new SymbolSubscribed(); + symbolSubscribed.setExchange("NSE"); + symbolSubscribed.setSymbol("ACC"); + subscription.setFull(List.of(symbolSubscribed)); + UpstoxResponse response = new UpstoxResponse<>(); + response.setCode(200); + response.setData(subscription); + + server.enqueue( + new MockResponse().setBody( + new Gson().toJson(response))); + + server.start(); + + ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); + + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); + + try { + UpstoxResponse serverResponse = service.symbolsSubscribed("FULL").get(); + assertEquals(response, serverResponse); + } catch (ExecutionException | InterruptedException e) { + log.fatal(e); + fail(); + } finally { + server.shutdown(); + } + } + + @Test + void symbolsSubscribed_failure_whenUpstoxReturnsError() throws IOException { + MockWebServer server = new MockWebServer(); + + server.enqueue(new MockResponse() + .setResponseCode(400) + .setBody("{\"code\":400," + + "\"status\":\"Bad Request\"," + + "\"timestamp\":\"2018-06-19T20:11:57+05:30\"," + + "\"message\":\"Random error\"," + + "\"error\":{\"name\":\"Error\",\"reason\":\"Random error\"}}")); + + server.start(); + + ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); + + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); + + assertThrows(ExecutionException.class, service.symbolsSubscribed("FULL")::get); + + server.shutdown(); + } + + @Test + void symbolsSubscribed_failure_onNetworkError() throws IOException { + MockWebServer server = new MockWebServer(); + + server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AFTER_REQUEST)); + + server.start(); + + ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); + + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); + + try { + service.symbolsSubscribed("FULL").get(); + } catch (ExecutionException | InterruptedException e) { + assertTrue(e.getCause() instanceof IOException); + } finally { + server.shutdown(); + } + } + + @Test + void symbolsSubscribed_throwIAE_whenRequiredParametersAreMissing() { + FeedService service = new FeedService(upstoxAuthService, retryPolicyFactory); + + assertThrows(IllegalArgumentException.class, () -> + service.symbolsSubscribed(null), + "Type cannot be null. Mandatory validation missing."); + + assertThrows(IllegalArgumentException.class, () -> + service.symbolsSubscribed(""), + "Type cannot be empty. Mandatory validation missing."); + } + } \ No newline at end of file diff --git a/src/test/java/com/github/rishabh9/riko/upstox/historical/HistoricalServiceTest.java b/src/test/java/com/github/rishabh9/riko/upstox/historical/HistoricalServiceTest.java index ad1daa8..d1605aa 100644 --- a/src/test/java/com/github/rishabh9/riko/upstox/historical/HistoricalServiceTest.java +++ b/src/test/java/com/github/rishabh9/riko/upstox/historical/HistoricalServiceTest.java @@ -24,12 +24,10 @@ package com.github.rishabh9.riko.upstox.historical; +import com.github.rishabh9.riko.upstox.BaseTest; import com.github.rishabh9.riko.upstox.common.ServiceGenerator; -import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; -import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; import com.github.rishabh9.riko.upstox.historical.models.Candle; -import com.github.rishabh9.riko.upstox.login.models.AccessToken; import com.google.gson.Gson; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -45,26 +43,10 @@ import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AFTER_REQUEST; import static org.junit.jupiter.api.Assertions.*; -class HistoricalServiceTest { +class HistoricalServiceTest extends BaseTest { private static final Logger log = LogManager.getLogger(HistoricalServiceTest.class); - private UpstoxAuthService upstoxAuthService = new UpstoxAuthService() { - @Override - public ApiCredentials getApiCredentials() { - return new ApiCredentials("secretApiKey", "secret-secret"); - } - - @Override - public AccessToken getAccessToken() { - AccessToken token = new AccessToken(); - token.setExpiresIn(86400L); - token.setType("Bearer"); - token.setToken("access_token_123456789"); - return token; - } - }; - @Test void getOhlc_success_whenAllParametersAreCorrect() throws IOException { MockWebServer server = new MockWebServer(); @@ -85,11 +67,11 @@ void getOhlc_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - HistoricalService service = new HistoricalService(upstoxAuthService); + HistoricalService service = new HistoricalService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse> serverResponse = - service.getOhlc("NSE", "RELIANCE", "", "", "") + service.getOhlc("NSE", "RELIANCE", "1", "", "") .get(); assertEquals(response, serverResponse); } catch (ExecutionException | InterruptedException e) { @@ -116,10 +98,10 @@ void getOhlc_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - HistoricalService service = new HistoricalService(upstoxAuthService); + HistoricalService service = new HistoricalService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, - service.getOhlc("NSE", "RELIANCE", null, null, null)::get); + service.getOhlc("NSE", "RELIANCE", "1", null, null)::get); server.shutdown(); } @@ -134,10 +116,10 @@ void getOhlc_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - HistoricalService service = new HistoricalService(upstoxAuthService); + HistoricalService service = new HistoricalService(upstoxAuthService, retryPolicyFactory); try { - service.getOhlc("NSE", "RELIANCE", null, null, null).get(); + service.getOhlc("NSE", "RELIANCE", "1", null, null).get(); } catch (ExecutionException | InterruptedException e) { assertTrue(e.getCause() instanceof IOException); } finally { @@ -147,7 +129,7 @@ void getOhlc_failure_onNetworkError() throws IOException { @Test void getOhlc_throwIAE_whenRequiredParametersAreMissing() { - HistoricalService service = new HistoricalService(upstoxAuthService); + HistoricalService service = new HistoricalService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.getOhlc(null, null, null, null, null), @@ -178,7 +160,7 @@ void getOhlc_throwIAE_whenRequiredParametersAreMissing() { void getOhlc_throwNPE_whenServiceParametersAreMissing() { assertThrows(NullPointerException.class, - () -> new HistoricalService(null), + () -> new HistoricalService(null, retryPolicyFactory), "Null check missing for 'UpstoxAuthService' from HistoricalService constructor"); } } \ No newline at end of file diff --git a/src/test/java/com/github/rishabh9/riko/upstox/login/LoginServiceTest.java b/src/test/java/com/github/rishabh9/riko/upstox/login/LoginServiceTest.java index 34b908f..e3a260a 100644 --- a/src/test/java/com/github/rishabh9/riko/upstox/login/LoginServiceTest.java +++ b/src/test/java/com/github/rishabh9/riko/upstox/login/LoginServiceTest.java @@ -24,6 +24,7 @@ package com.github.rishabh9.riko.upstox.login; +import com.github.rishabh9.riko.upstox.BaseTest; import com.github.rishabh9.riko.upstox.common.ServiceGenerator; import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; @@ -41,7 +42,7 @@ import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AFTER_REQUEST; import static org.junit.jupiter.api.Assertions.*; -class LoginServiceTest { +class LoginServiceTest extends BaseTest { private static final Logger log = LogManager.getLogger(LoginServiceTest.class); @@ -73,7 +74,7 @@ public AccessToken getAccessToken() { } }; - LoginService service = new LoginService(upstoxAuthService); + LoginService service = new LoginService(upstoxAuthService, retryPolicyFactory); try { AccessToken accessToken = service.getAccessToken(request).get(); @@ -121,7 +122,7 @@ public AccessToken getAccessToken() { } }; - LoginService service = new LoginService(upstoxAuthService); + LoginService service = new LoginService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, () -> service.getAccessToken(request).get()); @@ -154,7 +155,7 @@ public AccessToken getAccessToken() { } }; - LoginService service = new LoginService(upstoxAuthService); + LoginService service = new LoginService(upstoxAuthService, retryPolicyFactory); try { service.getAccessToken(request).get(); @@ -180,7 +181,7 @@ public AccessToken getAccessToken() { } }; - LoginService service = new LoginService(upstoxAuthService); + LoginService service = new LoginService(upstoxAuthService, retryPolicyFactory); assertThrows(NullPointerException.class, () -> service.getAccessToken(null), "Null check missing for 'TokenRequest' from LoginService constructor"); diff --git a/src/test/java/com/github/rishabh9/riko/upstox/orders/OrderServiceTest.java b/src/test/java/com/github/rishabh9/riko/upstox/orders/OrderServiceTest.java index afe440a..0fe2931 100644 --- a/src/test/java/com/github/rishabh9/riko/upstox/orders/OrderServiceTest.java +++ b/src/test/java/com/github/rishabh9/riko/upstox/orders/OrderServiceTest.java @@ -24,11 +24,9 @@ package com.github.rishabh9.riko.upstox.orders; +import com.github.rishabh9.riko.upstox.BaseTest; import com.github.rishabh9.riko.upstox.common.ServiceGenerator; -import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; -import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; -import com.github.rishabh9.riko.upstox.login.models.AccessToken; import com.github.rishabh9.riko.upstox.orders.models.Order; import com.github.rishabh9.riko.upstox.orders.models.OrderRequest; import com.github.rishabh9.riko.upstox.orders.models.Trade; @@ -47,26 +45,10 @@ import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AFTER_REQUEST; import static org.junit.jupiter.api.Assertions.*; -class OrderServiceTest { +class OrderServiceTest extends BaseTest { private static final Logger log = LogManager.getLogger(OrderServiceTest.class); - private UpstoxAuthService upstoxAuthService = new UpstoxAuthService() { - @Override - public ApiCredentials getApiCredentials() { - return new ApiCredentials("secretApiKey", "secret-secret"); - } - - @Override - public AccessToken getAccessToken() { - AccessToken token = new AccessToken(); - token.setExpiresIn(86400L); - token.setType("Bearer"); - token.setToken("access_token_123456789"); - return token; - } - }; - @Test void getOrderHistory_success_whenAllParametersAreCorrect() throws IOException { MockWebServer server = new MockWebServer(); @@ -87,7 +69,7 @@ void getOrderHistory_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse> serverResponse = service.getOrderHistory().get(); @@ -116,7 +98,7 @@ void getOrderHistory_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getOrderHistory()::get); @@ -133,7 +115,7 @@ void getOrderHistory_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.getOrderHistory().get(); @@ -165,7 +147,7 @@ void getOrderDetails_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse> serverResponse = @@ -195,7 +177,7 @@ void getOrderDetails_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getOrderDetails("ORD_ID_1")::get); @@ -212,7 +194,7 @@ void getOrderDetails_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.getOrderDetails("ORD_ID_1").get(); @@ -226,7 +208,7 @@ void getOrderDetails_failure_onNetworkError() throws IOException { @Test void getOrderDetails_throwIAE_whenRequiredParametersAreMissing() { - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.getOrderDetails(null), @@ -257,7 +239,7 @@ void getTradeBook_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse> serverResponse = service.getTradeBook().get(); @@ -286,7 +268,7 @@ void getTradeBook_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getTradeBook()::get); @@ -303,7 +285,7 @@ void getTradeBook_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.getTradeBook().get(); @@ -335,7 +317,7 @@ void getTradeHistory_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse> serverResponse = @@ -365,7 +347,7 @@ void getTradeHistory_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getTradeHistory("ORD_ID_1")::get); @@ -382,7 +364,7 @@ void getTradeHistory_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.getTradeHistory("ORD_ID_1").get(); @@ -396,7 +378,7 @@ void getTradeHistory_failure_onNetworkError() throws IOException { @Test void getTradeHistory_throwIAE_whenRequiredParametersAreMissing() { - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.getTradeHistory(null), @@ -425,7 +407,7 @@ void placeOrder_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); OrderRequest request = new OrderRequest(); request.setOrderId("ORD_ID_1"); @@ -457,7 +439,7 @@ void placeOrder_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.placeOrder(new OrderRequest())::get); @@ -474,7 +456,7 @@ void placeOrder_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.placeOrder(new OrderRequest()).get(); @@ -488,7 +470,7 @@ void placeOrder_failure_onNetworkError() throws IOException { @Test void placeOrder_throwIAE_whenRequiredParametersAreMissing() { - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.placeOrder(null), @@ -513,7 +495,7 @@ void modifyOrder_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); OrderRequest request = new OrderRequest(); request.setOrderId("ORD_ID_1"); @@ -546,7 +528,7 @@ void modifyOrder_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.modifyOrder("ORD_ID_1", new OrderRequest())::get); @@ -564,7 +546,7 @@ void modifyOrder_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.modifyOrder("ORD_ID_1", new OrderRequest()).get(); @@ -578,7 +560,7 @@ void modifyOrder_failure_onNetworkError() throws IOException { @Test void modifyOrder_throwIAE_whenRequiredParametersAreMissing() { - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.modifyOrder(null, new OrderRequest()), @@ -609,7 +591,7 @@ void cancelOrders_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse serverResponse = @@ -639,7 +621,7 @@ void cancelOrders_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.cancelOrders("ORDER_ID_1")::get); @@ -656,7 +638,7 @@ void cancelOrders_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.cancelOrders("ORD_ID_1,ORD_ID_2").get(); @@ -670,7 +652,7 @@ void cancelOrders_failure_onNetworkError() throws IOException { @Test void cancelOrders_throwIAE_whenRequiredParametersAreMissing() { - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.cancelOrders(null), @@ -697,7 +679,7 @@ void cancelAllOrders_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse serverResponse = service.cancelAllOrders().get(); @@ -726,7 +708,7 @@ void cancelAllOrders_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.cancelAllOrders()::get); @@ -743,7 +725,7 @@ void cancelAllOrders_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - OrderService service = new OrderService(upstoxAuthService); + OrderService service = new OrderService(upstoxAuthService, retryPolicyFactory); try { service.cancelAllOrders().get(); @@ -758,7 +740,7 @@ void cancelAllOrders_failure_onNetworkError() throws IOException { @Test void cancelAllOrders_throwNPE_whenServiceParametersAreMissing() { assertThrows(NullPointerException.class, - () -> new OrderService(null), + () -> new OrderService(null, retryPolicyFactory), "Null check missing for 'UpstoxAuthService' from OrderService constructor"); } } \ No newline at end of file diff --git a/src/test/java/com/github/rishabh9/riko/upstox/users/UserServiceTest.java b/src/test/java/com/github/rishabh9/riko/upstox/users/UserServiceTest.java index 298ae36..832b977 100644 --- a/src/test/java/com/github/rishabh9/riko/upstox/users/UserServiceTest.java +++ b/src/test/java/com/github/rishabh9/riko/upstox/users/UserServiceTest.java @@ -24,11 +24,9 @@ package com.github.rishabh9.riko.upstox.users; +import com.github.rishabh9.riko.upstox.BaseTest; import com.github.rishabh9.riko.upstox.common.ServiceGenerator; -import com.github.rishabh9.riko.upstox.common.UpstoxAuthService; -import com.github.rishabh9.riko.upstox.common.models.ApiCredentials; import com.github.rishabh9.riko.upstox.common.models.UpstoxResponse; -import com.github.rishabh9.riko.upstox.login.models.AccessToken; import com.github.rishabh9.riko.upstox.users.models.*; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -52,26 +50,10 @@ import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AFTER_REQUEST; import static org.junit.jupiter.api.Assertions.*; -class UserServiceTest { +class UserServiceTest extends BaseTest { private static final Logger log = LogManager.getLogger(UserServiceTest.class); - private UpstoxAuthService upstoxAuthService = new UpstoxAuthService() { - @Override - public ApiCredentials getApiCredentials() { - return new ApiCredentials("secretApiKey", "secret-secret"); - } - - @Override - public AccessToken getAccessToken() { - AccessToken token = new AccessToken(); - token.setExpiresIn(86400L); - token.setType("Bearer"); - token.setToken("access_token_123456789"); - return token; - } - }; - @Test void getProfile_success_whenAllParametersAreCorrect() throws IOException { MockWebServer server = new MockWebServer(); @@ -90,7 +72,7 @@ void getProfile_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse serverResponse = service.getProfile().get(); @@ -119,7 +101,7 @@ void getProfile_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getProfile()::get); @@ -136,7 +118,7 @@ void getProfile_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { service.getProfile().get(); @@ -167,7 +149,7 @@ void getProfileBalance_success_whenAllParametersAreCorrect() throws IOException ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse serverResponse = @@ -197,7 +179,7 @@ void getProfileBalance_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getProfileBalance(null)::get); @@ -214,7 +196,7 @@ void getProfileBalance_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { service.getProfileBalance(null).get(); @@ -245,7 +227,7 @@ void getPositions_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse> serverResponse = service.getPositions().get(); @@ -274,7 +256,7 @@ void getPositions_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getPositions()::get); @@ -291,7 +273,7 @@ void getPositions_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { service.getPositions().get(); @@ -322,7 +304,7 @@ void getHoldings_success_whenAllParametersAreCorrect() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse> serverResponse = service.getHoldings().get(); @@ -351,7 +333,7 @@ void getHoldings_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getHoldings()::get); @@ -368,7 +350,7 @@ void getHoldings_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { service.getHoldings().get(); @@ -398,7 +380,7 @@ void getAllMasterContracts_success_whenAllParametersAreCorrect() throws IOExcept ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { InputStream inputStream = @@ -449,7 +431,7 @@ void getAllMasterContracts_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getAllMasterContracts("NSE")::get); @@ -467,7 +449,7 @@ void getAllMasterContracts_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { service.getAllMasterContracts("NSE").get(); @@ -480,7 +462,7 @@ void getAllMasterContracts_failure_onNetworkError() throws IOException { @Test void getAllMasterContracts_throwIAE_whenRequiredParametersAreMissing() { - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.getAllMasterContracts(null), @@ -509,7 +491,7 @@ void getMasterContract_success_whenAllParametersAreCorrect() throws IOException ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { UpstoxResponse serverResponse = @@ -540,7 +522,7 @@ void getMasterContract_failure_whenUpstoxReturnsError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(ExecutionException.class, service.getMasterContract("NSE", "RELIANCE", null)::get); @@ -558,7 +540,7 @@ void getMasterContract_failure_onNetworkError() throws IOException { ServiceGenerator.getInstance().rebuildWithUrl(server.url("/")); - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); try { service.getMasterContract("NSE", "RELIANCE", null).get(); @@ -571,7 +553,7 @@ void getMasterContract_failure_onNetworkError() throws IOException { @Test void getMasterContract_throwIAE_whenRequiredParametersAreMissing() { - UserService service = new UserService(upstoxAuthService); + UserService service = new UserService(upstoxAuthService, retryPolicyFactory); assertThrows(IllegalArgumentException.class, () -> service.getMasterContract(null, null, null), @@ -605,7 +587,7 @@ void getMasterContract_throwIAE_whenRequiredParametersAreMissing() { @Test void getMasterContract_throwNPE_whenServiceParametersAreMissing() { assertThrows(NullPointerException.class, - () -> new UserService(null), + () -> new UserService(null, retryPolicyFactory), "Null check missing for 'UpstoxAuthService' from UserService constructor"); } } \ No newline at end of file