定义环回观察者的顺序

Defining the order of loopback observers

我目前正在使用 IBM 的环回在 node.js 中编写 REST API。我正在 运行 解决观察者顺序相关的问题,并且它们的调用顺序错误。

我的 Ticket 模型有一个 内部状态 字段。此状态作为 UUID 存储在数据库中。在大型 "metadata" 文件的某处,还定义了每个状态的人名。 外部状态 字段也是如此。

以下是Ticket.json的相关部分:

{
  "name": "Ticket",
  "properties": {
    "internalStatusId": {
      "type": "String"
    },
    "externalStatusId": {
      "type": "String"
    }
  }
}

我写了一个通用的 mixin,可以将这样一个 UUID 字段的人名转换为正确的 UUID。此 mixin 用于允许客户端通过名称设置 UUID 字段(通过不同名称的字段)。

这里是 mixin 的简化版本:

// This observer is actually a simplification of the actual observer,
// which is more generic. It handles "name to id" mapping for any number
// of fields you can configure in the <model>.json file.
Model.observe('before save', function (ctx, next) {
  let data = ctx.isNewInstance ? ctx.instance : ctx.data;
  if (data.internalStatusName) {
    data.internalStatusId = internalStatusNameToIdMap[data.internalStatusName];
    delete data.internalStatusName;
  }
  next();
});

因此,如果您向 /Tickets/1 发送一个 PUT 请求,正文为 { "internalStatusName": "Closed" },此代码会将其转换为正确的 UUID,将其放入 internalStatusId字段,并从参数中删除 internalStatusName 字段。

现在,系统中有一个业务规则:如果内部状态设置为Closed,则外部状态也需要设置为Closed。该代码位于 Ticket.js 中,因为它不是通用的,但仅与 Ticket 模型相关:

// This observer is located in Ticket.json.
// It makes sure that, when a ticket's internal status is set to Closed,
// the external status is also set to Closed.
Ticket.observe('before save', function (ctx, next) {
  let data = ctx.isNewInstance ? ctx.instance : ctx.data;
  if (data.internalStatusId === INTERNAL_STATUS_CLOSED_UUID) {
    data.externalStatusId = EXTERNAL_STATUS_CLOSED_UUID;
  }
  next();
});

我 运行 遇到的问题是这两个观察者不能很好地协同工作,因为它们的调用顺序错误。

如果我发送 { "internalStatusName": "Closed" }:

这个顺序可能是loopback首先加载Ticket.js中的观察者,然后是mixin观察者造成的。

当然,我可以修改第二个观察者来查找 internalStatusId 以及 internalStatusName 字段。然而,这会导致大量的代码重复,尤其是因为我有很多这样的业务逻辑观察器,还有很多像这样的 UUID 字段。

我一直在寻找一种方法来告诉环回 运行 这些观察者的顺序。即使是像 Model.observeFirst() 这样简单的函数(这实际上并不存在!)或链前端的更多观察者将解决这个问题。我一直无法找到这是否可行,如果可行,如何实现。

你会如何解决这个问题?

Loopback 首先加载模型的实现,然后才加载 mixins 的实现,因此顺序错误。 Loopback 使用标准的事件订阅系统。因此,您可以使用 prependListener 将侦听器添加到开头。

// Puts first
Model.prependListener('before save', function (ctx, next) {
  let data = ctx.isNewInstance ? ctx.instance : ctx.data;
  if (data.internalStatusName) {
    data.internalStatusId = internalStatusNameToIdMap[data.internalStatusName];
    delete data.internalStatusName;
  }
  next();
});