在 Python 中与 Jira API 集成_python集成开发环境有哪两个

liftword3个月前 (02-09)技术文章36

在 Python 中连接到 Jira。

概述

继续介绍有关Zato 3.2中最新云连接的系列文章,这一集从调用其 API 以构建 Jira 与其他系统之间的集成的角度介绍了 Atlassian Jira。

与 Jira 集成基本上有两种使用模式:

  1. Jira 对您项目中发生的事件做出反应,并通过 WebHooks 相应地调用您的端点。在这种情况下,Jira 明确地与您的 API 建立连接并向其发送请求。
  2. Jira 项目会定期或作为 Jira 使用 WebHook 以外的方式触发的事件的结果进行查询。

第一种情况通常更易于概念化 - 您在 Jira 中创建一个 WebHook,将其指向您的端点,当出现感兴趣的情况时,Jira 会调用它,例如打开或更新新票证。我将在以后的文章中讨论与 Jira 集成的这种变体,因为当前的版本是关于另一种情况,即您的系统与 Jira 建立连接。

首先讨论第二种形式更实际的原因是,即使 WebHooks 更容易推理,它们也有自己的分支。

首先,假设您使用基于云的 Jira 版本(例如https://example.atlassian.net),您需要有一个公开可用的端点供 Jira 通过 WebHooks 调用。很多时候,这是不可取的,因为您需要集成的系统可能是内部系统,绝不会暴露于公共网络。

其次,您的端点需要有一个由公共证书颁发机构签名的 TLS 证书,并且它们需要可以在端口 443 上访问。同样,这两者都是大多数企业系统根本不允许的,或者可能需要数月或数年才能完成在涉及的各个公司部门内部处理此类变更。

最后,即使可以使用 WebHook,您在来自 WebHook 的请求中收到的初始信息也不一定已经包含您在特定集成服务中需要的所有内容。因此,您仍然需要向 Jira 发出请求以查找特定对象的详细信息(例如票证),这样可以将 WebHook 减少为与 Jira 交互的初始触发器的角色,例如 WebHook 调用您的端点,您在输入时有一个工单 ID,然后无论如何调用 Jira 以获取您在业务集成中实际需要的所有详细信息。

最终情况是,虽然 WebHooks 是一个有用的概念,我将在以后的文章中写到,但对于许多集成用例来说,它们可能还不够。这就是为什么我从作为 WebHooks 替代方案的集成方法开始。

WebHooks 的替代品

如果在我们的例子中,我们不能使用 WebHooks,那么接下来怎么办?两个好的方法是:

  1. 预定作业
  2. 对电子邮件做出反应(通过 IMAP)

计划的作业将让您定期向 Jira 询问您尚未处理的更改。例如,作业定义如下:

现在,为此作业配置的服务将每分钟调用一次,以执行所需的任何集成工作。例如,它可以获取自上次运行以来的工单列表,根据业务上下文的需要处理每个工单,并使用刚刚完成的信息更新数据库——数据库可以基于 Redis、MongoDB , SQL 或其他任何东西。

当您需要定期扫描大量业务数据时,围绕计划作业构建的集成最有意义,当您不确切知道有多少数据时,这些是“给我上一期更改的所有内容”类型的交互你将收到。

但是,在 Jira 票证的特定情况下,一个有趣的替代方案可能是将计划作业与 IMAP 连接结合起来:

这里的想法是,当打开新票证或对现有票证进行更新时,Jira 将向特定电子邮件地址发送通知,我们可以利用它。

例如,您可以告诉 Jira 给 CC 或 BCC 一个地址。现在,Zato 仍将运行计划作业,但不是直接与 Jira 连接,而是该作业将为其收件箱查找未读电子邮件(根据相关RFC为“UNSEEN” )。

自上次迭代以来,任何未读的内容都必须是新的,这意味着我们可以处理收件箱中的每封此类电子邮件,这样可以保证我们只处理最新的更新,而无需我们自己的已处理票证数据库。我们可以从电子邮件中提取票证 ID 或其他详细信息,在 Jira 中查找其详细信息,然后根据需要继续。

文档中提供了有关如何使用 IMAP 电子邮件的所有详细信息,但归结为:

Python

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class MyService(Service):

    def handle(self):
        conn = self.email.imap.get('My Jira Inbox').conn

        for msg_id, msg in conn.get():

            # Process the message here ..
            process_message(msg.data)

            # .. and mark it as seen in IMAP.
? ? ? ? ? ? msg.mark_seen()

自然的问题是 - “process_message”函数如何从电子邮件中提取工单的详细信息?

有几种方法:

  1. 每封电子邮件都有一个固定形式的主题 - “[JIRA] (ABC-123) Here go description”。在本例中,ABC-123 是工单 ID。
  2. 每封电子邮件都会包含一个摘要,例如下面的,也可以解析:

纯文本

Summary: Here goes description
             Key: ABC-123
             URL: https://example.atlassian.net/browse/ABC-123
         Project: My Project
      Issue Type: Improvement
Affects Versions: 1.3.17
     Environment: Production
        Reporter: Reporter Name
? ? ? ? Assignee: Assignee Name


  1. 最后,每封电子邮件都会有一个“X-Atl-Mail-Meta”标头,其中包含有趣的元数据,这些元数据也可以被解析和提取:

纯文本

X-Atl-Mail-Meta: user_id="123456:12d80508-dcd0-42a2-a2cd-c07f230030e5",
                 event_type="Issue Created",
? ? ? ? ? ? ? ? ?tenant="https://example.atlassian.net"


第一个选项是最直接且可能最方便的选项 - 只需解析出票证 ID,并在输入时使用该 ID 调用 Jira 以获取有关票证的所有其他信息。如何准确地做到这一点将在下一章中介绍。

无论我们如何解析电子邮件,重要的是我们知道只有在有新的或更新的票证时才会调用 Jira——否则不会有任何新的电子邮件需要处理。此外,因为调用 Jira 的是我们这边,所以我们不会将我们的内部系统直接暴露给公共网络。

但是,从整体安全架构的角度来看,电子邮件仍然是攻击面的一部分,因此我们需要确保在阅读和解析电子邮件时考虑到这一点。换句话说,无论是 Jira 调用我们还是我们阅读来自 Jira 的电子邮件,所有关于 API 集成和接受外部资源输入的常见安全预防措施,所有这些仍然存在并且需要成为集成工作流设计的一部分.

创建 Jira 连接

上面介绍了我们可以到达何时调用 Jira 的步骤,现在我们已经准备好实际执行它了。

与其他类型的连接一样,Jira 连接是在 Zato Dashboard 中创建的,如下所示。请注意,您使用的是您代表其连接到 Jira 的用户的电子邮件地址,但唯一的其他凭据是该用户先前在 Jira 中生成的 API 令牌,而不是用户的密码。


调用 Jira

有了 Jira 连接,我们现在可以创建 Python API 服务。在这种情况下,我们在输入时接受票证 ID(在 Jira 中称为“密钥”),并将票证的一些详细信息返回给调用者。

这是一种可以从由计划作业触发的服务中调用的服务。也就是说,我们将任务分开,一项服务将负责打开 IMAP 收件箱和解析电子邮件,而下面的一项将负责与 Jira 通信。

多亏了这种松耦合,我们让一切变得更加可重用——服务可以独立更改只是其中的一部分,更重要的是,通过这种分离,它们都可以被未来的服务重用,而无需绑定他们死板地把这一单整合在一起。

Python

# -*- coding: utf-8 -*-

# stdlib
from dataclasses import dataclass

# Zato
from zato.common.typing_ import cast_, dictnone
from zato.server.service import Model, Service

# ###########################################################################

if 0:
    from zato.server.connection.jira_ import JiraClient

# ###########################################################################

@dataclass(init=False)
class GetTicketDetailsRequest(Model):
    key: str

@dataclass(init=False)
class GetTicketDetailsResponse(Model):
    assigned_to: str = ''
    progress_info: dictnone = None

# ###########################################################################

class GetTicketDetails(Service):

    class SimpleIO:
        input  = GetTicketDetailsRequest
        output = GetTicketDetailsResponse

    def handle(self):

        # This is our input data
        input = self.request.input # type: GetTicketDetailsRequest

        # .. create a reference to our connection definition ..
        jira = self.cloud.jira['My Jira Connection']

        # .. obtain a client to Jira ..
        with jira.conn.client() as client: # type: JiraClient

            # Cast to enable code completion
            client = cast_('JiraClient', client)

            # Get details of a ticket (issue) from Jira
            ticket = client.get_issue(input.key)

        # Observe that ticket may be None (e.g. invalid key), hence this 'if' guard ..
        if ticket:

            # .. build a shortcut reference to all the fields in the ticket ..
            fields = ticket['fields']

            # .. build our response object ..
            response = GetTicketDetailsResponse()
            response.assigned_to = fields['assignee']['emailAddress']
            response.progress_info = fields['progress']

            # .. and return the response to our caller.
            self.response.payload = response

# ###########################################################################


创建 REST 通道并对其进行测试

最后剩下的部分是一个 REST 通道,用于调用我们的服务。我们将在输入时提供票证 ID(密钥),服务将回复在 Jira 中为该票证找到的内容。

我们现在准备好进行最后一步 - 我们调用通道,该通道调用与 Jira 通信的服务,将来自 Jira 的响应转换为我们需要的输出:

$ curl localhost:17010/jira1 -d '{"key":"ABC-123"}'
{
    "assigned_to":"zato@example.com",
    "progress_info": {
        "progress": 10,
        "total": 30
    }
}
$


这就是今天的一切——请记住,这只是与 Jira 集成的一种方式。另一种是使用 WebHooks,我将在以后的一篇文章中介绍。



相关文章

通过Python调用deepseek的API进行对话

import requests import json # DeepSeek 模型的 API 端点 API_URL = "https://api.deepseek.com/v1/chat/compl...

使用python调用ChatGPT的API,打造属于自己的桌面智能助手

上期图文教程,我们介绍了ChatGPT的注册使用过程,并且使用ChatGPT生成了一个CNN卷积神经网络的代码,由于ChatGPT的官方只公布了GPT-3的API接口,因此我们基于ChatGPT 3代...

Python发送微信消息(文字、图片、文件)给指定好友和微信群

本示例是调用Windows API模拟发送,用Python调用win32api这个库来调用Windows API模拟人的手动操作来发送消息。在使用前,请将你微信的窗口设置为在最前面,这样就便于程序找到...

C#调用Python脚本的方式(一),以PaddleOCR-GUI为例

前言每种语言都有每种语言的优势,Python由于其强大的生态,很多任务通过调用包就可以实现,那么学会从C#项目中调用Python脚本完成任务就很重要。C#调用Python代码有多种方式,如果Pytho...

Python 集成 DeepSeek 示例_python 集成开发工具

DeepSeek 简介DeepSeek 由知名量化资管巨头幻言方量化创立于2023年7月17日,全称杭州深度求索人工智能基础技术研究有限公司。长久以来专注于开发先进的大语言模型(LLM)和相关技术。D...

PySpark和SparkSQL基础:如何利用Python编程执行Spark(附代码)

作者:Pinar Ersoy翻译:孙韬淳校对:陈振东本文约2500字,建议阅读10分钟本文通过介绍Apache Spark在Python中的应用来讲解如何利用PySpark包执行常用函数来进行数据处理...