从 SQL Server 触发器调用 Freshdesk API

Posted

技术标签:

【中文标题】从 SQL Server 触发器调用 Freshdesk API【英文标题】:Calling Freshdesk API from a SQL Server trigger 【发布时间】:2018-02-25 04:14:15 【问题描述】:

我的客户希望在他的 Order 表(来自 Sage)上使用插入触发器,以使用 API 创建 Freshdesk 工单。

作为我开发的一部分,我构建了一个存储过程,它在提供订单号时可以正常工作。但是,将相同的代码移植到触发器中不会出错,但在存储过程中的相同代码工作时,Freshdesk 系统中不会出现任何内容。

我希望 cmets 了解为什么在触发器中调用 API 可能是个坏主意,但 Freshdesk 调用非常快(从存储过程开始不到 1 秒)。

我想知道的是——SQL Server 出于某种原因在架构上是否禁止这样做?如果允许,我可以在哪里查找正在引发的错误。

Edit2:好的,这是整个触发器 .. 以前的版本刚刚进行了 OA 调用。

ALTER TRIGGER [dbo].[CreateFreshdeskTicketFromOrder] 
   ON  [dbo].[OEORDH] 
   AFTER INSERT
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

  -- Get the original order number, and use that in the main lookup query
  DECLARE @ORDNUM VARCHAR(22)
  SELECT @ORDNUM = ORDNUMBER FROM inserted

  -- Variables for fields going to the API
  DECLARE @EMAIL VARCHAR(60), @SHPCONTACT VARCHAR(60), @ORDNUMBER VARCHAR(22)
  DECLARE @LOCATION VARCHAR(6), @EXPDATE INT
  DECLARE @SHPPHONEC VARCHAR(30), @SHPNAME VARCHAR(60), @DESCR VARCHAR(60)
  DECLARE @CODEEMPL VARCHAR(15)

  -- Collect field values that were just inserted
  SELECT
    @EMAIL = rtrim(OEORDH1.SHPEMAILC), @SHPCONTACT = rtrim(SHPCONTACT),
    @ORDNUMBER = rtrim(ORDNUMBER), @LOCATION = LOCATION, @EXPDATE = EXPDATE,
    @SHPPHONEC = rtrim(OEORDH1.SHPPHONEC), @SHPNAME = SHPNAME,
    @DESCR = rtrim([DESC]), @CODEEMPL = rtrim(ARSAP.CODEEMPL)
  -- FROM inserted
  FROM dbo.OEORDH
  JOIN dbo.OEORDH1 on dbo.OEORDH.ORDUNIQ   = dbo.OEORDH1.ORDUNIQ
  JOIN dbo.ARSAP   on dbo.OEORDH.SALESPER1 = dbo.ARSAP.CODESLSP
  WHERE ORDNUMBER = @ORDNUM

  -- Variables from database to the API call
  DECLARE @EXPDATE_OUT VARCHAR(10)
  SET @EXPDATE_OUT =
    substring ( cast ( @EXPDATE as varchar(8) ), 1, 4 ) + '-' +
    substring ( cast ( @EXPDATE as varchar(8) ), 5, 2 ) + '-' +
    substring ( cast ( @EXPDATE as varchar(8) ), 7, 2 );

  DECLARE @STATUS_OUT VARCHAR(2)
  IF @LOCATION = '1A'
    SET @STATUS_OUT = '23';
  ELSE
    IF @LOCATION = '1'
      SET @STATUS_OUT = '40';
    ELSE
      SET @STATUS_OUT = '2';

  -- Variables for building the API call
  DECLARE @Object INT
  DECLARE @Url VARCHAR(80)

  DECLARE @Body1 VARCHAR(1000) =
  ' ' +
    '"email": "'+ @EMAIL +'", ' +
    '"custom_fields":  "order_number": "'+ @ORDNUMBER +'", "scheduled_date": "'+ @EXPDATE_OUT + '", ' +
    '"delivered_to": "'+ @SHPCONTACT + '", ' + '"consignee_phone_number": "'+ @SHPPHONEC +'" , ' +
    '"status": '+ @STATUS_OUT + ', ' +
    '"priority": 1, "subject": "'+ rtrim(@ORDNUMBER) + ' - ' + rtrim(@SHPNAME) + ' (' + @DESCR + ')", ' +
    '"responder_id": ' + @CODEEMPL +
  ' '

  DECLARE @ResponseText VARCHAR(1000), @return_status INT

  SET @Url = 'https://client.freshdesk.com/api/v2/tickets';

  -- Do REST call to API / All return statuses commented out except for last
  Exec @return_status = sp_OACreate 'MSXML2.ServerXMLHTTP', @Object OUT;
  -- Select 'Create return', @return_status

  Exec @return_status = sp_OAMethod @Object, 'Open', NULL, 'POST', @Url, false
  -- Select 'Open return', @return_status

  Exec @return_status = sp_OAMethod @Object, 'setRequestHeader', NULL,
    'Content-Type', 'application/json'
  -- Select 'Set Request Header1 return', @return_status
  Exec @return_status = sp_OAMethod @Object, 'setRequestHeader', NULL,
    'Authorization', 'Basic ABC123=='
  -- Select 'Set Request Header2 return', @return_status

  Exec @return_status = sp_OAMethod @Object, 'Send', NULL, @Body1
  -- Select 'Send1 return', @return_status

  Exec sp_OAMethod @Object, 'ResponseText', @ResponseText OUT
  -- Select 'Response', @ResponseText

  Exec sp_OADestroy @Object

  -- Add the conversation to the TriggerLog

  IF @ResponseText IS NULL
    SET @ResponseText = '(Null)';

  INSERT INTO dbo.TriggerLog (tl_source, tl_input, tl_output) VALUES
    ( 'FreshdeskInsertTrigger', @Body1, @ResponseText )
END

这是触发代码。

具有相同代码(但将订单号作为参数)的存储过程可以正常工作并执行 API 调用和日志记录。在触发器结束时注释掉日志使得 Sage 的错误消失了,但 API 调用仍然没有到达。

【问题讨论】:

不,这不是禁止的。通常,问题出在为触发器编写的代码中。在帖子中搜索 许多 触发相关问题,这些问题几乎总是涉及关于不良编码实践、无效假设和更好解决方案的讨论。要么发布触发代码。 这里是触发代码: 不——那不是你的触发代码。充其量,它只是您的触发代码的一部分。发布全部 - 然后描述它的作用。既然您声称它在存储过程中工作,那么您最简单的(这绝不意味着“最好”)方法可能是在触发器中调用您的过程。你当然需要添加错误处理。 “Freshdesk 调用速度非常快” - 可能是这样 - 当一切正常时。现在假设存在导致查找失败(30 秒后)的 DNS 问题。或者网络路径有问题。或者freshdesk正在经历某种中断。在决定冒险尝试插入订单的原始交易之前,您应该考虑这些事情。以及为什么引入某种形式的队列(例如服务代理)几乎总是更好,这样网络问题就不会影响原始事务。 我同意从长远来看,这个解决方案可能会失效。但是,我试图理解为什么在没有 DNS 问题的情况下它现在不起作用。当从存储过程手动运行代码时,它似乎运行良好。当它从触发器中自动运行时,它似乎也会静默失败,我试图了解原因。 【参考方案1】:

如果您只是从触发器(通过EXEC)调用您的工作存储过程,而不是将过程的代码包含在触发器中,会发生什么?


要仔细观察的是代码中的这个地方:

  -- Collect field values that were just inserted
  SELECT
    @EMAIL = rtrim(OEORDH1.SHPEMAILC), @SHPCONTACT = rtrim(SHPCONTACT),
    @ORDNUMBER = rtrim(ORDNUMBER), @LOCATION = LOCATION, @EXPDATE = EXPDATE,
    @SHPPHONEC = rtrim(OEORDH1.SHPPHONEC), @SHPNAME = SHPNAME,
    @DESCR = rtrim([DESC]), @CODEEMPL = rtrim(ARSAP.CODEEMPL)
  -- FROM inserted
  FROM dbo.OEORDH
  JOIN dbo.OEORDH1 on dbo.OEORDH.ORDUNIQ   = dbo.OEORDH1.ORDUNIQ
  JOIN dbo.ARSAP   on dbo.OEORDH.SALESPER1 = dbo.ARSAP.CODESLSP
  WHERE ORDNUMBER = @ORDNUM

您注释掉了FROM inserted 并尝试直接从表中读取值。

当触发器代码运行时,事务尚未提交,因此您很可能应该从inserted 表中读取值。这个SELECT 很可能没有找到具有给定@ORDNUM 的行并且变量仍然是NULL


旁注。 SQL Server 中的触发器每条语句触发一次,而不是每行触发一次。即使inserted 表有几行,您的触发器也应该可以正常工作。现在你的触发器只会选择一个ORDNUMBER,即使在表中插入了几行。这很可能不是您想要的。


如何调试触发器?

一种简单而直接的方法是创建一个用于记录的表并添加大量INSERT 语句,这些语句会将所有变量的值记录到该表中。然后您可以阅读日志并查看发生了什么。

【讨论】:

谢谢。没有解释(或者,更确切地说,我不明白)的是触发器的宇宙是一个事物尚未正式存在的宇宙。我正在做的多表连接涉及第二个表(OEORDH1),其记录也可能尚未正式存在,导致 JOIN 失败,因此触发器返回错误。 @talexb,如果除了定义触发器的表之外,还需要从其他表中获取数据,则需要小心。我对此类触发器没有太多经验,但我会尝试显式启动事务,首先将所需数据添加到“其他”表(OEORDH1),然后插入主表,触发器应该在其中触发同一笔交易。仅在插入主表后才显式提交事务。您需要检查,但我希望触发器能够“看到”“其他”表中的新数据,如果它被插入到与主表相同的事务中 触发器在覆盖原始语句的事务上下文中运行。因此,您对无法“此 SELECT 可能找不到具有给定 @ORDNUM 的行...”的担忧是无效的。就像在单个事务中您始终可以回读 已插入的行一样。 @Damien_The_Unbeliever,你是对的。如果您直接引用原始表,inserted 表中可见的行也可见。事实证明,在这种特定情况下,涉及的另一个表也在发生变化(目前尚不清楚它究竟是如何变化的),这可能会使一切复杂化。

以上是关于从 SQL Server 触发器调用 Freshdesk API的主要内容,如果未能解决你的问题,请参考以下文章

SQL server T-SQL存储过程

在删除 SQL Server 之前显示结果

SQL Server 触发器内的存储过程调用是不是隐含线程安全和原子?

在 SQL Server 中获取触发器的调用者

SQL Server 触发器

SQL server 创建触发器详解