Showing posts with label API. Show all posts
Showing posts with label API. Show all posts

Wednesday, August 21, 2019

NodeJs MySQL Observer

Hello,

In this blog we are going to learn how to use NodeJs to observe changes in MySql databases. This is useful when you want to track MySQL changes and based on that want to send some events to frontends or want to do any other actions.

For this first of all you have to enable binary logging in you database. Binary logging is very much useful for real time MySQL replication. In Amazon RDS, it's by default available and you can switch on it from configurations. For your local database if you are using MAMP, you can do following trick.

Create a file with name my.cnf and add following content to it.

[mysqld]
server-id = 1
default-storage-engine = InnoDB
log-bin=bin.log
log-bin-index=bin-log.index
max_binlog_size=100M
expire_logs_days = 10
binlog_format=row
socket=mysql.sock

Add this file to conf folder of your MAMP directory and restart MySQL server. This will enable binary logging in your database.

Now to observe this changes we use npm package called zongji . Install it with NPM.

Add following code to your NodeJs script.

var ZongJi = require('zongji');
var _underScore = require('underscore');

var zongji = new ZongJi({
    user : 'YOUR_USERNAME',
    password : "YOUR_PASSWORD",
    database: 'YOUR_DATABASE',
    socketPath : '/Applications/MAMP/tmp/mysql/mysql.sock'
});

Now add event on binlog.

zongji.on('binlog', function(evt) {

});

This event is triggered whenever there is a change in any of your database tables.

Inside this event you can have logic of checking new rows, updates rows, deleted rows.
zongji.on('binlog', function(evt) {
if (evt.getEventName() === 'writerows' || evt.getEventName() === 'updaterows' || evt.getEventName() === 'deleterows') {
var database = evt.tableMap[evt.tableId].parentSchema; 
        var table =  evt.tableMap[evt.tableId].tableName; 
        var columns = evt.tableMap[evt.tableId].columns; 
        _underScore.each(evt.rows, function(row) {
        });
}
});

At last start the process and pass the events you want to watch.
zongji.start({
  includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows']
});

Saturday, July 21, 2018

Step By Step : Laravel - Publish Post on Facebook Page with Graph API

Hello,

In this quick blog we will quickly go through how to publish a post on Facebook Page with graph API from your Laravel Application.

Step 1 : Create Facebook App

Login to https://developers.facebook.com/ with your Facebook account and create an app.

Now go to Settings -> Basic of your Facebook app and copy app id and app secret.


Step 2 : Install Facebook PHP SDK

Run following command to install PHP SDK in your laravel application.

composer require facebook/graph-sdk

Step 3: Get Exchange Token from Facebook App

Go to Facebook Graph API Explorer. Here is the link  https://developers.facebook.com/tools/explorer/

From here first select your application and then select the page for Page Access Token


When you are generating this token please select graph API version 2.2 and select the following permissions.

publish_actions, 
manage_pages, 
pages_show_list, 
publish_pages, 
public_profile

Step 4 : Generate Access Token 

From the exchange token generated in Step 3, Get the access token.

$url = 'https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id= YOUR_APP_ID&client_secret= YOUR_APP_SECRET&fb_exchange_token=YOUR_TOKEN';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
$output = json_decode($result);
$access_token = $output->access_token;

Step 5 : Call Facebook Graph API to Send Post to Page

Create SDK object

$fb = new \Facebook\Facebook([
'app_id' => 'YOUR_APP_ID',
'app_secret' => 'YOUR_APP_SECRET',
'default_graph_version' => 'v2.2',
]);

Generate Payload

$linkData = [
 'link' => YOUR_LINK,
 'message' => YOUR_TITLE
];

Call Graph API.

$pageAccessToken =$access_token;

try {
 $response = $fb->post('/me/feed', $linkData, $pageAccessToken);
} catch(Facebook\Exceptions\FacebookResponseException $e) {
 echo 'Graph returned an error: '.$e->getMessage();
} catch(Facebook\Exceptions\FacebookSDKException $e) {
 echo 'Facebook SDK returned an error: '.$e->getMessage();
}
$graphNode = $response->getGraphNode();

Now check your Facebook page there will be post on your Facebook page. Hope this helps you.

Sunday, September 25, 2016

Cordova Applicaiton Select Any File From SD Card and Upload to Server

Hello,

Recently in one of my project we were building android application with Cordova. There was a requirement where user can choose any files from SD card and upload it to server.

So in this blog I am going to explain how to do this.

Our challenges was user can pick any file like audio, video, image, pdf etc. So we have to properly save it on server with proper extension.

First of all you will need two plugins.

1) Cordova File Transfer Plugin

You can install it via following command.

cordova plugin add cordova-plugin-file-transfer

2) File Chooser Plugin

You can check this plugin here and can install it via following command.

cordova plugin add http://github.com/don/cordova-filechooser.git

Now once you install file choose plugin you can use following command to open file selector in android.

fileChooser.open(function(uri) {
    alert(uri);
});

Now the problem with this file chooser plugin is that it gives content path like this.

content://media/images/4

However this path can not be used with File Transfer Plugin as it needs absolute file URI like this.

file:///sdcard/0/downloads/myPDF.pdf

So we have to edit this file chooser plugin little bit. Go to your android project and open FileChooser.java file from the com.megster.cordova package and check the following function.


public void onActivityResult(int requestCode, int resultCode, Intent data) {

}

See the following line of code in this function.

Uri uri = data.getData();

 callback.success(uri.getPath());

So form here it returns the content path.

We have to convert that content path to absolute path. So add following functions in FileChooser.java


public static String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
            String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());

    }

Now update onActivityResult function as follow.

callback.success(getPath(cordova.getActivity(), uri));

It will send you correct absolute URI.  Now in your JavaScript.

var filePath = '';
var fileType = '';

fileChooser.open(function(obj) {
            filePath = 'file://'+obj;
            fileType= obj.substring(uri.lastIndexOf('.'));
        });

As you see above we are storing file path and file extension in two variables. Now following is the code to upload your file to server.

        var win = function (r) {
                alert('file uploaded');
        }

        var fail = function (error) {
         }

        var options = new FileUploadOptions();
        options.fileKey = "file";
        options.fileName = filePath.substr(this.evidencePath.lastIndexOf('/') + 1);
        options.mimeType = "text/plain";

        var params = {};
        params.fileType = fileType;
        options.params = params;

        var ft = new FileTransfer();
        ft.upload(filePath, encodeURI("http://yourserverpath"), win, fail, options);

On the server side your PHP code should be as follow.

$file_type = $_POST['fileType'];
$fileName = time()."_".$file_type;
move_uploaded_file($_FILES["file"]["tmp_name"], "/yourserverpath/".$fileName);
return json_encode(array('success'=>true,'index'=>$index, 'server_path'=>"http://serverpath".$fileName));

That's it, hope this helps you.

Wednesday, June 15, 2016

Magento 2.0 Products API Get all the Details Of Products.

Hello,

It's been long since I have worked on Magento and published a blog on it. But recently my team was stuck in Magento REST Products API so I have to look into it. Basically the problem was

V1/categories/:categoryId/products API.

This API gives following result in an array.

{
     "sku": "Cotton"
     "position": 10001
     "category_id": "2"
}

Now that's little bit weird as there are no other info of products like product name, description etc. So to get this info we have to use other API which is.


V1/products/:sku

But practically that is not a solution. As we may have n number of products so we can call details API n number of times. So what to do in this case. I have spent almost couple of hours on this but could not get any solution so finally this is what I did.


I used SOAP V1/categories/:categoryId/products API in other PHP file go get array of SKUs and loop through an array and formed following strings of SKUs.

sku1,sku2,sku3,sku4,sku5

Now I used SOAP V1/products API will following search criteria.

V1/products?searchCriteria[filter_groups][0][filters][0][field]=sku&searchCriteria[filter_groups][0][filters][0][value]=sku1,sku2, sku3,sku4,sku5&searchCriteria[filter_groups][0][filters][0][condition_type]=in

As you can see above I used filed SKU in search criteria , passed all the SKU I need in comma separated format and used condition IN.

But wait there was another problem,  After calling above API, I did not get any result. I used few different tricks like

[sku1,sku2,sku3,sku4,sku5]

['sku1','sku2','sku3','sku4','sku5']

'sku1','sku2','sku3','sku4','sku5'

But nothing worked. Again I tried to find solution for sometime ad found solution in Magento 2 GitHub repo.

Please check this link.

https://github.com/magento/magento2/commit/65819d2f61a63e4fa9fc978220f8662ee5472791

This problem is going to be fixed in next release but we could not wait so here I updated Magento code myself.

Open the following file.

lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php

Go to line no 2792

and add following code.

if (($key == 'in' || $key == 'nin') && is_string($value)) {
$value = explode(',', $value);
}

That's it and now run following API.

V1/products?searchCriteria[filter_groups][0][filters][0][field]=sku&searchCriteria[filter_groups][0][filters][0][value]=sku1,sku2, sku3,sku4,sku5&searchCriteria[filter_groups][0][filters][0][condition_type]=in


It should give all the details of mentioned SKU. Hope this helps you.

Sunday, April 24, 2016

Laravel Swagger Integration for API and Code Documentation

I know, I know, all developers hate documentation. Even I also don't like it but it's an industry practice and we have to follow this. In this blog I am going to explain how you can integrate swagger most popular framework for API with Laravel for creating API docs. Swagger is an incredible way to easily document and test your APIs. So here is the step by step guides.

1) Go to your laravel project on Terminal and include Swagger PHP package with composer.

composer require zircote/swagger-php

This will install swagger php package to laravel and will update your vendor folder. Now we have to create documentation.

2) There is standard syntax for creating docs with swagger. For each API functions and classes you have to add swagger config and config have all the standard keys to define what kind of information you will include in docs. You can check this on following URL.


Hover mouse on left side object and it will give you information on right side.


This you have to add in add in your controllers and models in your laravel project. So lets define some basic info. For example see following swagger config we have added. This we have added a comments to top of the controller function.

@SWG\Swagger(
     *     basePath="",
     *     schemes={"http"},
     *     @SWG\Info(
     *         version="1.0",
     *         title="Sample API",
     *         @SWG\Contact(name="Hiren Dave", email="hdave10@gmail.com"),
     *     ),
     *   @SWG\Get(
     *     path="/",
     *     description="Returns basic information",
     *     produces={"application/json"},
     *     @SWG\Response(
     *         response=200,
     *         description="Application Overview"
     *     )
     *   )
     * )

And following is my API function.

        public function index()
{
              return Response::json(array('success'=>false,'message'=>'This is Basic Info of API'));
}

It have basic info like API info and API path and output information.

3) Build Swagger Docs.

Go to your laravel project in terminal and run following command.

./vendor/bin/swagger ./app/Http

It will generate swagger.json file in your root folder with all the swagger config you have added in your controller. 

4) Add swagger UI to project to show this docs.

Go to following link to download swagger UI.


Download it and copy all the files from dist folder to your public/docs folder. Now open public/docs/index.html page and update path of your swagger.json file in following code.

if (url && url.length > 1) {
        url = decodeURIComponent(url[1]);
      } else {
        url = "http://petstore.swagger.io/v2/swagger.json";
      }

Change URL to url your own swagger.json URL, for example in my case it was http://localhost/laravel-swagger/swagger.json

5) Read the Docs. Now go to public docs folder. In my case URL was

http://localhost/laravel-swagger/public/docs/index.html

When you run, you will see following output page.


That's it and you have your API docs minimum efforts. Hope this helps you.

Tuesday, February 24, 2015

Android Obtain Crash Data of Application Remotely

Recently in one my project we distributed APKs to some people to test applications and we got a complain that APP is crashing. While on our side app was not crashing and it was working fine. We were not sure what could be possible reason of crash.  As they were running app from APK we sent in mail so there is no way they can report to Play Store. So we added our own solution to get stack trace on server from the app. In this blog I am going o explain how to do this.

First we added custom exception handler class. Here is that class. 

package com.myapp.app;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import android.util.Log;

public class CustomExceptionHandler implements UncaughtExceptionHandler {

    private UncaughtExceptionHandler defaultUEH;
    private String url;
    public CustomExceptionHandler(String url) {
        this.url = url;
        this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
    }

    public void uncaughtException(Thread t, Throwable e) {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timestamp = df.format(c.getTime());
        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        String stacktrace = result.toString();
        printWriter.close();
        String time = timestamp + ".stacktrace";
        if (url != null) {
            sendToServer(stacktrace, time);
        }

        defaultUEH.uncaughtException(t, e);
    }

    private void sendToServer(String stacktrace, String time) {
        Log.v("stacktrace","stacktrace");
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(url);
        List nvps = new ArrayList();
        nvps.add(new BasicNameValuePair("time", time));
        nvps.add(new BasicNameValuePair("stacktrace", stacktrace));
        try {
            httpPost.setEntity(
                    new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
            httpClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

As you can see in above class we have added Class with name CustomExceptionHandler which implements UncaughtExceptionHandler. The constructor is expecting server URL. This is your custom server URL where you want to log stack trace. As you can see in above code we have uncaughtException function which is logging stack trace to server by calling function sendToServer with HTTP POST request. Now We have to add this class as exception handler. This you can do in onCreate of your activity.

Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(
     null, "http://yourloggingurl));

As you can see in above code we are setting default exception handler for thread and passing the URL, you have to replace your own URL instead of yourloggingurl. That's it and your app will send complete stack trace on crash to server. Here is the PHP code on server to get data and write a file if you need.

<?php
    $time = $_POST['time'];
    $stacktrace = $_POST['stacktrace'] ;
    file_put_contents($filename, $stacktrace . "\n", FILE_APPEND);
?>

As yo can see in above codes you are getting stack trace and writing to a file. Make sure you have write permission on directory.


Wednesday, February 4, 2015

Solution for - Magento cart.totals API Returns Zero After Adding Product

Hello,

Recently in one of my project I was working with Magento SOAP APIs. Where we have used Magento cart.create API to create Magento cart and adding items to it. Now every time I add items I want to refresh totals. So I used cart_product.add API and after that used cart.totals API to get totals. But strangely it was always returning zero, no matter how many products are there. I tried to find out a solutions for it and did some tests and in one of the test I found out that if after creating a cart if I do operations like setting shipping address or add coupons, it was giving accurate totals.  That's really strange. I tried to find out issue for it but I am not magento expert to finally I came up with following solution. After creating  cart, I set dummy billing and shipping address immediately. And then when I add a product and get totals, it was giving me accurate result.

Please note this is not the best solution, but if you want to use you can use this. This will solve your problem. If any magento expert can debug API code and resolve this issue please post solution.

So here is what you have to do.

//Create a Quote

$client = new SoapClient($baseurl.'/api/soap/?wsdl');
$session = $client->login(USER,PASS);
$quote_id = $client->call( $session, 'cart.create', array($_storeId) );

//Add dummy shipping and billing address

$arrAddresses = array(
array(
"mode" => "shipping",
"firstname" => "testFirstname",
"lastname" => "testLastname",
"company" => "testCompany",
"street" => "testStreet",
"city" => "testCity",
"region" => "testRegion",
"postcode" => "testPostcode",
"country_id" => "id",
"telephone" => "0123456789",
"fax" => "0123456789",
"is_default_shipping" => 0,
"is_default_billing" => 0
),
array(
"mode" => "billing",
"firstname" => "testFirstname",
"lastname" => "testLastname",
"company" => "testCompany",
"street" => "testStreet",
"city" => "testCity",
"region" => "testRegion",
"postcode" => "testPostcode",
"country_id" => "id",
"telephone" => "0123456789",
"fax" => "0123456789",
"is_default_shipping" => 0,
"is_default_billing" => 0
)
);
$resultCustomerAddresses = $client->call($session, "cart_customer.addresses", array($quote_id, $arrAddresses));


//Now add a product to cart

$resultCartProductAdd = $client->call(
$session,
"cart_product.add",
array(
$quote_id,
$arrProducts
)
);

//Now get the cart info

$result = $client->call($session, 'cart.totals', $quote_id);
echo json_encode($result);

Hope this helps you and solve your problem.

Saturday, March 5, 2011

Working with Channel Advisor API with PHP

Hello Guys,

Recently I was working with Channel Advisor(CA) API in PHP script. Script was built to get product information from CA and  update product information in CA. First of all lets see what is Channel Advisor platform?

It's a SaaS(Software As A Service) provider which enables retailers to sell products online through various channels like eBay, Amazon etc. It has certain features like product inventory using which retailer can store product information. Those products can be synchronized in various e commerce channels. User can track sales through each channel. Detailed reports are also available.


This platform is built upon .Net Framework and it offers Asp.Net web services to use its API. Using those web service we can use that platform from the third party application. To use web services you have to register as a developer and get developer key and password. After this channel advisor admin will approve your developer account and provides a account ID and local id that developer can use in the scripts. 


Now as we all know that Asp.Net web services use SOAP protocol so we need some soap client in PHP. PHP 5.1 and above has soap pears available while for older version one need to install it separately.


Following is the code to use CA web service.

$developerKey = 'your developer key';
$password = 'password';
$localID = '1111111050';
$accountID = 'account id';


$client = new SoapClient("https://api.channeladvisor.com/ChannelAdvisorAPI/v3/InventoryService.asmx?WSDL");


$headersData = array('DeveloperKey' => $developerKey, 'Password' => $password);


$head = new SoapHeader("http://api.channeladvisor.com/webservices/","APICredentials",$headersData);
$client->__setSoapHeaders($head);


That's it and you have Soap client available now you can call any method which is provided by web service. See the example below.
$result = $client->GetInventoryItemQuantityInfo(array('accountID'=>$accountID,'sku' => $sku));
Above code gives quantity of product specified by sku. 


So this is how you can integrate channel advisor API in PHP. Above example is of inventory service. There are other services also available. Each method in services needs some input. Some are the required fields like product SKU etc. for example cosider following example of SynchInventoryItem method. This is the method used to add or update product in inventory. While working in PHP all the parameters need to be sent in format of an array.



$iteminfo = array('Sku'=>$sku,
                      'Title'=>$title,
                      'Subtitle'=>$title, 
                      'ShortDescription'=>$shortdescription,
                      'Description'=>$description,
                      'Weight'=>$weight,
                      'UPC'=>$upc,
                      'Manufacturer'=>$manufacturer,
                      'Brand'=>$brand,                      
                      
                      'QuantityInfo'=>array('UpdateType'=>'Absolute',
                                            'Total'=>$total
                                            ),
                      'PriceInfo'=>array('Cost'=>$cost,
                                         'RetailPrice'=>$price,
                                         'TakeItPrice'=>$special_price
                                         ),
                      'ImageList'=>array(
                                'ImageInfoSubmit'=>array(
                                'PlacementName'=>'ITEMIMAGEURL1',
                                'FilenameOrUrl'=>$imageurl)),                      
                      'Classification'=>$classification,
                      'AttributeList'=>array(
                                            'AttributeInfo'=>array(
                                                         array(
                                                              'Name'=>'Size',
                                                              'Value'=>$size
                                                           ),
                                                         array(
                                                              'Name'=>'Brand',
                                                              'Value'=>$brand)
                                                           )
                      ),
                      'VariationInfo'=>array('IsInRelationship'=>true,
                                             'RelationshipName'=>'Color',
                                             'IsParent'=>false,
                                             'ParentSku'=>$parent_sku
                                      ),   
                                      
                       );


Then we need to send above array as a parameter in the method.

$arrData = array(
'accountID'=>$accountID,
'item'=>$iteminfo,
);
                
$result=$client->SynchInventoryItem($arrData);
So this is how you can use CA API in PHP script.