我目前正在使用IBM的环回在node.js中编写REST API。我遇到了一个问题,即观察者的顺序是相关的,并且它们的顺序是错误的。
我的 Ticket 模型具有内部状态字段。此状态作为UUID存储在数据库中。某个地方的大型元数据"文件,还定义了每个状态的人名。 外部状态字段也是如此。
以下是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();
});
因此,如果您向PUT
发送/Tickets/1
个请求{ "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" }
:
Ticket.js
的观察者。这将检查是否设置了internalStatusId
字段,以了解是否需要更新外部状态。参数中不存在internalStatusId
,因此未设置外部状态。这个顺序可能是由环回首先加载Ticket.js
中的观察者以及之后的mixin观察者引起的。
我当然可以修改第二个观察者,以查找internalStatusId
和 internalStatusName
字段。但是,这会导致严重的代码重复,特别是因为我有许多这样的业务逻辑观察器以及许多像这样的UUID字段。
我一直在寻找一种方法来告诉loopback运行这些观察者的顺序。甚至像Model.observeFirst()
这样的函数(这实际上并不存在!)来放置一个或多个观察者链的前面会解决这个问题。我无法找到这是否可能,如果可行,如何。
你会如何解决这个问题?
答案 0 :(得分:0)
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();
});