diff --git a/README.md b/README.md index d04e378..6f17e7d 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,17 @@ SmartAPI is a set of REST-like APIs that expose many capabilities required to bu String response = smartConnect.candleData(requestObejct); } + + /** Search Scrip Data */ + public void getSearchScrip(SmartConnect smartConnect) throws SmartAPIException{ + JSONObject payload = new JSONObject(); + payload.put("exchange", "MCX"); + payload.put("searchscrip", "Crude"); + String response = smartConnect.getSearchScrip(payload); + } + + /** Logout user. */ + /** Market Data FULL*/ public void getMarketData(SmartConnect smartConnect) { diff --git a/pom.xml b/pom.xml index 3d8d098..198952d 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,8 @@ 4.4.0 1.4.0 2.27.2 + 1.7.32 + 1.2.6 @@ -165,8 +167,19 @@ wiremock ${wiremock.version} - - + + org.slf4j + slf4j-api + ${slf4j-api.version} + + + ch.qos.logback + logback-classic + ${logback-classic.version} + + + + diff --git a/src/main/java/com/angelbroking/smartapi/Routes.java b/src/main/java/com/angelbroking/smartapi/Routes.java index 089317e..352fa11 100644 --- a/src/main/java/com/angelbroking/smartapi/Routes.java +++ b/src/main/java/com/angelbroking/smartapi/Routes.java @@ -44,6 +44,7 @@ public Routes() { put("api.gtt.details", "/rest/secure/angelbroking/gtt/v1/ruleDetails"); put("api.gtt.list", "/rest/secure/angelbroking/gtt/v1/ruleList"); put("api.candle.data", "/rest/secure/angelbroking/historical/v1/getCandleData"); + put("api.search.script.data", "/rest/secure/angelbroking/order/v1/searchScrip"); put("api.market.data", "/rest/secure/angelbroking/market/v1/quote"); } }; diff --git a/src/main/java/com/angelbroking/smartapi/SmartConnect.java b/src/main/java/com/angelbroking/smartapi/SmartConnect.java index efb9c8c..77f1de3 100644 --- a/src/main/java/com/angelbroking/smartapi/SmartConnect.java +++ b/src/main/java/com/angelbroking/smartapi/SmartConnect.java @@ -15,6 +15,8 @@ import org.json.JSONException; import org.json.JSONObject; + + import java.io.IOException; import java.net.Proxy; import java.util.List; @@ -713,6 +715,30 @@ public String candleData(JSONObject params) { } /** + + * Get Search Script Data. + * + * @param payload is Search Script params. + * @return returns the details of Search Script data. + */ + + public String getSearchScrip(JSONObject payload) throws SmartAPIException, IOException { + try { + String url = routes.get("api.search.script.data"); + return smartAPIRequestHandler.postRequestJSONObject(this.apiKey, url, payload, accessToken); + }catch (IOException ex) { + log.error("{} while generating session {}", IO_EXCEPTION_OCCURRED, ex.getMessage()); + throw new IOException(String.format("%s in generating Session %s", IO_EXCEPTION_ERROR_MSG, ex.getMessage())); + } catch (JSONException ex) { + log.error("{} while generating session {}", JSON_EXCEPTION_OCCURRED, ex.getMessage()); + throw new JSONException(String.format("%s in generating Session %s", JSON_EXCEPTION_ERROR_MSG, ex.getMessage())); + } catch (SmartAPIException ex) { + log.error("{} while generating session {}", SMART_API_EXCEPTION_OCCURRED, ex.toString()); + throw new SmartAPIException(String.format("%s in generating Session %s", SMART_API_EXCEPTION_ERROR_MSG, ex)); + } + } + +/** * Get Market Data. * * @param params is market data params. @@ -732,6 +758,7 @@ public JSONObject marketData(JSONObject params) throws SmartAPIException, IOExce } catch (JSONException ex) { log.error("{} while placing order {}", JSON_EXCEPTION_OCCURRED, ex.getMessage()); throw new JSONException(String.format("%s in placing order %s", JSON_EXCEPTION_ERROR_MSG, ex.getMessage())); + } } @@ -754,5 +781,6 @@ public JSONObject logout() { return null; } } + +} -} \ No newline at end of file diff --git a/src/main/java/com/angelbroking/smartapi/http/SmartAPIRequestHandler.java b/src/main/java/com/angelbroking/smartapi/http/SmartAPIRequestHandler.java index f32b3a3..a53229a 100644 --- a/src/main/java/com/angelbroking/smartapi/http/SmartAPIRequestHandler.java +++ b/src/main/java/com/angelbroking/smartapi/http/SmartAPIRequestHandler.java @@ -1,23 +1,7 @@ package com.angelbroking.smartapi.http; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.Proxy; -import java.net.URL; -import java.util.Enumeration; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import com.angelbroking.smartapi.SmartConnect; import com.angelbroking.smartapi.http.exceptions.SmartAPIException; - import okhttp3.FormBody; import okhttp3.HttpUrl; import okhttp3.MediaType; @@ -26,6 +10,20 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.Proxy; +import java.net.URL; +import java.util.Enumeration; +import java.util.Map; +import java.util.concurrent.TimeUnit; /** * Request handler for all Http requests @@ -374,6 +372,27 @@ public Request createJsonPostRequest(String url, JSONArray jsonArray, String api return request; } + /** + * Makes a POST request. + * + * @return JSONObject which is received by Smart API Trade. + * @param url is the endpoint to which request has to be sent. + * @param apiKey is the api key of the Smart API Connect app. + * @param accessToken is the access token obtained after successful login + * process. + * @param params is the map of params which has to be sent in the body. + * @throws IOException is thrown when there is a connection related error. + * @throws SmartAPIException is thrown for all Smart API Trade related errors. + * @throws JSONException is thrown for parsing errors. + */ + public String postRequestJSONObject(String apiKey, String url, JSONObject params, String accessToken) + throws IOException, SmartAPIException, JSONException { + Request request = createPostRequest(apiKey, url, params, accessToken); + Response response = client.newCall(request).execute(); + String body = response.body().string(); + return new SmartAPIResponseHandler().handler(response, body); + } + /** * Creates a PUT request. * diff --git a/src/main/java/com/angelbroking/smartapi/http/SmartAPIResponseHandler.java b/src/main/java/com/angelbroking/smartapi/http/SmartAPIResponseHandler.java index 3beeaba..1f532a2 100644 --- a/src/main/java/com/angelbroking/smartapi/http/SmartAPIResponseHandler.java +++ b/src/main/java/com/angelbroking/smartapi/http/SmartAPIResponseHandler.java @@ -1,7 +1,14 @@ package com.angelbroking.smartapi.http; import java.io.IOException; - +import java.util.List; + +import com.angelbroking.smartapi.http.exceptions.ApiKeyException; +import com.angelbroking.smartapi.models.SearchScripResponseDTO; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -17,22 +24,26 @@ import okhttp3.Response; +import static com.angelbroking.smartapi.utils.Constants.APIKEY_EXCEPTION_MESSAGE; +import static com.angelbroking.smartapi.utils.Constants.TOKEN_EXCEPTION_MESSAGE; + /** * Response handler for handling all the responses. */ +@Slf4j public class SmartAPIResponseHandler { public JSONObject handle(Response response, String body) throws IOException, SmartAPIException, JSONException { System.out.println("***************************"); if (response.header("Content-Type").contains("json")) { JSONObject jsonObject = new JSONObject(body); - + // if (jsonObject.optString("data") == null || jsonObject.optString("data") == "") { - if (!jsonObject.has("status") || jsonObject.has("success")) { + if (!jsonObject.has("status") || jsonObject.has("success")) { if (jsonObject.has("errorcode")) { throw dealWithException(jsonObject, jsonObject.getString("errorcode")); } else if (jsonObject.has("errorCode")) { - + throw dealWithException(jsonObject, jsonObject.getString("errorCode")); } } @@ -58,8 +69,9 @@ private SmartAPIException dealWithException(JSONObject jsonObject, String code) return new TokenException(jsonObject.getString("message"), code); case "AG8001": - case "AG8002": - return new DataException(jsonObject.getString("message"), code); + return new TokenException(TOKEN_EXCEPTION_MESSAGE, code); + case "AG8002": + return new DataException(jsonObject.getString("message"), code); case "AB1004": case "AB2000": @@ -87,10 +99,63 @@ private SmartAPIException dealWithException(JSONObject jsonObject, String code) case "AB1001": case "AB1011": return new PermissionException(jsonObject.getString("message"), code); - + case "AG8004": + return new ApiKeyException(APIKEY_EXCEPTION_MESSAGE, code); default: return new SmartAPIException(jsonObject.getString("data not found")); } } + public String handler(Response response, String body) throws SmartAPIException, JSONException, IOException { + if (response.code() == 200) { + return handleResponse(response,body); + } else if (response.code() == 400){ + log.error("Bad request. Please provide valid input"); + return "Bad request. Please provide valid input"; + }else { + log.error("Response or response body is null."); + throw new IllegalArgumentException("Response or response body is null."); + } + } + + private String handleResponse(Response response, String body) throws SmartAPIException, IOException { + try { + JSONObject responseBodyJson = new JSONObject(body); + if(responseBodyJson.getBoolean("status")) { + JSONArray dataArray = responseBodyJson.optJSONArray("data"); + if (dataArray != null && dataArray.length() > 0) { + List stockDTOList = parseStockDTOList(dataArray); + + StringBuilder result = new StringBuilder(); + result.append("Search successful. Found ").append(stockDTOList.size()).append(" trading symbols for the given query:\n"); + + int index = 1; + for (SearchScripResponseDTO stockDTO : stockDTOList) { + result.append(index).append(". exchange: ").append(stockDTO.getExchange()).append(", tradingsymbol: ").append(stockDTO.getTradingSymbol()).append(", symboltoken: ").append(stockDTO.getSymbolToken()).append("\n"); + index++; + } + return result.toString(); + } else { + return "Search successful. No matching trading symbols found for the given query."; + } + }else { + return String.valueOf(handle(response,body)); + } + + } catch (JSONException e) { + log.error("Error parsing response body as JSON.", e.getMessage()); + throw new SmartAPIException("Error parsing response body as JSON."); + } + } + + private List parseStockDTOList(JSONArray dataArray) throws JSONException, SmartAPIException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.readValue(dataArray.toString(), new TypeReference>() { + }); + } catch (IOException e) { + log.error("Error parsing JSON data array.", e); + throw new SmartAPIException("Error parsing JSON data array."); + } + } } diff --git a/src/main/java/com/angelbroking/smartapi/http/exceptions/ApiKeyException.java b/src/main/java/com/angelbroking/smartapi/http/exceptions/ApiKeyException.java new file mode 100644 index 0000000..0a7c38f --- /dev/null +++ b/src/main/java/com/angelbroking/smartapi/http/exceptions/ApiKeyException.java @@ -0,0 +1,14 @@ +package com.angelbroking.smartapi.http.exceptions; + +/** + * Exception raised when invalid API Key is provided for Smart API trade. + */ + +public class ApiKeyException extends SmartAPIException { + + // initialize 2fa exception and call constructor of Base Exception + public ApiKeyException(String message, String code){ + super(message, code); + } +} + diff --git a/src/main/java/com/angelbroking/smartapi/models/SearchScripResponseDTO.java b/src/main/java/com/angelbroking/smartapi/models/SearchScripResponseDTO.java new file mode 100644 index 0000000..6f5ec17 --- /dev/null +++ b/src/main/java/com/angelbroking/smartapi/models/SearchScripResponseDTO.java @@ -0,0 +1,18 @@ +package com.angelbroking.smartapi.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class SearchScripResponseDTO { + + @JsonProperty("tradingsymbol") + private String tradingSymbol; + + @JsonProperty("exchange") + private String exchange; + + @JsonProperty("symboltoken") + private String symbolToken; + +} diff --git a/src/main/java/com/angelbroking/smartapi/sample/Examples.java b/src/main/java/com/angelbroking/smartapi/sample/Examples.java index ee64ad0..1c60bd5 100644 --- a/src/main/java/com/angelbroking/smartapi/sample/Examples.java +++ b/src/main/java/com/angelbroking/smartapi/sample/Examples.java @@ -272,6 +272,15 @@ public void getCandleData(SmartConnect smartConnect) throws SmartAPIException, I String response = smartConnect.candleData(requestObejct); } + + /** Search Scrip Data */ + public void getSearchScrip(SmartConnect smartConnect) throws SmartAPIException, IOException { + JSONObject payload = new JSONObject(); + payload.put("exchange", "MCX"); + payload.put("searchscrip", "Crude"); + smartConnect.getSearchScrip(payload); + } + /** * Market Data * To Retrieve Market Data with different modes use. @@ -292,6 +301,7 @@ public void getMarketData(SmartConnect smartConnect) throws SmartAPIException, I } + public void tickerUsage(String clientId, String feedToken, String strWatchListScript, String task) throws SmartAPIException { diff --git a/src/main/java/com/angelbroking/smartapi/sample/Test.java b/src/main/java/com/angelbroking/smartapi/sample/Test.java index 92a1316..6ef34e0 100644 --- a/src/main/java/com/angelbroking/smartapi/sample/Test.java +++ b/src/main/java/com/angelbroking/smartapi/sample/Test.java @@ -87,9 +87,14 @@ public static void main(String[] args) throws SmartAPIException { /* System.out.println("Historic candle Data"); */ examples.getCandleData(smartConnect); + + /* System.out.println("Search script api"); */ + examples.getSearchScrip(smartConnect); + /* System.out.println("Market Data"); */ examples.getMarketData(smartConnect); + /* System.out.println("logout"); */ examples.logout(smartConnect); diff --git a/src/main/java/com/angelbroking/smartapi/utils/Constants.java b/src/main/java/com/angelbroking/smartapi/utils/Constants.java index bbb7618..8f9d212 100644 --- a/src/main/java/com/angelbroking/smartapi/utils/Constants.java +++ b/src/main/java/com/angelbroking/smartapi/utils/Constants.java @@ -85,4 +85,8 @@ public class Constants { public static final String SMART_API_EXCEPTION_OCCURRED = "SmartAPIException occurred "; public static final String IO_EXCEPTION_OCCURRED = "IOException occurred "; public static final String JSON_EXCEPTION_OCCURRED = "JSONException occurred "; + + public static final String TOKEN_EXCEPTION_MESSAGE = "Unauthorized access. Please provide a valid or non-expired jwtToken."; + public static final String APIKEY_EXCEPTION_MESSAGE = "Invalid or missing api key. Please provide a valid api key."; + } diff --git a/src/test/java/com/angelbroking/smartapi/SmartConnectTest.java b/src/test/java/com/angelbroking/smartapi/SmartConnectTest.java index 346b276..f72886c 100644 --- a/src/test/java/com/angelbroking/smartapi/SmartConnectTest.java +++ b/src/test/java/com/angelbroking/smartapi/SmartConnectTest.java @@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j; import org.json.JSONArray; import org.json.JSONException; + import org.json.JSONObject; import org.junit.Before; import org.junit.Test; @@ -15,6 +16,12 @@ import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + import static com.angelbroking.smartapi.utils.Constants.IO_EXCEPTION_ERROR_MSG; import static com.angelbroking.smartapi.utils.Constants.IO_EXCEPTION_OCCURRED; import static com.angelbroking.smartapi.utils.Constants.JSON_EXCEPTION_ERROR_MSG; @@ -29,8 +36,7 @@ @RunWith(MockitoJUnitRunner.Silent.class) @Slf4j public class SmartConnectTest { - - @Mock + private SmartAPIRequestHandler smartAPIRequestHandler; @Mock @@ -41,6 +47,43 @@ public class SmartConnectTest { private String apiKey; private String accessToken; + @Mock + private SmartConnect smartConnect; + + @Before + public void setup() { + // Set up any necessary configurations or dependencies + } + + @Test + public void testGetSearchScript_Success() throws SmartAPIException, IOException { + // Mock the necessary objects + JSONObject payload = new JSONObject(); + when(smartConnect.getSearchScrip(payload)).thenReturn("response-data"); + + // Call the method under test + String result = smartConnect.getSearchScrip(payload); + // Assert the result + assertEquals("response-data", result); + + } + + @Test(expected = SmartAPIException.class) + public void testGetSearchScript_Exception() throws SmartAPIException, IOException { + JSONObject payload = new JSONObject(); + SmartAPIException expectedException = new SmartAPIException("Simulated SmartAPIException"); + when(smartConnect.getSearchScrip(payload)).thenThrow(expectedException); + try { + smartConnect.getSearchScrip(payload); + } catch (SmartAPIException e) { + throw new SmartAPIException(String.format("The operation failed to execute because of a SmartAPIException error in Search scrip api data %s", e)); + } + verify(smartConnect).getSearchScrip(payload); + } + + + + private static JSONObject createMarketDataResponse() { JSONObject jsonObject = new JSONObject(); @@ -282,4 +325,5 @@ private JSONObject getMarketDataRequest(String mode) { return payload; } -} \ No newline at end of file +} +