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:
Attribute | Description |
headers | Header details from the request made to the server. |
parameters | Parameter details from the request made to the server. |
body | Request body as the type string. |
http_method | GET | PUT | POST | DELETE |
user | Details 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();