Extension Incoming Webhook Handler

Integration with external applications can be done with the help of incoming webhook APIs. An extension token or ZAPI key generated by each user interacting with the extension, should be used for handling incoming webhooks and messaging apis. You will get the below given list of attributes when the extension's incoming webhook handler is executed:

AttributeDescription
headersHeader details from the request made to the server.
parametersParameter details from the request made to the server.
bodyRequest body as the type string.
http_methodGET | PUT | POST | DELETE
userDetails of the user triggering the handler.
ZAPI key (zapi key)The authtoken generated by each user interacting with the extension.
App key (appkey)This market place app key provided once the extension is created.

User Scenario

Let us consider an extension integrating GitLab and Cliq.  The installation validator authorizes the user to install the extension upon checking if they have an account in GitLab. The handler is triggered to activate the installed extension.  Following this installation and activation, the extension's webhook endpoints are configured by through a slash command. 

Note:

Configuring an extension's incoming webhook endpoint can be done through the following internal components - Slash Command, Bot, Message Action. 

Setting up webhooks in the GitLab for Cliq Extension

Create a slash command to configure webhooks for all your GitLab projects. Executing the command will return the token generation button as response. On clicking this button, each user will generate an extension token (ZAPI Key). The function associated with this button will register the webhooks for all GitLab projects present in your account. 

Slash command execution code


response = Map();
response = {"text":"Click on the token generation button below!","buttons":[
  {
    "label": "Create Webhook",
    "type": "+",
    "action": {
      "type": "invoke.function",
      "data": {
        "name": "authenticate"
      },
     "confirm":{
     "title":"Generate Webhooks for a GitLab Project",
      "description":"Connect to GitLab Projects from within Cliq",
       "input" : 
         {"type":"user_webhook_token"
		 }
	 }
    }
  }
  ]};
return response;

Button Function Code


response = Map();
info arguments;
token = arguments.get("input").get("token");
info token;
git_projects = invokeurl
[
	url :"https://gitlab.com/api/v4/projects?visibility=private&simple=true"
	type :GET
	connection:"ENTER YOUR CONNECTION NAME"
];
info git_projects;
for each  project in git_projects
{
info "here";
	projectID = project.get("id");
	info projectID;
	paramsMap = Map();
	paramsMap = {"url":"https://cliq.zoho.com/api/v2/applications/"+APPID+"/incoming?appkey="+APPKEY+"&zapikey=" +token,"id":projectID,"push_events":true,"issues_events":true,"note_events":true,"merge_requests_events":true};
	createWebhook = invokeurl
	[
		url :"https://gitlab.com/api/v4/projects/" + projectID + "/hooks"
		type :POST
		parameters:paramsMap
		connection:"ENTER YOUR CONNECTION NAME"
	];
	}
info createWebhook;
return response;

Extension Incoming Webhook Endpoint Workflow

The custom callbacks directed to the extension's incoming webhook endpoint should be handled in the extension's incoming webhook handler. The JSON response from the third party application will be passed as headers, parameters or in the body attribute. In this example, the response is available in the body parameter. The extension's webhook endpoint is called for any event update or change made in a GitLab project. According to the response received, a message is configured and posted to a channel of your choice. 


info body;
result = body.toMap();
if(result.get("object_kind") == "issue" && result.get("object_attributes").get("action") == "open")
{
	name = body.get("user").get("name");
	title = result.get("object_attributes").get("title");
	project = result.get("project").get("name");
	state = result.get("object_attributes").get("state");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.get("object_attributes").get("due_date") == "null")
	{
		duedate = "Null";
	}
	else
	{
		duedate = result.get("object_attributes").get("due_date");
		dateandtime = duedate.toTime("yyyy-MM-dd HH:mm:ss","UTC");
		duedate = dateandtime.toString("dd-MMM-yyyy hh:mm a",user.get("timezone"));
	}
	if(result.containKey("assignees"))
	{
		assignee = result.get("assignees").getJSON("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	response = {"text":"### :bug: Issue has been created by '" + name + "' in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nState: " + state + "\nProject: " + project + "\nAssignee: " + assignee + "\nDue date: " + duedate,"card":{"theme":"1"}};
}
else if(result.get("object_kind") == "issue" && result.get("object_attributes").get("action") == "reopen")
{
	name = body.get("user").get("name");
	title = result.get("object_attributes").get("title");
	project = result.get("project").get("name");
	state = result.get("object_attributes").get("state");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.get("object_attributes").get("due_date") == "null")
	{
		duedate = "Null";
	}
	else
	{
		duedate = result.get("object_attributes").get("due_date");
		dateandtime = duedate.toTime("yyyy-MM-dd HH:mm:ss","UTC");
		duedate = dateandtime.toString("dd-MMM-yyyy hh:mm a",user.get("timezone"));
	}
	if(result.containKey("assignees"))
	{
		assignee = result.get("assignees").getJSON("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	response = {"text":"### :bug: Issue has been reopened by '" + name + "' in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nState: " + state + "\nProject: " + project + "\nAssignee: " + assignee + "\nDue date: " + duedate,"card":{"theme":"1"}};
}
else if(result.get("object_kind") == "issue" && result.get("object_attributes").get("action") == "update")
{
	name = body.get("user").get("name");
	title = result.get("object_attributes").get("title");
	project = result.get("project").get("name");
	state = result.get("object_attributes").get("state");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.get("object_attributes").get("due_date") == "null")
	{
		duedate = "Null";
	}
	else
	{
		duedate = result.get("object_attributes").get("due_date");
		dateandtime = duedate.toTime("yyyy-MM-dd HH:mm:ss","UTC");
		duedate = dateandtime.toString("dd-MMM-yyyy hh:mm a",user.get("timezone"));
	}
	if(result.containKey("assignees"))
	{
		assignee = result.get("assignees").getJSON("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	response = {"text":"### :bug: Issue has been updated by '" + name + "' in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nState: " + state + "\nProject: " + project + "\nAssignee: " + assignee + "\nDue date: " + duedate,"card":{"theme":"1"}};
}
else if(result.get("object_kind") == "issue" && result.get("object_attributes").get("action") == "close")
{
	name = body.get("user").get("name");
	title = result.get("object_attributes").get("title");
	project = result.get("project").get("name");
	state = result.get("object_attributes").get("state");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.get("object_attributes").get("due_date") == "null")
	{
		duedate = "Null";
	}
	else
	{
		duedate = result.get("object_attributes").get("due_date");
		dateandtime = duedate.toTime("yyyy-MM-dd HH:mm:ss","UTC");
		duedate = dateandtime.toString("dd-MMM-yyyy hh:mm a",user.get("timezone"));
	}
	if(result.containKey("assignees"))
	{
		assignee = result.get("assignees").getJSON("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	response = {"text":"### :bug: Issue has been closed by '" + name + "' in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nState: " + state + "\nProject: " + project + "\nAssignee: " + assignee + "\nDue date: " + duedate,"card":{"theme":"1"}};
}
else if(result.get("object_kind") == "push")
{
	username = result.get("user_name");
	//name = result.get("commits").getJSON("author").get("name");
	msg = result.get("commits").getJSON("message");
	url = result.get("commits").getJSON("url");
	repository = result.get("repository").get("name");
	response = {"text":"*'" + username + "'* has made a commit  [" + msg + "](" + url + ")  in the repository *'" + repository + "'*","card":{"theme":"3","title":" Commit changes"}};
}
else if(result.get("object_kind") == "note")
{
	username = result.get("user").get("name");
	//name = result.get("commits").getJSON("author").get("name");
	msg = result.get("object_attributes").get("note");
	url = result.get("object_attributes").get("url");
	repository = result.get("repository").get("name");
	commitname = result.get("commit").get("message");
	response = {"text":"*'" + username + "'* has added a comment  [" + msg + "](" + url + ") for the commit *'" + commitname + "'* in the repository *'" + repository + "'*","card":{"theme":"3","title":":task: Comment Added for a Commit"}};
}
else if(result.get("object_kind") == "merge_request" && result.get("object_attributes").get("action") == "open")
{
	title = result.get("object_attributes").get("title");
	project = result.get("project").get("name");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.containKey("assignee"))
	{
		assignee = result.get("assignee").get("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	sourcebranch = result.get("changes").get("source_branch").get("current");
	targetbranch = result.get("changes").get("target_branch").get("current");
	response = {"text":"### New merge request in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nSource branch: " + sourcebranch + "\nTarget branch: " + targetbranch + "\nAssignee: " + assignee,"card":{"theme":"1"}};
}
else if(result.get("object_kind") == "merge_request" && result.get("object_attributes").get("action") == "close")
{
	title = result.get("object_attributes").get("title");
	//project = result.get("project").get("name");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.containKey("assignee"))
	{
		assignee = result.get("assignee").get("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	sourcebranch = result.get("object_attributes").get("source_branch");
	targetbranch = result.get("object_attributes").get("target_branch");
	response = {"text":"### Merge request has been closed in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nSource branch: " + sourcebranch + "\nTarget branch: " + targetbranch + "\nAssignee: " + assignee,"card":{"theme":"1"}};
}
else if(result.get("object_kind") == "merge_request" && result.get("object_attributes").get("action") == "reopen")
{
	title = result.get("object_attributes").get("title");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.containKey("assignee"))
	{
		assignee = result.get("assignee").get("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	sourcebranch = result.get("object_attributes").get("source_branch");
	targetbranch = result.get("object_attributes").get("target_branch");
	response = {"text":"### Merge request has been reopened in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nSource branch: " + sourcebranch + "\nTarget branch: " + targetbranch + "\nAssignee: " + assignee,"card":{"theme":"1"}};
}
else if(result.get("object_kind") == "merge_request" && result.get("object_attributes").get("action") == "update")
{
	title = result.get("object_attributes").get("title");
	repository = result.get("repository").get("name");
	url = result.get("object_attributes").get("url");
	if(result.containKey("assignee"))
	{
		assignee = result.get("assignee").get("name");
		info assignee;
	}
	else
	{
		assignee = "No assignee";
	}
	sourcebranch = result.get("object_attributes").get("source_branch");
	targetbranch = result.get("object_attributes").get("target_branch");
	response = {"text":"### Merge request has been updated in the repository '" + repository + "' \n\n*Details are as follows:*\nTitle: [" + title + "](" + url + ") \nSource branch: " + sourcebranch + "\nTarget branch: " + targetbranch + "\nAssignee: " + assignee,"card":{"theme":"1"}};
}
zoho.cliq.postToChannel("ENTER CHANNEL UNIQUE NAME",response);
return Map();