Moralis 觸發(fā)器

2022-05-12 11:12 更新

介紹

觸發(fā)器是一種響應(yīng)特定服務(wù)器端事件(例如將數(shù)據(jù)保存到特定集合時)執(zhí)行操作的方法。 這些通常有兩種情況。

  • Before X:在 X 發(fā)生之前調(diào)用觸發(fā)器。
  • After X:在 X 發(fā)生后調(diào)用觸發(fā)器。

觸發(fā)器針對特定的集合(例如 ?EthTransactions?)進(jìn)行操作。 例如,要在轉(zhuǎn)移特定 NFT 時將訂單對象的狀態(tài)更新為“已售出”,您可以在 ?EthNFTTransfers ?上定義 ?afterSave ?觸發(fā)器。 每次將新條目添加到 ?EthNFTTransfers ?集合時都會調(diào)用觸發(fā)器,您可以檢查它是否與其中一個未結(jié)訂單匹配。

未經(jīng)確認(rèn)的交易

測試網(wǎng)和主網(wǎng)上的交易可能需要一段時間才能得到確認(rèn)。 當(dāng) Moralis 檢測到處于未確認(rèn)狀態(tài)的新交易(或事件)時,這些交易將被放入帶有confirmed:true集合中。 當(dāng)交易被確認(rèn)時,狀態(tài)更新為confirmed:true。

觸發(fā)器的后果

這意味著如果您在集合上定義 ?afterSave ?觸發(fā)器,則觸發(fā)器可以被觸發(fā)兩次——一次為confirmed:false,再次為confirmed:true。 任何具有confirmed屬性的集合都可能發(fā)生這種情況。 考慮到這種行為,編寫您的觸發(fā)器回調(diào)函數(shù)。 如果觸發(fā)器在其他集合中創(chuàng)建條目,則這可能導(dǎo)致重復(fù)條目或計算中的重復(fù)計數(shù)等。

可以通過使用 ?request.object? 獲取觸發(fā)觸發(fā)器的對象來檢查確認(rèn)狀態(tài)。

Moralis.Cloud.afterSave("EthTransactions", async function (request) {
  const confirmed = request.object.get("confirmed");
  if (confirmed) {
    // do something
  } else {
    // handle unconfirmed case
  }
});

本地開發(fā)鏈

?Ganache ?和 ?Hardhat ?僅在進(jìn)行交易時處理新塊。 這意味著,如果您鑄造新的 ?NFT ?或進(jìn)行代幣轉(zhuǎn)移,這些交易可能會卡在待處理表中。 一次確認(rèn)后,這些將被移出待處理。

保存觸發(fā)器

保存之前

實施數(shù)據(jù)驗證

在云中運行代碼的另一個原因是強制執(zhí)行特定的數(shù)據(jù)格式。 例如,您可能同時擁有一個 Android 和 iOS 應(yīng)用程序,并且您希望驗證每個應(yīng)用程序的數(shù)據(jù)。 無需為每個客戶端環(huán)境編寫一次代碼,您可以使用云代碼只編寫一次。

數(shù)據(jù)驗證可以在觸發(fā)器的代碼中完成。

Moralis.Cloud.beforeSave("Review", (request) => {
   throw "validation error";
}
});

如果函數(shù)拋出,那么 ?Review ?對象將不會被保存并且客戶端會得到一個錯誤。 如果沒有拋出任何東西,對象將被正常保存。

一個有用的提示是,即使您的應(yīng)用程序有許多不同的版本,相同版本的云代碼也適用于所有版本。 因此,如果您啟動的應(yīng)用程序沒有正確檢查輸入數(shù)據(jù)的有效性,您仍然可以通過使用 ?beforeSave ?添加驗證來解決此問題。

修改保存的對象

在某些情況下,您不想丟棄無效數(shù)據(jù)。 您只想在保存之前對其進(jìn)行一些調(diào)整。 ?beforeSave ?也可以處理這種情況。 您對 ?request.object? 所做的任何調(diào)整都將被保存。

在我們的電影評論示例中,我們可能希望確保評論不會太長。 一條長評論可能很難顯示。 我們可以使用 ?beforeSave ?將評論字段截斷為 140 個字符:

Moralis.Cloud.beforeSave("Review", (request) => {
  const comment = request.object.get("comment");
  if (comment.length > 140) {
    // Truncate and add a ...
    request.object.set("comment", comment.substring(0, 137) + "...");
  }
});

預(yù)定義類

如果您想對 Moralis JavaScript SDK 中的預(yù)定義類使用 ?beforeSave?,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:

Moralis.Cloud.beforeSave(
  Moralis.User,
  async (request) => {
    // code here
  }
  // Validation Object or Validation Function
);

保存后

在某些情況下,您可能希望在保存對象后執(zhí)行某種類型的操作,例如推送。 您可以通過使用 ?afterSave ?方法注冊處理程序來做到這一點。 例如,假設(shè)您想要跟蹤博客文章的評論數(shù)量。 您可以通過編寫如下函數(shù)來做到這一點:

const logger = Moralis.Cloud.getLogger();

Moralis.Cloud.afterSave("Comment", (request) => {
  const query = new Moralis.Query("Post");
  query
    .get(request.object.get("post").id)
    .then(function (post) {
      post.increment("comments");
      return post.save();
    })
    .catch(function (error) {
      logger.error("Got an error " + error.code + " : " + error.message);
    });
});

異步行為

在上面的示例中,客戶端將在處理程序中的承諾完成之前收到成功的響應(yīng),無論承諾如何解決。 例如,即使處理程序拋出異常,客戶端也會收到成功的響應(yīng)。 運行處理程序時發(fā)生的任何錯誤都可以在云代碼日志中找到。

在將響應(yīng)發(fā)送回客戶端后,您可以使用 ?afterSave ?處理程序執(zhí)行冗長的操作。 為了在 ?afterSave ?處理程序完成之前響應(yīng)客戶端,您的處理程序可能不會返回承諾,并且您的 ?afterSave ?處理程序可能不會使用 ?async/await?。

預(yù)定義類

如果您想將 ?afterSave ?用于 Moralis JavaScript SDK 中的預(yù)定義類,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:

Moralis.Cloud.afterSave(Moralis.User, async (request) => {
  // code here
});

上下文

保存 ?Moralis.Object? 時,您可以傳遞可在 ?Cloud Code Save Triggers? 中訪問的上下文字典。

上下文也從 ?beforeSave ?處理程序傳遞到 ?afterSave ?處理程序。 以下示例異步向正在添加到 ?Moralis.Role? 的用戶關(guān)系的用戶發(fā)送電子郵件,以便客戶端在電子郵件完成發(fā)送之前收到響應(yīng):

const beforeSave = function beforeSave(request) {
  const { object: role } = request;
  // Get users that will be added to the users relation.
  const usersOp = role.op("users");
  if (usersOp && usersOp.relationsToAdd.length > 0) {
    // add the users being added to the request context
    request.context = { buyers: usersOp.relationsToAdd };
  }
};

const afterSave = function afterSave(request) {
  const { object: role, context } = request;
  if (context && context.buyers) {
    const purchasedItem = getItemFromRole(role);
    const promises = context.buyers.map(emailBuyer.bind(null, purchasedItem));
    item.increment("orderCount", context.buyers.length);
    promises.push(item.save(null, { useMasterKey: true }));
    Promise.all(promises).catch(request.log.error.bind(request.log));
  }
};

刪除觸發(fā)器

刪除前

您可以在刪除對象之前運行自定義云代碼。 您可以使用 ?beforeDelete ?方法執(zhí)行此操作。 例如,這可用于實現(xiàn)比通過 ?ACLs? 表達(dá)的內(nèi)容更復(fù)雜的受限刪除策略。 例如,假設(shè)您有一個相冊應(yīng)用程序,其中許多照片與每個相冊相關(guān)聯(lián),并且您希望阻止用戶刪除仍然有照片的相冊。 您可以通過編寫如下函數(shù)來做到這一點:

Moralis.Cloud.beforeDelete("Album", async (request) => {
  const query = new Moralis.Query("Photo");
  query.equalTo("album", request.object);
  const count = await query.count({ useMasterKey: true });
  if (count > 0) {
    throw "Can't delete album if it still has photos.";
  }
});

如果函數(shù)拋出,?Album?對象不會被刪除,客戶端會報錯。 否則,對象將被正常刪除。

預(yù)定義類

如果您想對 ?Moralis JavaScript SDK? 中的預(yù)定義類使用 ?beforeDelete?,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:

Moralis.Cloud.beforeDelete(Moralis.User, async (request) => {
  // code here
});

刪除后

在某些情況下,您可能希望在刪除對象后執(zhí)行某種類型的操作,例如推送。 您可以通過使用 ?afterDelete ?方法注冊處理程序來完成此操作。 例如,假設(shè)在刪除博客文章后,您還想刪除所有關(guān)聯(lián)的評論。 您可以通過編寫如下函數(shù)來做到這一點:

const logger = Moralis.Cloud.getLogger();

Moralis.Cloud.afterDelete("Post", (request) => {
  const query = new Moralis.Query("Comment");
  query.equalTo("post", request.object);
  query
    .find()
    .then(Moralis.Object.destroyAll)
    .catch((error) => {
      logger.error(
        "Error finding related comments " + error.code + ": " + error.message
      );
    });
});

?afterDelete? 處理程序可以訪問通過 ?request.object? 刪除的對象。 此對象已完全提取,但無法重新提取或重新保存。

在處理程序終止后,客戶端將收到對刪除請求的成功響應(yīng),無論 ?afterDelete ?是如何終止的。 例如,即使處理程序拋出異常,客戶端也會收到成功的響應(yīng)。 運行處理程序時發(fā)生的任何錯誤都可以在云代碼日志中找到。

預(yù)定義類

如果您想對 ?Moralis JavaScript SDK? 中的預(yù)定義類使用 ?afterDelete?,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:

Moralis.Cloud.afterDelete(Moralis.User, async (request) => {
  // code here
});

文件觸發(fā)器

保存文件之前

使用 ?beforeSaveFile ?方法,您可以在保存任何文件之前運行自定義云代碼。 返回一個新的 ?Moralis.File? 將保存新文件,而不是客戶端發(fā)送的文件。

例如

// Changing the file name
Moralis.Cloud.beforeSaveFile(async (request) => {
  const { file } = request;
  const fileData = await file.getData();
  const newFile = new Moralis.File("a-new-file-name.txt", { base64: fileData });
  return newFile;
});

// Returning an already saved file
Moralis.Cloud.beforeSaveFile((request) => {
  const { user } = request;
  const avatar = user.get("avatar"); // this is a Moralis.File that is already saved to the user object
  return avatar;
});

// Saving a different file from uri
Moralis.Cloud.beforeSaveFile((request) => {
  const newFile = new Moralis.File("some-file-name.txt", {
    uri: "www.somewhere.com/file.txt",
  });
  return newFile;
});

元數(shù)據(jù)和標(biāo)簽

向您的文件添加元數(shù)據(jù)和標(biāo)簽允許您向存儲在您的存儲解決方案(即 AWS S3)中的文件添加額外的數(shù)據(jù)位。 ?beforeSaveFile ?鉤子是在文件上設(shè)置元數(shù)據(jù)和/或標(biāo)簽的好地方。

注意:并非所有存儲適配器都支持元數(shù)據(jù)和標(biāo)簽。 檢查您正在使用的存儲適配器的文檔以了解兼容性。

// Adding metadata and tags
Moralis.Cloud.beforeSaveFile((request) => {
  const { file, user } = request;
  file.addMetadata("createdById", user.id);
  file.addTag("groupId", user.get("groupId"));
});

保存文件后

?afterSaveFile ?方法是跟蹤應(yīng)用程序中存儲的所有文件的好方法。 例如:

Moralis.Cloud.afterSaveFile(async (request) => {
  const { file, fileSize, user } = request;
  const fileObject = new Moralis.Object("FileObject");
  fileObject.set("file", file);
  fileObject.set("fileSize", fileSize);
  fileObject.set("createdBy", user);
  const token = { sessionToken: user.getSessionToken() };
  await fileObject.save(null, token);
});

刪除文件之前

您可以在刪除任何文件之前運行自定義云代碼。 例如,假設(shè)您要添加只允許創(chuàng)建文件的用戶刪除文件的邏輯。 您可以使用 ?afterSaveFile ?和 ?beforeDeleteFile ?方法的組合,如下所示:

Moralis.Cloud.afterSaveFile(async (request) => {
  const { file, user } = request;
  const fileObject = new Moralis.Object('FileObject');
  fileObject.set('fileName', file.name());
  fileObject.set('createdBy', user);
  await fileObject.save(null, { useMasterKey: true );
});

Moralis.Cloud.beforeDeleteFile(async (request) => {
  const { file, user } = request;
  const query = new Moralis.Query('FileObject');
  query.equalTo('fileName', file.name());
  const fileObject = await query.first({ useMasterKey: true });
  if (fileObject.get('createdBy').id !== user.id) {
    throw 'You do not have permission to delete this file';
  }
});

刪除文件后

在上面的 ?beforeDeleteFile ?示例中,?FileObject ?集合用于跟蹤應(yīng)用程序中保存的文件。 成功刪除文件后,?afterDeleteFile ?觸發(fā)器是清理這些對象的好地方。

Moralis.Cloud.afterDeleteFile(async (request) => {
  const { file } = request;
  const query = new Moralis.Query("FileObject");
  query.equalTo("fileName", file.name());
  const fileObject = await query.first({ useMasterKey: true });
  await fileObject.destroy({ useMasterKey: true });
});

查找觸發(fā)器

查找之前

在某些情況下,您可能希望轉(zhuǎn)換傳入查詢、添加額外限制或增加默認(rèn)限制、添加額外包含或?qū)⒔Y(jié)果限制為鍵的子集。 您可以使用 ?beforeFind ?觸發(fā)器來執(zhí)行此操作。

例如

// Properties available
Moralis.Cloud.beforeFind("MyObject", (req) => {
  let query = req.query; // the Moralis.Query
  let user = req.user; // the user
  let triggerName = req.triggerName; // beforeFind
  let isMaster = req.master; // if the query is run with masterKey
  let isCount = req.count; // if the query is a count operation
  let logger = req.log; // the logger
  let installationId = req.installationId; // The installationId
});

// Selecting keys
Moralis.Cloud.beforeFind("MyObject", (req) => {
  let query = req.query; // the Moralis.Query
  // Force the selection on some keys
  query.select(["key1", "key2"]);
});

// Asynchronous support
Moralis.Cloud.beforeFind("MyObject", (req) => {
  let query = req.query;
  return aPromise().then((results) => {
    // do something with the results
    query.containedIn("key", results);
  });
});

// Returning a different query
Moralis.Cloud.beforeFind("MyObject", (req) => {
  let query = req.query;
  let otherQuery = new Moralis.Query("MyObject");
  otherQuery.equalTo("key", "value");
  return Moralis.Query.or(query, otherQuery);
});

// Rejecting a query
Moralis.Cloud.beforeFind("MyObject", (req) => {
  // throw an error
  throw new Moralis.Error(101, "error");

  // rejecting promise
  return Promise.reject("error");
});

// Setting the read preference for a query
Moralis.Cloud.beforeFind("MyObject2", (req) => {
  req.readPreference = "SECONDARY_PREFERRED";
  req.subqueryReadPreference = "SECONDARY";
  req.includeReadPreference = "PRIMARY";
});

預(yù)定義類

如果要將 ?beforeFind ?用于 ?Moralis JavaScript SDK? 中的預(yù)定義類,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:

Moralis.Cloud.beforeFind(Moralis.User, async (request) => {
  // code here
});

查找后

在某些情況下,您可能希望在將查詢結(jié)果發(fā)送到客戶端之前對其進(jìn)行操作。 您可以使用 ?afterFind ?觸發(fā)器執(zhí)行此操作。

Moralis.Cloud.afterFind("MyCustomClass", async (request) => {
  // code here
});

預(yù)定義類

如果要將 ?afterFind ?用于 ?Moralis JavaScript SDK? 中的預(yù)定義類,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:

Moralis.Cloud.afterFind(Moralis.User, async (request) => {
  // code here
});

會話觸發(fā)器

登錄前

有時您可能希望對登錄請求運行自定義驗證。 ?beforeLogin ?觸發(fā)器可用于阻止帳戶登錄(例如,如果他們被禁止),記錄登錄事件以進(jìn)行分析,如果登錄發(fā)生在不尋常的 IP 地址上,則通過電子郵件通知用戶等等。

Moralis.Cloud.beforeLogin(async (request) => {
  const { object: user } = request;
  if (user.get("isBanned")) {
    throw new Error("Access denied, you have been banned.");
  }
});

需要注意的一些注意事項

  • 它等待任何?promise?解決。
  • 用戶在請求對象上不可用 - 直到 ?beforeLogin ?成功完成之后,才向用戶提供會話。
  • 與 ?Moralis.User? 上的 ?afterSave ?一樣,除非明確保存,否則它不會將突變保存到用戶。

觸發(fā)器將運行

  • 用戶名和密碼登錄。
  • 在 ?authProvider ?登錄時。

觸發(fā)器不會運行

  • 注冊時。
  • 如果登錄憑據(jù)不正確。

注銷后

有時您可能希望在用戶注銷后運行操作。 例如,?afterLogout ?觸發(fā)器可用于用戶注銷后的清理操作。 觸發(fā)器包含已在注銷時刪除的會話對象。 從此會話對象中,您可以確定注銷以執(zhí)行用戶特定任務(wù)的用戶。

Moralis.Cloud.afterLogout(async (request) => {
  const { object: session } = request;
  const user = session.get("user");
  user.set("isOnline", false);
  user.save(null, { useMasterKey: true });
});

需要注意的一些注意事項

  • 與 ?afterDelete ?觸發(fā)器一樣,請求中包含的 ?_Session? 對象已被刪除。

觸發(fā)器將運行

  • 當(dāng)用戶注銷并刪除 ?_Session? 對象時。

觸發(fā)器不會運行

  • 如果用戶注銷并且沒有找到要刪除的 ?_Session? 對象。
  • 如果 ?_Session? 對象被刪除,而用戶沒有通過調(diào)用 SDK 的 logout 方法注銷。

LiveQuery 觸發(fā)器

連接前

您可以在用戶嘗試使用 ?beforeConnect ?方法連接到您的 LiveQuery 服務(wù)器之前運行自定義云代碼。 例如,這可用于僅允許已登錄的用戶連接到 LiveQuery 服務(wù)器。

Moralis.Cloud.beforeConnect((request) => {
  if (!request.user) {
    throw "Please login before you attempt to connect.";
  }
});

大多數(shù)情況下,?connect ?事件在客戶端第一次調(diào)用 ?subscribe ?時被調(diào)用。 如果這是您的用例,您可以使用此事件偵聽錯誤。

const logger = Moralis.Cloud.getLogger();

Moralis.LiveQuery.on("error", (error) => {
  logger.info(error);
});

訂閱前

在某些情況下,您可能希望轉(zhuǎn)換傳入的訂閱查詢。 示例包括添加額外限制、增加默認(rèn)限制、添加額外包含或?qū)⒔Y(jié)果限制為鍵的子集。 您可以使用 ?beforeSubscribe ?觸發(fā)器來執(zhí)行此操作。

Moralis.Cloud.beforeSubscribe("MyObject", (request) => {
  if (!request.user.get("Admin")) {
    throw new Moralis.Error(
      101,
      "You are not authorized to subscribe to MyObject."
    );
  }
  let query = request.query; // the Moralis.Query
  query.select("name", "year");
});

afterLiveQueryEvent

在某些情況下,您可能希望在將實時查詢的結(jié)果發(fā)送到客戶端之前對其進(jìn)行操作。 您可以使用 ?afterLiveQueryEvent ?觸發(fā)器執(zhí)行此操作。

例如

// Changing values on object and original
Moralis.Cloud.afterLiveQueryEvent("MyObject", (request) => {
  const object = request.object;
  object.set("name", "***");

  const original = request.original;
  original.set("name", "yolo");
});

// Prevent LiveQuery trigger unless 'foo' is modified
Moralis.Cloud.afterLiveQueryEvent("MyObject", (request) => {
  const object = request.object;
  const original = request.original;
  if (!original) {
    return;
  }
  if (object.get("foo") != original.get("foo")) {
    request.sendEvent = false;
  }
});

默認(rèn)情況下,?MoralisLiveQuery ?不執(zhí)行需要額外數(shù)據(jù)庫操作的查詢。 這是為了讓您的 ?Moralis Server? 盡可能快速和高效。 如果您需要此功能,您可以在 ?afterLiveQueryEvent ?中執(zhí)行這些功能。

// Including an object on LiveQuery event, on update only.
Moralis.Cloud.afterLiveQueryEvent("MyObject", async (request) => {
  if (request.event != "update") {
    request.sendEvent = false;
    return;
  }
  const object = request.object;
  const pointer = object.get("child");
  await pointer.fetch();
});

// Extend matchesQuery functionality to LiveQuery
Moralis.Cloud.afterLiveQueryEvent("MyObject", async (request) => {
  if (request.event != "create") {
    return;
  }
  const query = request.object.relation("children").query();
  query.equalTo("foo", "bart");
  const first = await query.first();
  if (!first) {
    request.sendEvent = false;
  }
});

需要注意的一些注意事項

  • 在 ?afterLiveQueryEvent ?觸發(fā)器完成之前,不會觸發(fā)實時查詢事件。 確保觸發(fā)器內(nèi)的任何功能都有效且具有限制性,以防止出現(xiàn)瓶頸。

onLiveQueryEvent

有時您可能希望監(jiān)視要與第三方(例如“?Datadog?”)一起使用的實時查詢事件。 ?onLiveQueryEvent ?觸發(fā)器可以記錄觸發(fā)的事件、連接的客戶端數(shù)量、訂閱數(shù)量和錯誤。

Moralis.Cloud.onLiveQueryEvent(
  ({
    event,
    client,
    sessionToken,
    useMasterKey,
    installationId,
    clients,
    subscriptions,
    error,
  }) => {
    if (event !== "ws_disconnect") {
      return;
    }
    // Do your magic
  }
);

事件

  • connect
  • subscribe
  • unsubscribe
  • ws_connect
  • ws_disconnect
  • ws_disconnect_error

“connect”與“ws_connect”不同,前者意味著客戶端完成了Moralis Live Query協(xié)議定義的連接過程,其中“ws_connect”只是意味著創(chuàng)建了一個新的websocket。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號