Friday, June 29, 2018

Bulk delete users via admin services in WSO2 IS

There are times you create bulk users in WSO2 IS product for testing purposes and once you are done with the test you need to delete them. The following script can be used to bulk delete users by iteratively calling the deleteUser operation of the UserAdmin admin service.

This script is written to delete users created according to the following pattern.

If the username is Test,
Test001
Test002
...
Test010
Test011
...
Test099
Test100

You can modify the script according to your requirement.

#!/bin/bash
username=<username>

echo "Started deleting users"

for i in `seq -w 1 100`
do
   
 
curl -v -k --user <username>:<password> 
https://<hostname>:<port>/services/UserAdmin.UserAdminHttpsSoap11Endpoint/
 --data '<soapenv:Envelope 
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://org.apache.axis2/xsd">
   <soapenv:Header/>
   <soapenv:Body>
      <xsd:deleteUser>
         <!--Optional:-->
         <xsd:userName>'"${username}${i}"'</xsd:userName>
      </xsd:deleteUser>   
   </soapenv:Body>
</soapenv:Envelope>' --header "Content-Type:text/xml" --header "SOAPAction:urn:deleteApplication"


  echo "Deleted user : ${username}${i}"
done

echo "Completed deleting users"
Hope it is useful :)

Friday, March 2, 2018

How to correlate the incoming messages to outgoing messages in WSO2 ESB/EI



When there are many requests coming to ESB and there are failures in the responses, you will need to correlate the corresponding incoming request of the failed outgoing response in order to find the root cause of the issue.

Even though there is a message ID generated for each request and response, there will be two message ID's generated for both leaving it difficult for a user to correlate the request and response.

One way to correlate the two messages is by getting the MessageID of the incoming request in the in-sequence, save it in a property and log the same property in the out-sequence.

You can refer the following sample to achieve this. Copy the following synapse proxy configuration in WSO2 ESB/EI. Follow this document for more information on creating a proxy service.

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="TestProxy"
       startOnLoad="true"
       statistics="disable"
       trace="disable"
       transports="https,http">
   <target>
      <inSequence>
         <property expression="get-property('MessageID')" name="msgID"/>
         <log level="full">
            <property expression="get-property('msgID')" name="MessageID IN===:"/>
         </log>
         <send>
            <endpoint>
               <address uri="http://www.mocky.io/v2/5a696f212e000056097a7459"/>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <log level="full">
            <property expression="get-property('msgID')" name="MessageID OUT===:"/>
         </log>
         <send/>
      </outSequence>
   </target>
   <description/>
</proxy>

After invoking the service, you will see the logs as below. Highlighted in yellow is the message ID's to correlate the messages.

[2018-01-25 16:19:32,074]  INFO - LogMediator To: /services/TestProxy.TestProxyHttpSoap12Endpoint, WSAction: urn:mediate, SOAPAction: urn:mediate, MessageID: urn:uuid:dfc7d874-0b6c-4348-beb4-de6bc34c0106, Direction: request, MessageID IN===: = urn:uuid:dfc7d874-0b6c-4348-beb4-de6bc34c0106, Envelope: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body/></soapenv:Envelope>
[2018-01-25 16:19:32,228]  INFO - LogMediator To: http://www.w3.org/2005/08/addressing/anonymous, WSAction: , SOAPAction: , MessageID: urn:uuid:3cf4faa1-2298-4373-8cf2-3eddc2dd9e2c, Direction: response, MessageID OUT===: = urn:uuid:dfc7d874-0b6c-4348-beb4-de6bc34c0106, Payload: {"Test":"response"}
Hope you find it useful :)




Monday, December 4, 2017

Writing a Gmail Connector with Ballerina


In a previous post, I explained how to configure the Gmail API console to send an email. Now, I will write a simple program using Ballerina language to send an email using the Gmail API. Here, the use case is to check if the access token is valid and if not retrieve a new access token and call the API. Lets see how we can achieve this in Ballerina. I have used Ballerina 0.95.2 version.

Creating a Gmail Connector

First of all, I have created a config file named ballerina.conf which is residing in the root location of the project and added all the confidential information we require to call the Gmail API. These values are accessed when required with the capability of the Config API of Ballerina.

gmail_refresh_token=xxxxxxxxxxxxxxxxxxxxx
gmail_client_id=xxxxxxxxxxxxxxxxxxx
gmail_client_secret=xxxxxxxxxxxxxxxxxxxx
gmail_access_token=xxxxxxxxxxxxxxxxxxx

Following is the code for the Gmail connector in Ballerina. First, I check whether the access token has expired and get a new access token if it has expired. Then, I construct the message body of the request and encode it with base64 as it is expected by the API. I have already explained the details regarding this in the previous blog. Then, the connector will call the Gmail API and send the email or return an error if Ballerina couldn’t connect to the endpoint. The inline comments will guide you to understand the solution more.

import ballerina.net.http;
import ballerina.util;
import ballerina.config;
import ballerina.log;


string access_token = config:getGlobalValue("gmail_access_token");


public connector GmailConnector () {


  action sendMail (string to, string subject, string from, string messageBody, string cc, string bcc, string id,
                   string threadId) (json responseToSend, http:HttpConnectorError err) {


      //creating an endpoint where we indicate the endpoint we are interacting with
      endpoint<http:HttpClient> gmailEP {
          string baseURL = "https://www.googleapis.com/gmail";
          gmailEndpoint = create http:HttpClient(baseURL, {});
      }


      boolean isExpired;
      error e;
      error errAccess;


      //check if the access token has expired and get a new access token if expired
      isExpired, e = isAccessTokenExpired();


      if (isExpired) {
          access_token, errAccess = getNewAccessToken();
      } else if (!isExpired) {
          log:printInfo("Access token has not expired");
      } else {
          log:printError("Error: " + e.msg);
      }


      //create the message body of the request
      http:Request request = {};
      http:Response response = {};
      string concatRequest = "";


      if (to != "null") {
          concatRequest = concatRequest + "to:" + to + "\n";
      }


      if (subject != "null") {
          concatRequest = concatRequest + "subject:" + subject + "\n";
      }


      if (from != "null") {
          concatRequest = concatRequest + "from:" + from + "\n";
      }


      if (cc != "null") {
          concatRequest = concatRequest + "cc:" + cc + "\n";
      }


      if (bcc != "null") {
          concatRequest = concatRequest + "bcc:" + bcc + "\n";
      }


      if (id != "null") {
          concatRequest = concatRequest + "id:" + id + "\n";
      }


      if (threadId != "null") {
          concatRequest = concatRequest + "threadId:" + threadId + "\n";
      }


      if (messageBody != "null") {
          concatRequest = concatRequest + "\n" + messageBody + "\n";
      }


      //encoding the message and create a json message expected by the API
      string encodedRequest = util:base64Encode(concatRequest);
      json sendMailRequest = {"raw":encodedRequest};
      
      string sendMailPath = "/v1/users/me/messages/send";
      request.setHeader("Authorization", "Bearer " + access_token);
      request.setHeader("Content-Type", "application/json");
      request.setJsonPayload(sendMailRequest);


      if (errAccess == null) {
          log:printInfo("Access token is " + access_token);
          response, err = gmailEP.post(sendMailPath, request);
          responseToSend = response.getJsonPayload();
      }


      return;
  }


}


This is the function to check if the access token has expired. This function will return true if the access token is valid.

function isAccessTokenExpired () (boolean, error) {
  endpoint<http:HttpClient> gmailTokenEP {
      create http:HttpClient("https://www.googleapis.com/oauth2/v1", {});
  }


  http:Request request = {};
  http:Response response = {};
  http:HttpConnectorError err;
  error msg;
  string res;
  boolean isExpired = false;


  string body = "access_token=" + access_token;
  request.setStringPayload(body);
  request.setHeader("content-type", "application/x-www-form-urlencoded");


  response, err = gmailTokenEP.post("/tokeninfo", request);
  json jResponse = response.getJsonPayload();


  try {
      if ((error)err == null) {
          //check if an error returns as the response, meaning the token has expired
          res, _ = (string)jResponse.error;
          isExpired = true;
          log:printInfo("Access Token has been expired");
      } else {
          msg = {msg:"Error occurred when sending a request to retrieve the access token"};
      }
  } catch (error e) {
      msg = {msg:"Error getting the access token"};
  }


  return isExpired, msg;
}



This function is to generate a new access token if it has expired.

function getNewAccessToken () (string, error) {
  endpoint<http:HttpClient> gmailTokenEP {
      create http:HttpClient("https://www.googleapis.com/oauth2/v4", {});
  }


  string refresh_token = config:getGlobalValue("gmail_refresh_token");
  string client_id = config:getGlobalValue("gmail_client_id");
  string client_secret = config:getGlobalValue("gmail_client_secret");


  http:Request request = {};
  http:Response response = {};
  http:HttpConnectorError err;
  error msg;
  string body = "grant_type=refresh_token&client_id=" + client_id + "&client_secret=" + client_secret + "&refresh_token=" + refresh_token;
  request.setStringPayload(body);


  request.setHeader("content-type", "application/x-www-form-urlencoded");


  response, err = gmailTokenEP.post("/token", request);
  json jResponse = response.getJsonPayload();


  try {
      if ((error)err == null) {
          access_token, _ = (string)jResponse.access_token;
          log:printInfo("New access token is generated");
      } else {


          msg = {msg:"Error occurred when sending a request to retrieve the access token"};
      }
  } catch (error e) {
      msg = {msg:"Error getting the access token"};
  }


  return access_token, msg;


}


Invoking the connector


Now, we will invoke the connector. The following is a main method where the connector is being invoked.
function main (string[] args) {


  endpoint<conn:GmailConnector> ep {
      create conn:GmailConnector();
  }


  var mailSent, ex = ep.sendMail("dilinig@wso2.com", "This is the subject", "dilinisg@gmail.com", "This is the message", "", "", "", "");


  if (ex == null) {
      log:printInfo("Mail successfully sent");
  }
  else {
      error err = (error)ex;
      log:printError("Error occured when sending the mail: " + err.msg);
  }
}


This simple connector can be improved a lot and errors can be handled better. This is just a guideline for you to start implementing your own connector. Happy coding with Ballerina :)