Jira Webhook配置与使用

阿特蓝胖 2024-09-15 16:03:06 阅读 71

Jira Webhook配置与使用

什么是Jira Webhook(网络钩子)创建Webhook使用Webhook通过Webhook创建时设置的事件来触发通过工作流来触发Webhook

总结一下

什么是Jira Webhook(网络钩子)

Webhook是一种回调接口,一般用在由事件驱动的系统中。那么Jira内置的功能是支持自定义Webhook的,然后在特定的事件或者工作流执行中调用自己定义的Webhook,来实现与第三方系统的通信,并且Webhook中提供了几乎与Issue相关的所有字段信息。

创建Webhook

使用系统管员登录,进入系统设置页面,点击左侧“网络钩子菜单”,进入网络钩子管理页面,点击右上角“创建网络钩子”按钮,添加一个新的Webhook。

在这里插入图片描述

在这里,我们使用一个在线工具来测试打印出webhook推送给第三方系统的数据,UU在线工具https://uutool.cn/mock/。

名称:自己取个名字就好了

状态:可以启用或禁用掉Webhook,因为在实际工作中Webhook可以在很多流程里面去触发,所以这里可以禁用掉就不用去修改每个流程了

URL:这里填写的就是第三方系统的接口地址,我这里填写的是UU在线工具的临时接口地址,这个接口是一个挡板能把所有的请求信息打印出来。

事件:这里我们定义在创建和修改Issue时去触发这个Webhook的执行,注意这里也可以不选任何的事件,而是由工作流中我们去触发这个Webhook,事件的主体有Issue、项目、用户、备注还有敏捷相关的看板等等。注意这里选的事件类型与数据主体信息是相对应的,比如选Issue的事件,Raw里面的json数据就是issue相关的;如果选择的是用户事件,那么Raw里面在json主体就是用户信息

排除主体:有些事件发生时我们只需要少量的字段我们可以通过在URL的变量中携带而不必传输整个Raw数据,这时我们勾选排除主体就可以了。

在这里插入图片描述

在这里插入图片描述

使用Webhook

通过Webhook创建时设置的事件来触发

刚才我们定义的Webhook是在Issue创建和修改的时候触发的,那么我们现在创建一个Issue观察一下Webhook推送的数据。

创建Issue:

在这里插入图片描述

查看推送数据:

请求头

<code>请求头,可以看到是通用HttpClient的库发送的请求

{

"Content-Type": "application/json; charset=UTF-8",

"User-Agent": "Atlassian HttpClient 3.0.4 / JIRA-9.13.1 (9130002) / Default",

"Connection": "Keep-Alive",

"Host": "mock.uutool.cn",

"Content-Length": "7137",

"Via": "1.1 localhost (Apache-HttpClient/4.5.14 (cache))",

"Accept": "*/*"

}

数据结构

Issue的Webhook数据结构中包括了用户、项目、报告人、Issue字段等的信息,如果在第三方系统需要其它信息都可以通过自定义字段去放置。

数据位置和结构,可以看到Webhook在GET参数中带了用户名,具体信息在请求体中通过JSON结构传输的。

GET参数:

{

"user_id": "admin",

"user_key": "JIRAUSER10000"

}

POST参数:

[]

Raw:

{

"timestamp": 1725868650247,

"webhookEvent": "jira:issue_created",

"issue_event_type_name": "issue_created",

"user": {

"self": "http://192.168.1.100:8080/rest/api/2/user?username=admin",

"name": "admin",

"key": "JIRAUSER10000",

"emailAddress": "admin@abdxww.co",

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/useravatar?avatarId=10351",

"24x24": "http://192.168.1.100:8080/secure/useravatar?size=small&avatarId=10351",

"16x16": "http://192.168.1.100:8080/secure/useravatar?size=xsmall&avatarId=10351",

"32x32": "http://192.168.1.100:8080/secure/useravatar?size=medium&avatarId=10351"

},

"displayName": "admin",

"active": true,

"timeZone": "Asia/Shanghai"

},

"issue": {

"id": "11104",

"self": "http://192.168.1.100:8080/rest/api/2/issue/11104",

"key": "TEST-17",

"fields": {

"issuetype": {

"self": "http://192.168.1.100:8080/rest/api/2/issuetype/10006",

"id": "10006",

"description": "",

"iconUrl": "http://192.168.1.100:8080/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",

"name": "故障",

"subtask": false,

"avatarId": 10303

},

"timespent": null,

"project": {

"self": "http://192.168.1.100:8080/rest/api/2/project/10200",

"id": "10200",

"key": "TEST",

"name": "TEST1",

"projectTypeKey": "software",

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/projectavatar?avatarId=10324",

"24x24": "http://192.168.1.100:8080/secure/projectavatar?size=small&avatarId=10324",

"16x16": "http://192.168.1.100:8080/secure/projectavatar?size=xsmall&avatarId=10324",

"32x32": "http://192.168.1.100:8080/secure/projectavatar?size=medium&avatarId=10324"

}

},

"fixVersions": [],

"customfield_10110": null,

"aggregatetimespent": null,

"resolution": null,

"customfield_10104": null,

"customfield_10108": "0|i005h3:",

"customfield_10109": null,

"resolutiondate": null,

"workratio": -1,

"lastViewed": null,

"watches": {

"self": "http://192.168.1.100:8080/rest/api/2/issue/TEST-17/watchers",

"watchCount": 0,

"isWatching": false

},

"created": "2024-09-09T15:57:30.142+0800",

"priority": {

"self": "http://192.168.1.100:8080/rest/api/2/priority/3",

"iconUrl": "http://192.168.1.100:8080/images/icons/priorities/medium.svg",

"name": "Medium",

"id": "3"

},

"customfield_10100": null,

"customfield_10101": null,

"customfield_10102": null,

"customfield_10300": null,

"labels": [],

"customfield_10103": null,

"timeestimate": null,

"aggregatetimeoriginalestimate": null,

"versions": [],

"issuelinks": [],

"assignee": null,

"updated": "2024-09-09T15:57:30.142+0800",

"status": {

"self": "http://192.168.1.100:8080/rest/api/2/status/10000",

"description": "",

"iconUrl": "http://192.168.1.100:8080/",

"name": "待办",

"id": "10000",

"statusCategory": {

"self": "http://192.168.1.100:8080/rest/api/2/statuscategory/2",

"id": 2,

"key": "new",

"colorName": "default",

"name": "待办"

}

},

"components": [],

"timeoriginalestimate": null,

"description": null,

"timetracking": { },

"archiveddate": null,

"customfield_10401": null,

"customfield_10203": null,

"attachment": [],

"aggregatetimeestimate": null,

"summary": "test webhook1",

"creator": {

"self": "http://192.168.1.100:8080/rest/api/2/user?username=admin",

"name": "admin",

"key": "JIRAUSER10000",

"emailAddress": "admin@abdxww.co",

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/useravatar?avatarId=10351",

"24x24": "http://192.168.1.100:8080/secure/useravatar?size=small&avatarId=10351",

"16x16": "http://192.168.1.100:8080/secure/useravatar?size=xsmall&avatarId=10351",

"32x32": "http://192.168.1.100:8080/secure/useravatar?size=medium&avatarId=10351"

},

"displayName": "admin",

"active": true,

"timeZone": "Asia/Shanghai"

},

"subtasks": [],

"reporter": {

"self": "http://192.168.1.100:8080/rest/api/2/user?username=admin",

"name": "admin",

"key": "JIRAUSER10000",

"emailAddress": "admin@abdxww.co",

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/useravatar?avatarId=10351",

"24x24": "http://192.168.1.100:8080/secure/useravatar?size=small&avatarId=10351",

"16x16": "http://192.168.1.100:8080/secure/useravatar?size=xsmall&avatarId=10351",

"32x32": "http://192.168.1.100:8080/secure/useravatar?size=medium&avatarId=10351"

},

"displayName": "admin",

"active": true,

"timeZone": "Asia/Shanghai"

},

"customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@7ae0bb11[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@f3a1dea[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@4a6368d[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@5b2caebe[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@4a735e4b[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@38f61afc[dueDate=<null>,overDue=false,state=<null>,stateCount=0,count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@dd14131[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@33b6576b[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@63e7c650[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@6bce112a[count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@6c110dab[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@6d1d024d[count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}",code>

"aggregateprogress": {

"progress": 0,

"total": 0

},

"customfield_10200": null,

"customfield_10201": null,

"customfield_10400": null,

"customfield_10202": null,

"environment": null,

"duedate": null,

"progress": {

"progress": 0,

"total": 0

},

"comment": {

"comments": [],

"maxResults": 0,

"total": 0,

"startAt": 0

},

"votes": {

"self": "http://192.168.1.100:8080/rest/api/2/issue/TEST-17/votes",

"votes": 0,

"hasVoted": false

},

"worklog": {

"startAt": 0,

"maxResults": 20,

"total": 0,

"worklogs": []

},

"archivedby": null

}

}

}

通过工作流来触发Webhook

我们创建的Webhook,不勾选任何事件,然后在流程中去触发。

在这里插入图片描述

修改项目D35问题类型是故障的工作流,在完成的转换中添加后处理功能,去触发刚才定义的Webhook。

点击后处理功能:

在这里插入图片描述

点击添加后处理功能:

在这里插入图片描述

点击引发一个webhook,选择刚才定义的webhook。

在这里插入图片描述

在D35-1这个Issue中点击完成。

在这里插入图片描述

我们可以看到通过流程触发的webhook的数据主体还是Issue,多了流程状态的数据。

<code>GET和POST外的其他方法见Raw数据:

GET参数:

{

"user_id": "admin",

"user_key": "JIRAUSER10000"

}

POST参数:

[]

Raw:

{

"transition": {

"workflowId": 11105,

"workflowName": "Software Simplified Workflow for Project D35",

"transitionId": 31,

"transitionName": "Done",

"from_status": "To Do",

"to_status": "Done"

},

"comment": "",

"user": {

"self": "http://192.168.1.100:8080/rest/api/2/user?username=admin",

"name": "admin",

"key": "JIRAUSER10000",

"emailAddress": "admin@abdxww.co"

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/useravatar?avatarId=10351",

"24x24": "http://192.168.1.100:8080/secure/useravatar?size=small&avatarId=10351",

"16x16": "http://192.168.1.100:8080/secure/useravatar?size=xsmall&avatarId=10351",

"32x32": "http://192.168.1.100:8080/secure/useravatar?size=medium&avatarId=10351"

},

"displayName": "admin",

"active": true,

"timeZone": "Asia/Shanghai"

},

"issue": {

"id": "11105",

"self": "http://192.168.1.100:8080/rest/api/2/issue/11105",

"key": "D35-1",

"fields": {

"issuetype": {

"self": "http://192.168.1.100:8080/rest/api/2/issuetype/10006",

"id": "10006",

"description": "",

"iconUrl": "http://192.168.1.100:8080/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",

"name": "故障",

"subtask": false,

"avatarId": 10303

},

"timespent": null,

"project": {

"self": "http://192.168.1.100:8080/rest/api/2/project/10302",

"id": "10302",

"key": "D35",

"name": "D35",

"projectTypeKey": "software",

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/projectavatar?avatarId=10324",

"24x24": "http://192.168.1.100:8080/secure/projectavatar?size=small&avatarId=10324",

"16x16": "http://192.168.1.100:8080/secure/projectavatar?size=xsmall&avatarId=10324",

"32x32": "http://192.168.1.100:8080/secure/projectavatar?size=medium&avatarId=10324"

}

},

"fixVersions": [],

"customfield_10110": null,

"aggregatetimespent": null,

"resolution": null,

"customfield_10104": null,

"customfield_10108": "0|i005hb:",

"customfield_10109": null,

"resolutiondate": null,

"workratio": -1,

"lastViewed": "2024-09-09T16:45:38.601+0800",

"watches": {

"self": "http://192.168.1.100:8080/rest/api/2/issue/D35-1/watchers",

"watchCount": 1,

"isWatching": true

},

"created": "2024-09-09T16:35:50.000+0800",

"priority": {

"self": "http://192.168.1.100:8080/rest/api/2/priority/3",

"iconUrl": "http://192.168.1.100:8080/images/icons/priorities/medium.svg",

"name": "Medium",

"id": "3"

},

"customfield_10100": null,

"customfield_10101": null,

"customfield_10102": null,

"customfield_10300": null,

"labels": [],

"customfield_10103": null,

"timeestimate": null,

"aggregatetimeoriginalestimate": null,

"versions": [],

"issuelinks": [],

"assignee": null,

"updated": "2024-09-09T16:44:11.000+0800",

"status": {

"self": "http://192.168.1.100:8080/rest/api/2/status/10000",

"description": "",

"iconUrl": "http://192.168.1.100:8080/",

"name": "待办",

"id": "10000",

"statusCategory": {

"self": "http://192.168.1.100:8080/rest/api/2/statuscategory/2",

"id": 2,

"key": "new",

"colorName": "default",

"name": "待办"

}

},

"components": [],

"timeoriginalestimate": null,

"description": null,

"timetracking": { },

"archiveddate": null,

"customfield_10401": null,

"customfield_10203": null,

"attachment": [],

"aggregatetimeestimate": null,

"summary": "bug1",

"creator": {

"self": "http://192.168.1.100:8080/rest/api/2/user?username=admin",

"name": "admin",

"key": "JIRAUSER10000",

"emailAddress": "admin@abdxww.co",

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/useravatar?avatarId=10351",

"24x24": "http://192.168.1.100:8080/secure/useravatar?size=small&avatarId=10351",

"16x16": "http://192.168.1.100:8080/secure/useravatar?size=xsmall&avatarId=10351",

"32x32": "http://192.168.1.100:8080/secure/useravatar?size=medium&avatarId=10351"

},

"displayName": "admin",

"active": true,

"timeZone": "Asia/Shanghai"

},

"subtasks": [],

"reporter": {

"self": "http://192.168.1.100:8080/rest/api/2/user?username=admin",

"name": "admin",

"key": "JIRAUSER10000",

"emailAddress": "admin@abdxww.co",

"avatarUrls": {

"48x48": "http://192.168.1.100:8080/secure/useravatar?avatarId=10351",

"24x24": "http://192.168.1.100:8080/secure/useravatar?size=small&avatarId=10351",

"16x16": "http://192.168.1.100:8080/secure/useravatar?size=xsmall&avatarId=10351",

"32x32": "http://192.168.1.100:8080/secure/useravatar?size=medium&avatarId=10351"

},

"displayName": "admin",

"active": true,

"timeZone": "Asia/Shanghai"

},

"customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@46fa0a08[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@4c98c539[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@734de769[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@79f64534[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@780f02[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@6d38812b[dueDate=<null>,overDue=false,state=<null>,stateCount=0,count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@71d2454a[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@3a4671a1[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@7a0669f4[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@531e8e46[count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@3005697a[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@39c7cda2[count=0,lastUpdated=<null>,lastUpdatedTimestamp=<null>]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}",code>

"aggregateprogress": {

"progress": 0,

"total": 0

},

"customfield_10200": null,

"customfield_10201": null,

"customfield_10400": null,

"customfield_10202": null,

"environment": null,

"duedate": null,

"progress": {

"progress": 0,

"total": 0

},

"comment": {

"comments": [],

"maxResults": 0,

"total": 0,

"startAt": 0

},

"votes": {

"self": "http://192.168.1.100:8080/rest/api/2/issue/D35-1/votes",

"votes": 0,

"hasVoted": false

},

"worklog": {

"startAt": 0,

"maxResults": 20,

"total": 0,

"worklogs": []

},

"archivedby": null

}

},

"timestamp": 1725871538608

}

总结一下

Jira的Webhook定义及使用都非常灵活,几乎可以Jira侧不进行二次开发来实现与第三方系统对接的所有功能。如果再结合Scriptrunner可以实现与第三方系统之间双向通信,来实现复杂的业务逻辑,这些都是不需要通Jira的二次开发来实现的功能。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。