跳转到主要内容

使用ZODB和requests实现的Python/Zope3服务器端webhooks。

项目描述

nti.webhooks

https://github.com/NextThought/nti.webhooks/workflows/tests/badge.svg https://coveralls.io/repos/github/NextThought/nti.webhooks/badge.svg?branch=master Documentation Status

此软件包提供服务器支持webhooks投递的基础设施和交付机制。有关完整详情和变更日志,请参阅文档

Webhooks

Webhooks是一个从一方(源)到另一方(目的地)的HTTPS请求。这些请求是单向的:源将请求发送到目的地,除了确认已收到请求外,不采取任何进一步行动(来自目的地的请求响应无关紧要)。这些请求从源发送到目的地,以便让目的地知道已发生某些事情:创建了新的实体(或资源,在REST意义上),更新或删除了旧的,等等。此类请求通常在正文中携带有效载荷,提供有关操作的信息(通常是受影响资源的表示)。目的地通过完整的URL进行识别;目的地可能希望被告知影响其处理的所有可能类型的实体的事件。

此软件包

本包安装在源服务器上,用于管理webhook的注册和发送。注册可以是静态的,也可以是动态的,例如在REST Hooks的情况下,可以启动和停止单个“订阅”。

本包旨在与使用zope.event的具有高度事件驱动应用程序集成,这些应用程序使用zope.interface定义其资源,使用zope.component管理事件传递、资源适配和依赖注入,并(可选)使用zope.sitenti.site实现组件注册的层次结构。数据持久性通过持久对象提供,通常使用ZODB

数据模型(订阅组合)

本包的一个激励性示例是与其Zapier集成以及更一般的REST Hooks概念。

在此模型中,当事件发生时向目标URL发送数据的服务器(源)上的配置称为订阅。订阅应包括

  1. 订阅包含的事件名称(或名称);

  2. 父用户或账户关系;

  3. 目标URL;以及

  4. 激活与不激活状态。

订阅查找必须高效,因此订阅的用户和事件名称信息应易于查找。

在此,事件名称被定义为“使用名词.动词点语法,即:contact.create或lead.delete)。”使用zope.eventzope.component,这对应于对象类型或接口与事件类型或接口的配对。例如,(IContentContainer, IObjectAddedEvent).

Zapier为每个事件名称生成一个唯一的target URL,因此要获取单个类型对象的创建(添加)、修改和删除资源,将有三个不同的target URL和因此三个不同的订阅。一般来说,对象类型和事件类型与target URL或订阅的扩展是N x M。

本包直接实现了此模型。(当然,您可以使用应用于多个对象或事件类型的伞形接口将相关事件发送到单个订阅。)为了展示目的,可以编写聚合“某种类型对象的全部webhook交付”或“某种类型事件的全部webhook交付”的数据视图,但这在目前的设置下并不特别自然。

此模型的一个重要结果是,不需要在给定的HTTP请求中显式包含识别事件类型的任何内容;默认方言(见下文)假定URL包含接收者所需的全部内容,并且不会做像添加X-NTI-EventType头或添加到JSON体中的任何操作。它可以是URL参数或整个不同的URL,无关紧要。

范围之外

某些关注点超出了本包的范围(但基于此包构建的其他包可能提供它们)。这些包括但不限于

  • 提供用于管理订阅的用户界面。

  • 提供用于管理订阅的HTTPS API。此包提供底层数据存储,但接受参数等,并将它们封装到正确的Python调用中,这不是一个关注点。

  • 提供用于查看webhook审计日志的用户界面或HTTPS API。

  • 仅允许特定对象触发webhook。此包处理范围(站点)和对象类型,而不是单个实例。

范围/功能

本包涵盖了一些关键的担忧点,并应提供一套完整、易于使用的解决方案来应对这些问题。当需要时,如果本包无法直接解决某个担忧点,则可以定义扩展点(接口和 zope.component 工具)。这包括但不限于以下内容:

  • 资源表示

    资源的在线形式使用 nti.externalization 构建。

    为了允许自定义外部形式,使用了一个命名的外部化器;如果不存在给定名称的外部化器,nti.externalization 将回退到默认的外部化器。默认外部化器的名称为“webhook-delivery”,但方言可能使用不同的名称。

  • 替代 Webhook 方言

    Webhooks 是一种通用协议,大部分是可互操作的。但为了支持特定目的地有特定要求的情况,“方言”被使用。存在一个默认方言,然后可能有它的特殊化。每个 webhook 订阅都可能关联到一个要使用的方言名称。这些方言在组件注册表中可以找到。例如,一个方言可能选择使用不同的外部化器名称,如“zapier-webhook-delivery”。

  • 事务性

    如果最终创建或持久化资源失败,则不应交付 Webhooks。为此,本包中的 webhook 交付与 transaction 包集成。

    资源在事务提交过程的后期阶段外部化;交付的详细信息被记录并持久化,只有在事务成功提交后,才会发起 HTTP 请求。

  • 并发性

    Webhook 交付和记录应轻量级,所有实际的网络 IO 都应以非阻塞方式执行。这意味着此包将创建线程(或使用 gevent 的 greenlets)。

  • 错误处理/失败重试

    此包提供了一定数量的重试逻辑,但这并不扩展到进程边界。如果在交付挂起时终止托管此包的进程,不会自动在其他任何进程中恢复交付尝试。

    尽管如此,API 存在以允许实现这一点。

  • 审计/交付历史

    对于每个订阅,交付尝试、状态和响应都存储在类似环形缓冲区结构中。可以检查交付是否成功、失败或从未完成。

  • 交付上的访问控制

    每个订阅都与一个拥有它的 IPrincipal 关联。只有当拥有订阅的 IPrincipal 可以访问实体时(由 zope.security 确定),才会向订阅交付请求。

  • 订阅上的访问控制

    虽然此包没有强制执行,但上述所有者关系将被用于提供角色管理器,仅授予订阅所有者删除订阅的读取和读取/写入访问权限。

    待办事项:确保客户端包可以扩展以提供管理访问。只要我们不拒绝,就应该是可以的。

  • 订阅的层次结构

    订阅是在特定的 Zope 站点内进行的(当资源被订阅时,最近的包含站点,或者如果不是,则是当前活动站点)。这些站点可能有父级。

    待办事项:解决该细节。

    当收到可能引起 webhook 交付的事件时,将检查当前活动站点中的活动订阅,以及资源本身的层次结构中的站点。所有适用订阅者都将收到交付。

    例如,如果公司总裁(管理员)在全球(根、基础或“/”)级别订阅了“新用户创建”事件,而部门负责人订阅了其部门的“新用户创建”事件(“/NOAA”),同时地方办公室经理订阅了其办公室的事件(“/NOAA/NWS/OUN”),那么在OKC办公室创建新用户可能会发送三次投递,一次给经理,一次给秘书,一次给总裁。

  • 从对象事件转换为Webhook事件

    待办事项:为我编写。

    该包需要有一种清晰的方式,让客户端包指定应产生webhook投递的事件。确切的机制待定。可能是客户端预计将使用<classImplements> ZCML指令来应用标记接口?或者它们可能为现有的接口注册由该包提供的订阅者?

    我们希望这个过程,以及查找所有活动订阅的过程,都要快。我想象的是类似于视图查找,在各个组件注册表中保持活动订阅?这不会持久化。

更改

0.0.6 (2021-09-07)

  • 在外部化时,使订阅、投递尝试、投递尝试请求和投递尝试响应具有mimeType值。

0.0.5 (2020-12-04)

  • 添加对Python 3.9的支持。

  • 主要ID不再需要是URI或点名称。见问题21

0.0.4 (2020-09-16)

  • 在查找要安装持久ZCML订阅的站点时使用自定义的ITraverser。此遍历器触发IBeforeTraverseEvent通知,让该通知的订阅者(例如nti.site.subscribers.threadSiteSubscriber)采取行动(例如在站点即将被遍历时使站点成为当前站点)。这有助于当站点路径包含命名空间时。

0.0.3 (2020-08-24)

  • 将权限定义移至单独的文件,permissions.zcml,该文件默认包含。如果遇到配置冲突,请在包含此包的配置之前使用ZCML <exclude>指令。

0.0.2 (2020-08-06)

  • 添加在主要删除时添加订阅者和删除订阅的方法。见PR 17

0.0.1 (2020-08-05)

  • 初始PyPI发布。

由以下支持