Sunday, July 24, 2022

How to parse JSON string to Object and vice versa in Groovy Grails application

In this tutorial, we will learn how to parse or convert the object to JSON string and JSON string to object.

Convert Object to JSON String:

Let's look into the example, here we will parse the Map to JSON string.

Map mapToConvert = [username : "Test", phonenumber : "+10656564"]
List listToConvert = ["Test", "+10656564"]

Now, let's convert to JSON string using JSON class

import grails.converters.JSON
        Map mapToConvert = [username : "Test", phonenumber : "+10656564"]
        List listToConvert = ["Test", "+10656564"]
        String jsonStringFromMap =  (mapToConvert as JSON).toString()
        String jsonStringFromList =  (listToConvert as JSON).toString()
        

You can use any other object data type to convert as above

Convert JSON String to Object:

For converting JSON string to object we are using JsonSlurper as below:

import grails.converters.JSON
import groovy.json.JsonSlurper
		Map mapToConvert = [username : "Test", phonenumber : "+10656564"]
        List listToConvert = ["Test", "+10656564"]
        String jsonStringFromMap =  (mapToConvert as JSON).toString()
        String jsonStringFromList =  (listToConvert as JSON).toString()
        Map convertedMap = parseTo(jsonStringFromMap)
        List convertedList = parseTo(jsonStringFromList)
        
        private static def parseTo(String jsonString) {
        return new JsonSlurper().parseText(jsonString)
    }
        
Share:

Tuesday, July 19, 2022

Java round double to decimal places with up and down rounding

This is a quick tutorial on how we can round the double or float value to x decimal places

Here we are using DecimalFormat class to do so. Let's look at the example. Here we are creating DecimalFormatter.java class

Round to x decimal places:

import java.text.DecimalFormat;

public class DecimalFormatter {

    public static void main(String[] args) {
        // normal format
        System.out.println("Normal format: " + format(2.0451, "0.00"));
        System.out.println("Normal format add extra zero: " + format(2.045, "0.00000"));
    }
    
    private static String format(double value, String precision) {
        DecimalFormat df = new DecimalFormat(precision);
        return df.format(value);
    }
}    

In the above example, we are using a default rounding mode i.e DecimalFormat by default uses half even rounding mode to format. i.e if the decimal value is greater or equal to 5 then it will round up for that precision else use the same value for that precision. Here, 2.0451 will be formatted to 2.05 as we are using the two decimal place 0.00

Also, in the second example, we have a double value of 2.045 but we are trying to round 0.00000 five decimal places so it will add the extra 2 zeros to the double value.

Output:

Normal format: 2.05
Normal format add extra zero: 2.04500

Round Up to x decimal places:

import java.math.RoundingMode;
import java.text.DecimalFormat;

public class DecimalFormatter {

    public static void main(String[] args) {
        // round up format
        System.out.println("Round up format: " + formatRoundUp(1.03510001, "0.0000"));
    }
    
     private static String formatRoundUp(double value, String precision) {
        DecimalFormat df = new DecimalFormat(precision);
        df.setRoundingMode(RoundingMode.UP);
        return df.format(value);
    }
}    

Here, we are setting rounding mode to Up for DecimalFormat so it will round up the decimal value, whether it is greater or equal to 5 or not for the given decimal places.

Output:

Round up format: 1.0352

Round Down to x decimal places:

import java.math.RoundingMode;
import java.text.DecimalFormat;

public class DecimalFormatter {

    public static void main(String[] args) {
        // round down format
        System.out.println("Round down format: " + formatRoundDown(1.0316, "0.000"));
    }
    
	private static String formatRoundDown(double value, String precision) {
        DecimalFormat df = new DecimalFormat(precision);
        df.setRoundingMode(RoundingMode.DOWN);
        return df.format(value);
    }
}    

In the above example, we are setting rounding mode to Down such that it will round down the decimal value whether it is greater or equal to 5.

Output:

Round down format: 1.031
Share:

Saturday, July 9, 2022

Setup and run Appium for Android with Java client on Linux

How to set up and run Appium for android apps with Java client with Emulator setup.
1. Introduction:

Appium is an open-source automation tool for mobile applications. It is a cross-platform tool that is used to automate native, hybrid, and mobile web applications running on ios, android, and windows platforms. In this tutorial, we are going to set up Appium and run a sample example.


2. prerequisites:

  1. Installed JDK
  2. NodeJs
3. Download and run Appium:

Download the latest version of the Appium desktop App Image from here. Under "Assets" download AppImage for Linux. Go to download directory and open terminal. Give permission for the app image file.

 
chmod a+x

Now, execute the AppImage file either by double-clicking it or running the following command.
./AppImage
You can see as the Appium Ui started similar to this.


Now, start the Appium server with default port 4723 by clicking the "Start Server" button.




4. Setup Android Emulator:

In order to set up an Android Emulator, you can set it up via a command-line tool without installing an android studio but here, we are going to download an android studio and set up android SDK to run Emulator. For this download android studio from here. Extract the downloaded tar file.
 
tar -xvf android-studio.tar.gz

Go to the extracted directory cd to the bin directory you can see the studio.sh simply execute this sh file by opening the terminal. Which will popup the android studio setting import option. Select "Do not import settings". Click "Next", select "Standard" and verify the setting. Make sure to verify the path of SDK installed.


Click "Next" and "Finish" which will download the SDK on the SDK folder mentioned.

Now, we need to configure the android virtual device for this click "Configure" and select "AVD Manager".


If you see "dev/kvm device: permission denied" you can give permission for the user by using the command:
sudo chown yourLinuxUser /dev/kvm
Create a Virtual Device







After that, select the downloaded image and click "Next" and verify android virtual device configuration and click "Finish" to finish the virtual device setup. Now you can launch the virtual device in the Emulator.



Now, let's configure the SDK path in our system. For this open bashrc file by using the command:
 
  sudo gedit ~/.bashrc

Add the following line at the end of the file.
 
  export ANDROID_HOME=/home/kchapagain/Downloads/Sdk
export PATH=$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$PATH
export PATH=$PATH:$ANDROID_HOME/platform-tools
Make sure you provided the correct download SDK path in "ANDROID_HOME" in my case it's "/home/kchapagain/Downloads/Sdk". Save the file and reload the changes by using the command:
 
  source ~/.bashrc

You can launch the device on Emulator via the command line so that you don't need to open the android studio each time you run the device. For this, open the terminal use command:
emulator -list-avds
Output:
Pixel_2_API_28
Pixel_2_API_28_2

Here, we have two virtual devices installed in your case it will output the list of devices downloaded.

Now, run the device on the Emulator using command:
emulator -avd Pixel_2_API_28
User your device name instead "Pixel_2_API_28". It will run the android virtual device on Emulator.

 
5. Download and load library for Java Client:

Download the Appium java client jar from here.



Similarly, download JUnit jar from here.  Download selenium from here.

Create a java project and create a package called "libs" under "src". Copy all the downloaded jar file in this package and load the jar file from Ide.

For IntelliJ Idea:

Go to: File >> Project Structure(Ctr + Alt + Shift + s) >> Libraries and click the + icon on the top left corner and select package.


6. Setup driver config for Appium:

Create a sample class to config the android driver. Here, we are using the sample calculator app to test and the driver setup config looks like below:
 @Before
    public static void setUp() throws MalformedURLException {
        DesiredCapabilities cap = new DesiredCapabilities();
        cap.setCapability("platformName", "Android");
        cap.setCapability("deviceName", "50b6ab0");
        cap.setCapability("appPackage", "");
        cap.setCapability("appActivity", "");
        cap.setCapability("automationName", "UiAutomator1");
        cap.setCapability("autoGrantPermissions", true);
        cap.setCapability("autoAcceptAlerts", "true");
        driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), cap);
    }
Share:

How to call parent window function from child window in javascript

In this tutorial, we will learn how to call the parent window function from the child window in javascript.

In some cases, we need to open the child browser window as a modal or dialog. For example, if need to handle and load the redirect URL in a single-page application.

While using third-party services providers for e.g PayPal we are supposed to get the redirect URL to load the payment page for security reasons. In this case, we will load that redirect URL in the child window as a modal and the user will proceed with the payment securely. Once the payment is done we need to notify the parent window or our single-page application that the payment got succeeded or pass some token. In this case, we need to call the parent window function from the child window.

Let's look into the example, where we are passing token from the child window to the parent function.

Here, while opening the child window we will register the function in the document interface that represents any web page loaded in the browser and serves as an entry point into the web page's content

document.responseToken = function (token){
          // manuplate the token
        }

Now let's add the calling function from the child window.

window.opener.document.responseToken(token);
        window.close();

Here, we are using window.opener The Window interface's opener property returns a reference to the window that opened the window i.e parent window. So we can get the responseToken function that we registered previously. This will pass the token to the reference or parent window. After that window.close() will close the child dialog window.

Share:

Tomcat Invalid character found in the request target

While running an application in the tomcat server, sometimes we might get the following error.

org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header
 Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
	java.lang.IllegalArgumentException: Invalid character found in the request target [/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=HelloThinkPHP21 ]. The valid characters are defined in RFC 7230 and RFC 3986
		at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:509)
		at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:511)
		at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
		at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831)
		at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1673)
		at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
		at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
		at java.lang.Thread.run(Thread.java:748)

First, make sure that the domain pointed to the server is working fine or not expired.

From tomcat, it is suggested that

The HTTP/1.1 specification requires that certain characters are %nn encoded when used in URI query strings. Unfortunately, many user agents including all the major browsers are not compliant with this specification and use these characters in unencoded form. To prevent Tomcat rejecting such requests, this attribute may be used to specify the additional characters to allow. If not specified, no additional characters will be allowed. The value may be any combination of the following characters: " < > [ \ ] ^ ` { | } . Any other characters present in the value will be ignored.

Tomcat increased its security and no longer allows raw square brackets or the above characters in the query string.

In order to resolve this issue use relaxedQueryChars attributes inside the server.xml file of tomcat server. Make sure to back up the server.xml file before we change it.

Open the server.xml file using vim or vi and type Shift + i

sudo vim /path_to_tomcat/conf/server.xml

Press Esc and hit :wq! and save the changes

Restart the tomcat server.

Share:

Thursday, July 7, 2022

How to use custom validation class in Grails Application

In this tutorial, we will learn how to add custom validation in the Grails application.

Grails domain classes support the validation by default. So we can simply add the constrains for the domain classes fields as below

class User {
    String username
    String password
    String email
    Integer age
    
    
	static constraints = {
        username blank: false, unique: true
        password size: 5..15, blank: false
        email email: true, blank: false
        age min: 18
	}
}

Now we can simply validate the object using validate() or hasErrors() methods

User user = new User(params) // map the request parameters to user object
if (!user.validate()) {
    // format errors and send to the client
}
User user = new User(params) // map the request parameters to user object
if (user.hasErrors()) {
    // format errors and send to the client
}

Now, let's say that we want to validate some non-domain fields or use a custom validation class to validate the request parameter sent by the user.

We can do so by implementing Validateable trait so that we can add the constrain for the fields in the custom class. Let's look into the example

import grails.validation.Validateable

class UserCommand implements Validateable {

    public static final emailRegex = "^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+\$"

    UserCommand(def requestParams) {
        email = requestParams.email
        password = requestParams.password
        username = requestParams.username
        age = requestParams.age
    }

    static constraints = {
        username blank: false, unique: true
        password size: 5..15, blank: false
        email email: true, blank: false, validator: emailValidator
        age min: 18
    }
    
    static emailValidator = { String value, UserCommand command ->
        if (!value.find(emailRegex)) {
            return "email.invalid" // getting message from message.properties file
        }
    }
}

In the above example, we can add the constraints after implementing the Validateable on the custom class and do the validation logic similar to the domain classes validation.

Now, let's utilize the above command class inside the controller.

UserCommand userCommand = new UserCommand(requestParams)
        if (!userCommand.validate()) {
            Map result = [success: false, errors: []]
            result.errors = getCommandErrors(userCommand)
            render(result as JSON)
            return
        }
def messageSource

public static List getCommandErrors(def command) {
        List errors = []
        command.errors.allErrors.each { ObjectError error ->
            String msg = messageSource.getMessage(error.code, error.arguments, error.defaultMessage)
            errors.add(msg)
        }
        return errors
    }

For more details visit Grails Validation

Share:

Wednesday, July 6, 2022

How to enable CORS in Grails Application

In this tutorial, we are going to learn how to enable the cross-origin resources sharing CORS in a Grail Application.

Introduction:

Cross-origin resource sharing is the way to trust between the two web services or applications. So if the two web services don't satisfy then a CORS issue may arise.

Cors is a mechanism for the web application that controls listening to certain request from other web applications which is not hosted on the same server. It will not grant access to the content from other applications. So, in order to interact between two different web applications, we need to enable the Cors for that particular app.

Enable CORS in Grails Application:

We can enable cors in application.yml as below

grails:
    cors:
        enabled: true

The above configuration enables the cors for all the origins i.e all applications can interact with our application.

Enable Cors for a specific domain:

application.yml

grails:
    cors:
        enabled: true
        allowedOrigins:
            - https://example.com

This will allow the request only for the domain example.com

Enable Cors for specific requests or URLs:

application.yml

grails:
    cors:
        enabled: true
        allowedHeaders:
            - Content-Type
        mappings:
            '[/api/**]':
                allowedOrigins:
                    - https://example.com

This will allow all the requests from URLs that start with /api for example.com domain. Note that the mapping key must be made with bracket notation i.e [/api/**]

For more detail please visit Grails CORS.

Share:

How to create Local and Remote Tags on Git

This is a quick tutorial on how to create a Tag in Git

Creating the Tag in Git is important because it gives the ability to mark or point to a repository's history.

Basically, we will release the tag once the new version is deployed and finalized. By doing so, if we need the previous Tag version code, we can create a new branch from this tag and use the branch for further manipulation.

Create a Tag:

Before creating the tag make sure to switch to the branch where we want to create a Tag. Use the following command to switch the branch.

git checkout development

Here, development is the branch name to switch, use your own branch name.

Create a tag:
git tag -a v0.0.1-06-07-2022 -m "version 0.0.1 released"

This will create the tag with the name v0.0.1-06-07-2022; please use your own version name and "version 0.0.1 released" as a message

List the Tag:

Now, let's list the available tags created.

git tag

We can see all the created tag output below.

v0.0.1-06-07-2022

Push to the repository:

Once the tag is created it will available only locally. If we are using a code management service provider like Github, Bitbucket, or Gitlab then we need to push the tag to a point on the repository. Use the following command to push

git push origin v0.0.1-06-07-2022

Once the tag is pushed we can see the output similar to below

Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 176 bytes | 176.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To https://bitbucket.org/repo_name.git
 * [new tag]         v0.0.1-06-07-2022 -> v0.0.1-06-07-2022

Delete the Tag:

Let's first delete the tag created from the remote.

git push --delete origin v0.0.1-06-07-2022

Once the tag is deleted we can see the output similar as below

To https://bitbucket.org/repo_name.git
 - [deleted]         v0.0.1-06-07-2022

Now, let's delete the tag from locally

git tag -d v0.0.1-06-07-2022
Share:

Binance API code -1013 msg - Filter failure: PRICE_FILTER

While implementing the Binance API, we might get the following error.

{
"code":-1013,
"msg":"Filter failure: PRICE_FILTER"
}

This might be due to the mismatch of the given Filter for the price by Binance. As from the Binance Spot Api documentation, the condition that needs to be satisfied is

price >= minPrice
price <= maxPrice
price % tickSize == 0

We can get the Filter value for PRICE_FILTER as below.

For BTC USDT:

https://api.binance.us/api/v3/exchangeInfo?symbol=BTCUSDT

OR

https://api.binance.com/api/v3/exchangeInfo?symbol=BTCUSDT

The output of the above endpoint is:

{
   "timezone":"UTC",
   "serverTime":1657086978303,
   "rateLimits":[
      {
         "rateLimitType":"REQUEST_WEIGHT",
         "interval":"MINUTE",
         "intervalNum":1,
         "limit":1200
      },
      {
         "rateLimitType":"ORDERS",
         "interval":"SECOND",
         "intervalNum":10,
         "limit":100
      },
      {
         "rateLimitType":"ORDERS",
         "interval":"DAY",
         "intervalNum":1,
         "limit":200000
      },
      {
         "rateLimitType":"RAW_REQUESTS",
         "interval":"MINUTE",
         "intervalNum":5,
         "limit":6100
      }
   ],
   "exchangeFilters":[
      
   ],
   "symbols":[
      {
         "symbol":"BTCUSDT",
         "status":"TRADING",
         "baseAsset":"BTC",
         "baseAssetPrecision":8,
         "quoteAsset":"USDT",
         "quotePrecision":8,
         "quoteAssetPrecision":8,
         "baseCommissionPrecision":8,
         "quoteCommissionPrecision":8,
         "orderTypes":[
            "LIMIT",
            "LIMIT_MAKER",
            "MARKET",
            "STOP_LOSS_LIMIT",
            "TAKE_PROFIT_LIMIT"
         ],
         "icebergAllowed":true,
         "ocoAllowed":true,
         "quoteOrderQtyMarketAllowed":true,
         "allowTrailingStop":false,
         "cancelReplaceAllowed":false,
         "isSpotTradingAllowed":true,
         "isMarginTradingAllowed":false,
         "filters":[
            {
               "filterType":"PRICE_FILTER",
               "minPrice":"0.01000000",
               "maxPrice":"100000.00000000",
               "tickSize":"0.01000000"
            },
            {
               "filterType":"PERCENT_PRICE",
               "multiplierUp":"5",
               "multiplierDown":"0.2",
               "avgPriceMins":5
            },
            {
               "filterType":"LOT_SIZE",
               "minQty":"0.00000100",
               "maxQty":"9000.00000000",
               "stepSize":"0.00000100"
            },
            {
               "filterType":"MIN_NOTIONAL",
               "minNotional":"10.00000000",
               "applyToMarket":true,
               "avgPriceMins":5
            },
            {
               "filterType":"ICEBERG_PARTS",
               "limit":10
            },
            {
               "filterType":"MARKET_LOT_SIZE",
               "minQty":"0.00000000",
               "maxQty":"29.33387056",
               "stepSize":"0.00000000"
            },
            {
               "filterType":"MAX_NUM_ORDERS",
               "maxNumOrders":200
            },
            {
               "filterType":"MAX_NUM_ALGO_ORDERS",
               "maxNumAlgoOrders":5
            }
         ],
         "permissions":[
            "SPOT"
         ]
      }
   ]
}

For PRICE_FILTER:

{
               "filterType":"PRICE_FILTER",
               "minPrice":"0.01000000",
               "maxPrice":"100000.00000000",
               "tickSize":"0.01000000"
            },

Lets look into the first condition; price >= minPrice price <= maxPrice

We need to have a price that must be greater or equal to min price and less than or equal to the max price.

Let's look into the second condition: price % tickSize == 0; For this, we might get the precision issue if we don't parse the price. Here, tickSize is in the 2 decimal place so we need to parse the price in 2 decimal places to validate the filter. This will help resolve the issue.

Share:

Tuesday, July 5, 2022

Binance Api code-1013 msg:Filter failure: MIN_NOTIONAL

This is a quick tutorial on how we can resolve the Filter failure: MIN_NOTIONAL

While using Binance API, when placing the order and calling API we might get the following error.

{
"code":-1013,
"msg":"Filter failure: MIN_NOTIONAL"
}

As described on binance spot API documentation, The MIN_NOTIONAL filter defines the minimum notional value allowed for an order on a symbol. An order's notional value is the price * quantity

Now let's look into what is the required values for MIN_NOTIONAL. We can get it from the exchange info endpoint as below

For BTC USDT

https://api.binance.us/api/v3/exchangeInfo?symbol=BTCUSDT

The Output for the above endpoint is:

{
   "timezone":"UTC",
   "serverTime":1657007585516,
   "rateLimits":[
      {
         "rateLimitType":"REQUEST_WEIGHT",
         "interval":"MINUTE",
         "intervalNum":1,
         "limit":1200
      },
      {
         "rateLimitType":"ORDERS",
         "interval":"SECOND",
         "intervalNum":10,
         "limit":100
      },
      {
         "rateLimitType":"ORDERS",
         "interval":"DAY",
         "intervalNum":1,
         "limit":200000
      },
      {
         "rateLimitType":"RAW_REQUESTS",
         "interval":"MINUTE",
         "intervalNum":5,
         "limit":6100
      }
   ],
   "exchangeFilters":[
      
   ],
   "symbols":[
      {
         "symbol":"BTCUSDT",
         "status":"TRADING",
         "baseAsset":"BTC",
         "baseAssetPrecision":8,
         "quoteAsset":"USDT",
         "quotePrecision":8,
         "quoteAssetPrecision":8,
         "baseCommissionPrecision":8,
         "quoteCommissionPrecision":8,
         "orderTypes":[
            "LIMIT",
            "LIMIT_MAKER",
            "MARKET",
            "STOP_LOSS_LIMIT",
            "TAKE_PROFIT_LIMIT"
         ],
         "icebergAllowed":true,
         "ocoAllowed":true,
         "quoteOrderQtyMarketAllowed":true,
         "allowTrailingStop":false,
         "cancelReplaceAllowed":false,
         "isSpotTradingAllowed":true,
         "isMarginTradingAllowed":false,
         "filters":[
            {
               "filterType":"PRICE_FILTER",
               "minPrice":"0.01000000",
               "maxPrice":"100000.00000000",
               "tickSize":"0.01000000"
            },
            {
               "filterType":"PERCENT_PRICE",
               "multiplierUp":"5",
               "multiplierDown":"0.2",
               "avgPriceMins":5
            },
            {
               "filterType":"LOT_SIZE",
               "minQty":"0.00000100",
               "maxQty":"9000.00000000",
               "stepSize":"0.00000100"
            },
            {
               "filterType":"MIN_NOTIONAL",
               "minNotional":"10.00000000",
               "applyToMarket":true,
               "avgPriceMins":5
            },
            {
               "filterType":"ICEBERG_PARTS",
               "limit":10
            },
            {
               "filterType":"MARKET_LOT_SIZE",
               "minQty":"0.00000000",
               "maxQty":"29.33387056",
               "stepSize":"0.00000000"
            },
            {
               "filterType":"MAX_NUM_ORDERS",
               "maxNumOrders":200
            },
            {
               "filterType":"MAX_NUM_ALGO_ORDERS",
               "maxNumAlgoOrders":5
            }
         ],
         "permissions":[
            "SPOT"
         ]
      }
   ]
}

For MIN_NOTIONAL:

{
               "filterType":"MIN_NOTIONAL",
               "minNotional":"10.00000000",
               "applyToMarket":true,
               "avgPriceMins":5
            },

So this defines that the limit order placed must be minimum of $10 i.e price * quantity = total here, the total must be at least 10. If this doesn't satisfy then we will get the mentioned error. So the condition must be satisfied while calling the API.

Share: