diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c340fb..7399017a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,13 @@ In this file you can find what's changed in each version. (Versions with -dev, - --- -### 4.5.1 +### 4.7.0 + +- New Wix apps added to Sync plug-in including: Wix Stores, Wix eCommerce, Wix Marketing, Wix Pricing Plans, Wix Blog. Now you can sync all data from these apps easily. +- Wix Members now also has functions for badges collection. +- Some BUG Fixes for sync plugins + +### 4.5.1, 4.5.2 - BUG Fixes and TS type fixes. For Wix Members sync plug-in. diff --git a/app/lib/Apps/wix_blog.js b/app/lib/Apps/wix_blog.js new file mode 100644 index 00000000..df58ce12 --- /dev/null +++ b/app/lib/Apps/wix_blog.js @@ -0,0 +1,297 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.onPostCreated = onPostCreated; +exports.onPostUpdated = onPostUpdated; +exports.onPostDeleted = onPostDeleted; +exports.onCategoryCreated = onCategoryCreated; +exports.onCategoryUpdated = onCategoryUpdated; +exports.onCategoryDeleted = onCategoryDeleted; +exports.onTagCreated = onTagCreated; +exports.onTagUpdated = onTagUpdated; +exports.onTagDeleted = onTagDeleted; +const error_manager_1 = require("../Errors/error_manager"); +const insert_1 = require("../Functions/insert"); +const native_1 = require("../Functions/native"); +const sleep_1 = require("./sleep"); +const wix_data_1 = __importDefault(require("wix-data")); +const weiv_data_config_1 = require("../Config/weiv_data_config"); +const logCollection = "WeivDataWixAppsSyncLogs/WixBlog"; +async function onPostCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const postId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Post Created - ${postId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyBlogPost = await getBlogPostData(postId); + (await (0, native_1.native)(`${syncDatabase}/WixBlogPosts`, true)).insertOne(readyBlogPost, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog post couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create blog post when syncing Wix Blog: ${err}`); + } +} +async function onPostUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const postId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Post Updated - ${postId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: postId } }; + const readyBlogPost = await getBlogPostData(postId); + (await (0, native_1.native)(`${syncDatabase}/WixBlogPosts`, true)).updateOne(filter, { $set: readyBlogPost }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog post couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update blog post when syncing Wix Blog: ${err}`); + } +} +async function onPostDeleted(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const postId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Post Deleted - ${postId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: postId } }; + (await (0, native_1.native)(`${syncDatabase}/WixBlogPosts`, true)).deleteMany(filter, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog post couldn't be deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't delete blog post when syncing Wix Blog: ${err}`); + } +} +async function onCategoryCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const categoryId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Category Created - ${categoryId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyCategoryData = await getBlogCategoryData(categoryId); + (await (0, native_1.native)(`${syncDatabase}/WixBlogCategories`, true)).insertOne(readyCategoryData, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog category couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create blog category when syncing Wix Blog: ${err}`); + } +} +async function onCategoryUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const categoryId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Category Updated - ${categoryId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: categoryId } }; + const readyCategoryData = await getBlogCategoryData(categoryId); + (await (0, native_1.native)(`${syncDatabase}/WixBlogCategories`, true)).updateOne(filter, { $set: readyCategoryData }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog category couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update blog category when syncing Wix Blog: ${err}`); + } +} +async function onCategoryDeleted(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const categoryId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Category Deleted - ${categoryId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: categoryId } }; + (await (0, native_1.native)(`${syncDatabase}/WixBlogCategories`, true)).deleteMany(filter, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog category couldn't be deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't delete blog category when syncing Wix Blog: ${err}`); + } +} +async function onTagCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const tagId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Tag Created - ${tagId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyTagData = await getBlogTagData(tagId); + (await (0, native_1.native)(`${syncDatabase}/WixBlogTags`, true)).insertOne(readyTagData, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog tag couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create blog tag when syncing Wix Blog: ${err}`); + } +} +async function onTagUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const tagId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Tag Updated - ${tagId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: tagId } }; + const readyTagData = await getBlogTagData(tagId); + (await (0, native_1.native)(`${syncDatabase}/WixBlogTags`, true)).updateOne(filter, { $set: readyTagData }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog tag couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update blog tag when syncing Wix Blog: ${err}`); + } +} +async function onTagDeleted(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const tagId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Blog Tag Deleted - ${tagId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: tagId } }; + (await (0, native_1.native)(`${syncDatabase}/WixBlogTags`, true)).deleteMany(filter, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Blog tag couldn't be deeleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't delete blog tag when syncing Wix Blog: ${err}`); + } +} +async function getBlogPostData(postId) { + try { + if (!postId) { + (0, error_manager_1.kaptanLogar)("00024", `postId is required but it's undefined or invalid when syncing Wix Blog`); + } + const blogPost = await wix_data_1.default.get("Blog/Posts", postId, { suppressAuth: true, consistentRead: true }); + const readyBlogPost = { ...blogPost, entityId: blogPost._id }; + delete readyBlogPost._id; + return readyBlogPost; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `failed to get blog post data when syncing Wix Blog: ${err}`); + } +} +async function getBlogCategoryData(categoryId) { + try { + if (!categoryId) { + (0, error_manager_1.kaptanLogar)("00024", `categoryId is required but it's undefined or invalid when syncing Wix Blog`); + } + const blogCategory = await wix_data_1.default.get("Blog/Categories", categoryId, { suppressAuth: true, consistentRead: true }); + const readyBlogCategory = { ...blogCategory, entityId: blogCategory._id }; + delete readyBlogCategory._id; + return readyBlogCategory; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `failed to get blog category data when syncing Wix Blog: ${err}`); + } +} +async function getBlogTagData(tagId) { + try { + if (!tagId) { + (0, error_manager_1.kaptanLogar)("00024", `tagId is required but it's undefined or invalid when syncing Wix Blog`); + } + const blogTag = await wix_data_1.default.get("Blog/Tags", tagId, { suppressAuth: true, consistentRead: true }); + const readyBlogTag = { ...blogTag, entityId: blogTag._id }; + delete readyBlogTag._id; + return readyBlogTag; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `failed to get blog tag data when syncing Wix Blog: ${err}`); + } +} diff --git a/app/lib/Apps/wix_ecom.js b/app/lib/Apps/wix_ecom.js new file mode 100644 index 00000000..310226d0 --- /dev/null +++ b/app/lib/Apps/wix_ecom.js @@ -0,0 +1,136 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.onOrderCreated = onOrderCreated; +exports.onOrderUpdated = onOrderUpdated; +exports.onAbandonedCheckoutCreated = onAbandonedCheckoutCreated; +exports.onAbandonedCheckoutRecovered = onAbandonedCheckoutRecovered; +exports.getOrderData = getOrderData; +const error_manager_1 = require("../Errors/error_manager"); +const insert_1 = require("../Functions/insert"); +const native_1 = require("../Functions/native"); +const sleep_1 = require("./sleep"); +const wix_ecom_backend_1 = require("wix-ecom-backend"); +const wix_auth_1 = require("wix-auth"); +const weiv_data_config_1 = require("../Config/weiv_data_config"); +const logCollection = "WeivDataWixAppsSyncLogs/WixeCom"; +async function onOrderCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const orderId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix eCom Order Created - ${orderId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyOrder = await getOrderData(orderId); + (await (0, native_1.native)(`${syncDatabase}/WixeComOrders`, true)).insertOne(readyOrder, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "eCommerce order couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create order when syncing Wix eCom: ${err}`); + } +} +async function onOrderUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const orderId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix eCom Order Updated - ${orderId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyOrder = await getOrderData(orderId); + const filter = { "entityId": { $eq: orderId } }; + (await (0, native_1.native)(`${syncDatabase}/WixeComOrders`, true)).updateOne(filter, { $set: readyOrder }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "eCommerce order couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update order when syncing Wix eCom: ${err}`); + } +} +async function onAbandonedCheckoutCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const abandonedCheckoutId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix eCom Abandoned Checkout Created - ${abandonedCheckoutId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyAbandonedCheckout = { ...event.entity, entityId: abandonedCheckoutId }; + delete readyAbandonedCheckout._id; + (await (0, native_1.native)(`${syncDatabase}/WixeComAbandonedCheckouts`, true)).insertOne(readyAbandonedCheckout, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "eCommerce abandoned checkout couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create abandoned checkout when syncing Wix eCom: ${err}`); + } +} +async function onAbandonedCheckoutRecovered(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const abandonedCheckoutId = event.data.abandonedCheckout._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix eCom Abandoned Checkout Updated/Recovered - ${abandonedCheckoutId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: abandonedCheckoutId } }; + const readyAbandonedCheckout = { ...event.data.abandonedCheckout, entityId: abandonedCheckoutId }; + delete readyAbandonedCheckout._id; + (await (0, native_1.native)(`${syncDatabase}/WixeComAbandonedCheckouts`, true)).updateOne(filter, { $set: readyAbandonedCheckout }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "eCommerce abandoned checkout couldn't be updated", + entityId: event.data.abandonedCheckout._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update abandoned checkout when syncing Wix eCom: ${err}`); + } +} +async function getOrderData(orderId) { + try { + if (!orderId) { + (0, error_manager_1.kaptanLogar)("00024", "orderId is required but it's undefined"); + } + const elevatedGetOrder = (0, wix_auth_1.elevate)(wix_ecom_backend_1.orders.getOrder); + const order = await elevatedGetOrder(orderId); + const readyOrder = { ...order, entityId: order._id }; + delete readyOrder._id; + return readyOrder; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `failed to get the order data when syncing Wix eCom: ${err}`); + } +} diff --git a/app/lib/Apps/wix_marketing.js b/app/lib/Apps/wix_marketing.js new file mode 100644 index 00000000..d72fe848 --- /dev/null +++ b/app/lib/Apps/wix_marketing.js @@ -0,0 +1,107 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.onCouponCreated = onCouponCreated; +exports.onCouponUpdated = onCouponUpdated; +exports.onCouponDeleted = onCouponDeleted; +const error_manager_1 = require("../Errors/error_manager"); +const insert_1 = require("../Functions/insert"); +const native_1 = require("../Functions/native"); +const sleep_1 = require("./sleep"); +const wix_data_1 = __importDefault(require("wix-data")); +const weiv_data_config_1 = require("../Config/weiv_data_config"); +const logCollection = "WeivDataWixAppsSyncLogs/WixMarketing"; +async function onCouponCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const couponId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Marketing Coupon Created - ${couponId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyCoupon = await getCouponData(couponId); + (await (0, native_1.native)(`${syncDatabase}/WixMarketingCoupons`, true)).insertOne(readyCoupon, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Marketing coupon couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create coupon when syncing Wix Marketing: ${err}`); + } +} +async function onCouponUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const couponId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Marketing Coupon Updated - ${couponId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: couponId } }; + const readyCoupon = await getCouponData(couponId); + (await (0, native_1.native)(`${syncDatabase}/WixMarketingCoupons`, true)).updateOne(filter, { $set: readyCoupon }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Marketing coupon couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update coupon when syncing Wix Marketing: ${err}`); + } +} +async function onCouponDeleted(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const couponId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Marketing Coupon Deleted - ${couponId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: couponId } }; + (await (0, native_1.native)(`${syncDatabase}/WixMarketingCoupons`, true)).deleteMany(filter, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Marketing coupon couldn't be deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't delete coupon when syncing Wix Marketing: ${err}`); + } +} +async function getCouponData(couponId) { + try { + if (!couponId) { + (0, error_manager_1.kaptanLogar)("00024", "couponId is undefined or invalid when syncing Wix Marketing"); + } + const coupon = await wix_data_1.default.get("Marketing/Coupons", couponId, { supressAuth: true, consistentRead: true }); + const readyCoupon = { ...coupon, entityId: coupon._id }; + delete readyCoupon._id; + return readyCoupon; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `failed to get coupon data when syncing Wix Marketing: ${err}`); + } +} diff --git a/app/lib/Apps/wix_members.js b/app/lib/Apps/wix_members.js index 01e95efe..b542b4b5 100644 --- a/app/lib/Apps/wix_members.js +++ b/app/lib/Apps/wix_members.js @@ -6,19 +6,29 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.onMemberCreated = onMemberCreated; exports.onMemberUpdated = onMemberUpdated; exports.onMemberDeleted = onMemberDeleted; +exports.onBadgeCreated = onBadgeCreated; +exports.onBadgeUpdated = onBadgeUpdated; +exports.onBadgeDeleted = onBadgeDeleted; const error_manager_1 = require("../Errors/error_manager"); const insert_1 = require("../Functions/insert"); const native_1 = require("../Functions/native"); const sleep_1 = require("./sleep"); const wix_data_1 = __importDefault(require("wix-data")); const weiv_data_config_1 = require("../Config/weiv_data_config"); +const logCollection = "WeivDataWixAppsSyncLogs/WixMembers"; async function onMemberCreated(event) { try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } await (0, sleep_1.sleep)(1000); const memberId = event.entity._id; - const { syncDatabase } = (0, weiv_data_config_1.getWeivDataConfigs)(); + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Members Created - ${memberId}`); + } if (!syncDatabase) { - (0, error_manager_1.kaptanLogar)("00024", "You didn't configure any database name to sync Wix apps data!"); + (0, error_manager_1.kaptanLogar)("00026"); } const { readyFullData, readyPrivateData, readyPublicData } = await getMemberData(memberId); Promise.all([ @@ -28,7 +38,7 @@ async function onMemberCreated(event) { ]); } catch (err) { - (0, insert_1.insert)("WeivDataWixAppsSyncLogs/WixMembers", { + (0, insert_1.insert)(logCollection, { message: "Member couldn't be created", entityId: event.entity._id, metadata: event.metadata @@ -39,22 +49,28 @@ async function onMemberCreated(event) { ; async function onMemberUpdated(event) { try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } await (0, sleep_1.sleep)(1000); const memberId = event.entity._id; - const { syncDatabase } = (0, weiv_data_config_1.getWeivDataConfigs)(); + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Members Updated - ${memberId}`); + } if (!syncDatabase) { - (0, error_manager_1.kaptanLogar)("00024", "You didn't configure any database name to sync Wix apps data!"); + (0, error_manager_1.kaptanLogar)("00026"); } const { readyFullData, readyPrivateData, readyPublicData } = await getMemberData(memberId); const find = { "entityId": { $eq: memberId } }; Promise.all([ - (await (0, native_1.native)(`${syncDatabase}/WixMembersPublicData`, true)).updateOne(find, readyPublicData, { retryWrites: true }), - (await (0, native_1.native)(`${syncDatabase}/WixMembersPrivateData`, true)).updateOne(find, readyPrivateData, { retryWrites: true }), - (await (0, native_1.native)(`${syncDatabase}/WixMembersFullData`, true)).updateOne(find, readyFullData, { retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixMembersPublicData`, true)).updateOne(find, { $set: readyPublicData }, { retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixMembersPrivateData`, true)).updateOne(find, { $set: readyPrivateData }, { retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixMembersFullData`, true)).updateOne(find, { $set: readyFullData }, { retryWrites: true }), ]); } catch (err) { - (0, insert_1.insert)("WeivDataWixAppsSyncLogs/WixMembers", { + (0, insert_1.insert)(logCollection, { message: "Member couldn't be updated", entityId: event.entity._id, metadata: event.metadata @@ -65,20 +81,26 @@ async function onMemberUpdated(event) { ; async function onMemberDeleted(event) { try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } const memberId = event.metadata.entityId; - const { syncDatabase } = (0, weiv_data_config_1.getWeivDataConfigs)(); + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Members Deleted - ${memberId}`); + } if (!syncDatabase) { - (0, error_manager_1.kaptanLogar)("00024", "You didn't configure any database name to sync Wix apps data!"); + (0, error_manager_1.kaptanLogar)("00026"); } const find = { "entityId": { $eq: memberId } }; Promise.all([ - (await (0, native_1.native)(`${syncDatabase}/WixMembersPublicData`, true)).deleteMany(find, { retryWrites: true }), - (await (0, native_1.native)(`${syncDatabase}/WixMembersPrivateData`, true)).deleteMany(find, { retryWrites: true }), - (await (0, native_1.native)(`${syncDatabase}/WixMembersFullData`, true)).deleteMany(find, { retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixMembersPublicData`, true)).deleteMany(find, { ordered: false, retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixMembersPrivateData`, true)).deleteMany(find, { ordered: false, retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixMembersFullData`, true)).deleteMany(find, { ordered: false, retryWrites: true }), ]); } catch (err) { - (0, insert_1.insert)("WeivDataWixAppsSyncLogs/WixMembers", { + (0, insert_1.insert)(logCollection, { message: "Member couldn't deleted", entityId: event.metadata.entityId, metadata: event.metadata @@ -87,15 +109,93 @@ async function onMemberDeleted(event) { } } ; +async function onBadgeCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const badgeId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Members Badge Created - ${badgeId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyBadgeData = await getBadgeData(badgeId); + (await (0, native_1.native)(`${syncDatabase}/WixMembersBadges`, true)).insertOne(readyBadgeData, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Member badge couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create badge when syncing: ${err}`); + } +} +async function onBadgeUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const badgeId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Members Badge Updated - ${badgeId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyBadgeData = await getBadgeData(badgeId); + const find = { "entityId": { $eq: badgeId } }; + (await (0, native_1.native)(`${syncDatabase}/WixMembersBadges`, true)).updateOne(find, { $set: readyBadgeData }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Member badge couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update badge when syncing: ${err}`); + } +} +async function onBadgeDeleted(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const badgeId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Members Badge Deleted - ${badgeId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const find = { "entityId": { $eq: badgeId } }; + (await (0, native_1.native)(`${syncDatabase}/WixMembersBadges`, true)).deleteMany(find, { ordered: false, retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Member badge couldn't deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't delete badge when syncing: ${err}`); + } +} async function getMemberData(memberId) { try { if (!memberId) { (0, error_manager_1.kaptanLogar)("00024", "Member ID is undefined when syncing WixMembers"); } const [publicData, privateData, fullData] = await Promise.all([ - wix_data_1.default.get("Members/PublicData", memberId, { suppressAuth: true, suppressHooks: true }), - wix_data_1.default.get("Members/PrivateMembersData", memberId, { suppressAuth: true, suppressHooks: true }), - wix_data_1.default.get("Members/FullData", memberId, { suppressAuth: true, suppressHooks: true }), + wix_data_1.default.get("Members/PublicData", memberId, { suppressAuth: true, suppressHooks: true, consistentRead: true }), + wix_data_1.default.get("Members/PrivateMembersData", memberId, { suppressAuth: true, suppressHooks: true, consistentRead: true }), + wix_data_1.default.get("Members/FullData", memberId, { suppressAuth: true, suppressHooks: true, consistentRead: true }), ]); const readyPublicData = { ...publicData, entityId: publicData._id }; delete readyPublicData._id; @@ -113,3 +213,17 @@ async function getMemberData(memberId) { (0, error_manager_1.kaptanLogar)("00024", `Couldn't get member data when syncing: ${err}`); } } +async function getBadgeData(badgeId) { + try { + if (!badgeId) { + (0, error_manager_1.kaptanLogar)("00024", "Badge ID is undefined when syncing Wix Members Badges"); + } + const badgeData = await wix_data_1.default.get("Members/Badges", badgeId, { suppressAuth: true, consistentRead: true }); + const readyBadgeData = { ...badgeData, entityId: badgeData._id }; + delete readyBadgeData._id; + return readyBadgeData; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `Couldn't get badge data when syncing: ${err}`); + } +} diff --git a/app/lib/Apps/wix_pricingplans.js b/app/lib/Apps/wix_pricingplans.js new file mode 100644 index 00000000..0914dde2 --- /dev/null +++ b/app/lib/Apps/wix_pricingplans.js @@ -0,0 +1,114 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.onPlanCreated = onPlanCreated; +exports.onPlanUpdated = onPlanUpdated; +exports.onPlanArchived = onPlanArchived; +const error_manager_1 = require("../Errors/error_manager"); +const insert_1 = require("../Functions/insert"); +const native_1 = require("../Functions/native"); +const sleep_1 = require("./sleep"); +const wix_data_1 = __importDefault(require("wix-data")); +const weiv_data_config_1 = require("../Config/weiv_data_config"); +const logCollection = "WeivDataWixAppsSyncLogs/WixPricingPlans"; +async function onPlanCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const planId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Pricing Plans Created - ${planId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const readyPlan = await getPlanData(planId); + (await (0, native_1.native)(`${syncDatabase}/WixPricingPlansPlans`, true)).insertOne(readyPlan, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Pricing plan couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create pricing plan when syncing Wix Pricing Plans: ${err}`); + } +} +async function onPlanUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const planId = event.entity._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Pricing Plans Updated - ${planId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: planId } }; + const readyPlan = await getPlanData(planId); + (await (0, native_1.native)(`${syncDatabase}/WixPricingPlansPlans`, true)).updateOne(filter, { $set: readyPlan }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Pricing plan couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update pricing plan when syncing Wix Pricing Plans: ${err}`); + } +} +async function onPlanArchived(event, deletePlan) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const planId = event.data.plan._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Pricing Plans Archived - ${planId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: planId } }; + if (deletePlan === true) { + (await (0, native_1.native)(`${syncDatabase}/WixPricingPlansPlans`, true)).deleteOne(filter, { retryWrites: true }); + } + else { + const readyPlan = await getPlanData(planId); + (await (0, native_1.native)(`${syncDatabase}/WixPricingPlansPlans`, true)).updateOne(filter, { $set: readyPlan }, { retryWrites: true }); + } + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Pricing plan couldn't be archived/deleted", + entityId: event.data.plan._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't archive/deleted pricing plan when syncing Wix Pricing Plans: ${err}`); + } +} +async function getPlanData(planId) { + try { + if (!planId) { + (0, error_manager_1.kaptanLogar)("00024", `planId is undefined or invalid but it's required to get the plan data when syncing Wix Pricing Plans`); + } + const plan = await wix_data_1.default.get("PaidPlans/Plans", planId, { supressAuth: true, consistentRead: true }); + const readyPlans = { ...plan, entityId: plan._id }; + delete readyPlans._id; + return readyPlans; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `failed to get plan data when syncing Wix Pricing Plans: ${err}`); + } +} diff --git a/app/lib/Apps/wix_stores.js b/app/lib/Apps/wix_stores.js new file mode 100644 index 00000000..9c5dedbe --- /dev/null +++ b/app/lib/Apps/wix_stores.js @@ -0,0 +1,267 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.onProductCreated = onProductCreated; +exports.onProductUpdated = onProductUpdated; +exports.onProductDeleted = onProductDeleted; +exports.onCollectionCreated = onCollectionCreated; +exports.onCollectionUpdated = onCollectionUpdated; +exports.onCollectionDeleted = onCollectionDeleted; +const error_manager_1 = require("../Errors/error_manager"); +const insert_1 = require("../Functions/insert"); +const native_1 = require("../Functions/native"); +const sleep_1 = require("./sleep"); +const wix_data_1 = __importDefault(require("wix-data")); +const weiv_data_config_1 = require("../Config/weiv_data_config"); +const logCollection = "WeivDataWixAppsSyncLogs/WixStores"; +async function onProductCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const productId = event._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Stores Product Created - ${productId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const { readyInventoryData, readyProductData, readyVariantsData } = await getProductData(productId); + const variantsWrites = readyVariantsData.map((variant) => { return { insertOne: { document: variant } }; }); + const inventoryItemsWrites = readyInventoryData.map((inventoryItem) => { return { insertOne: { document: inventoryItem } }; }); + Promise.all([ + (await (0, native_1.native)(`${syncDatabase}/WixStoresProducts`, true)).insertOne(readyProductData, { retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixStoresVariants`, true)).bulkWrite(variantsWrites, { ordered: false, retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixStoresInventoryItems`, true)).bulkWrite(inventoryItemsWrites, { ordered: false, retryWrites: true }) + ]); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Stores product couldn't be created", + entityId: event._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create product when syncing Wix Stores: ${err}`); + } +} +async function onProductUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const productId = event.productId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Stores Product Updated - ${productId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const { readyInventoryData, readyProductData, readyVariantsData } = await getProductData(productId); + const filter = { "entityId": { $eq: productId } }; + const variantsWrites = readyVariantsData.map((variant) => { + return { + updateOne: { + filter: { "entityId": { $eq: variant.entityId } }, + update: { $set: variant } + } + }; + }); + const inventoryItemsWrites = readyInventoryData.map((inventoryItem) => { + return { + updateOne: { + filter: { "entityId": { $eq: inventoryItem.entityId } }, + update: { $set: inventoryItem } + } + }; + }); + Promise.all([ + (await (0, native_1.native)(`${syncDatabase}/WixStoresProducts`, true)).updateOne(filter, { $set: readyProductData }, { retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixStoresVariants`, true)).bulkWrite(variantsWrites, { ordered: false, retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixStoresInventoryItems`, true)).bulkWrite(inventoryItemsWrites, { ordered: false, retryWrites: true }) + ]); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Stores product couldn't be updated", + entityId: event.productId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update product when syncing Wix Stores: ${err}`); + } +} +async function onProductDeleted(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const productId = event.productId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Stores Product Deleted - ${productId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "productId": { $eq: productId } }; + Promise.all([ + (await (0, native_1.native)(`${syncDatabase}/WixStoresProducts`, true)).deleteMany({ "entityId": { $eq: productId } }, { ordered: false, retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixStoresVariants`, true)).deleteMany(filter, { ordered: false, retryWrites: true }), + (await (0, native_1.native)(`${syncDatabase}/WixStoresInventoryItems`, true)).deleteMany(filter, { ordered: false, retryWrites: true }) + ]); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Stores product couldn't deleted", + entityId: event.productId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't delete product when syncing Wix Stores: ${err}`); + } +} +async function onCollectionCreated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(4000); + const collectionId = event._id; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Stores Collection Created - ${collectionId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const collection = await getCollectionData(collectionId); + (await (0, native_1.native)(`${syncDatabase}/WixStoresCollections`)).insertOne(collection, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Stores collection couldn't be created", + entityId: event._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't create collection when syncing Wix Stores: ${err}`); + } +} +async function onCollectionUpdated(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + await (0, sleep_1.sleep)(1000); + const collectionId = event.collectionId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Stores Collection Updated - ${collectionId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const collection = await getCollectionData(collectionId); + const filter = { "entityId": { $eq: collection.entityId } }; + (await (0, native_1.native)(`${syncDatabase}/WixStoresCollections`)).updateOne(filter, { $set: collection }, { retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Stores collection couldn't be updated", + entityId: event.collectionId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't update collection when syncing Wix Stores: ${err}`); + } +} +async function onCollectionDeleted(event) { + try { + if (!event) { + (0, error_manager_1.kaptanLogar)("00025"); + } + const collectionId = event.collectionId; + const { syncDatabase, enableSyncLogs } = (0, weiv_data_config_1.getWeivDataConfigs)(); + if (enableSyncLogs) { + console.info(`Wix Stores Collection Deleted - ${collectionId}`); + } + if (!syncDatabase) { + (0, error_manager_1.kaptanLogar)("00026"); + } + const filter = { "entityId": { $eq: collectionId } }; + (await (0, native_1.native)(`${syncDatabase}/WixStoresCollections`)).deleteMany(filter, { ordered: false, retryWrites: true }); + } + catch (err) { + (0, insert_1.insert)(logCollection, { + message: "Stores collection couldn't deleted", + entityId: event.collectionId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + (0, error_manager_1.kaptanLogar)("00024", `Couldn't delete collection when syncing Wix Stores: ${err}`); + } +} +async function getProductData(productId) { + try { + if (!productId) { + (0, error_manager_1.kaptanLogar)("00024", "ProductId is undefined when syncing Wix Stores"); + } + const options = { + suppressAuth: true, + consistentRead: true, + appOptions: { includeVariants: true, includeHiddenProducts: true }, + omitTotalCount: false + }; + const productData = await wix_data_1.default.query("Stores/Products").eq("_id", productId).include("collections").find(options); + if (productData.items.length === 0) { + (0, error_manager_1.kaptanLogar)("00024", `Product not found when syncing Wix Stores products: ${productId}`); + } + const product = { ...productData.items[0], collections: productData.items[0].collections.map((c) => c._id), entityId: productData.items[0]._id }; + delete product._id; + const variantsResults = await wix_data_1.default.query("Stores/Variants").eq("productId", productId).limit(100).find(options); + let variants = variantsResults.items; + while (variantsResults.hasNext()) { + const nextPage = await variantsResults.next(); + variants = variants.concat(nextPage.items); + } + variants = variants.map((variant) => { + const vr = { ...variant, entityId: variant._id }; + delete vr._id; + return vr; + }); + const inventoryItemsResult = await wix_data_1.default.query("Stores/InventoryItems").eq("productId", productId).limit(100).find(options); + let inventoryItems = inventoryItemsResult.items; + while (inventoryItemsResult.hasNext()) { + const nextPage = await inventoryItemsResult.next(); + inventoryItems = inventoryItems.concat(nextPage.items); + } + inventoryItems = inventoryItems.map((item) => { + const it = { ...item, entityId: item._id }; + delete it._id; + return it; + }); + return { + readyInventoryData: inventoryItems, + readyProductData: product, + readyVariantsData: variants + }; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `Couldn't get product data when syncing Wix Stores: ${err}`); + } +} +async function getCollectionData(collectionId) { + try { + if (!collectionId) { + (0, error_manager_1.kaptanLogar)("00024", "CollectionId is undefined when syncing Wix Stores"); + } + const collection = await wix_data_1.default.get("Stores/Collections", collectionId, { suppressAuth: true, consistentRead: true }); + const readyCollectionData = { ...collection, entityId: collection._id }; + delete readyCollectionData._id; + return readyCollectionData; + } + catch (err) { + (0, error_manager_1.kaptanLogar)("00024", `Couldn't get collection data when syncing Wix Stores: ${err}`); + } +} diff --git a/app/lib/Errors/errors.js b/app/lib/Errors/errors.js index 4d1e85aa..058d5203 100644 --- a/app/lib/Errors/errors.js +++ b/app/lib/Errors/errors.js @@ -26,6 +26,8 @@ exports.errorsList = { "00021": "Error on WeivData config object, make sure you pass correct config object", "00022": "Collection Manager Error", "00023": "Aggegration Error", - "00024": "Wix Application Sync Error" + "00024": "Wix Application Sync Error", + "00025": "Wix Application Sync Error - Event data not found, don't forget to pass the event object from the Wix event function", + "00026": "You didn't configure any database name to sync Wix apps data!" }; exports.default = exports.errorsList; diff --git a/app/lib/index.js b/app/lib/index.js index 0864b7ed..2fef8881 100644 --- a/app/lib/index.js +++ b/app/lib/index.js @@ -80,8 +80,10 @@ exports.filter = filter; const _version = () => package_json_1.default.version; exports._version = _version; const wix_members_1 = require("./Apps/wix_members"); +const wix_stores_1 = require("./Apps/wix_stores"); const SyncWixApps = { - wixMembers: { onMemberCreated: wix_members_1.onMemberCreated, onMemberUpdated: wix_members_1.onMemberUpdated, onMemberDeleted: wix_members_1.onMemberDeleted } + wixMembers: { onMemberCreated: wix_members_1.onMemberCreated, onMemberUpdated: wix_members_1.onMemberUpdated, onMemberDeleted: wix_members_1.onMemberDeleted, onBadgeCreated: wix_members_1.onBadgeCreated, onBadgeUpdated: wix_members_1.onBadgeUpdated, onBadgeDeleted: wix_members_1.onBadgeDeleted }, + wixStores: { onCollectionCreated: wix_stores_1.onCollectionCreated, onCollectionUpdated: wix_stores_1.onCollectionUpdated, onCollectionDeleted: wix_stores_1.onCollectionDeleted, onProductCreated: wix_stores_1.onProductCreated, onProductUpdated: wix_stores_1.onProductUpdated, onProductDeleted: wix_stores_1.onProductDeleted } }; exports.SyncWixApps = SyncWixApps; exports.default = { diff --git a/app/package-lock.json b/app/package-lock.json index 0069609e..89b7f623 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,12 +1,12 @@ { "name": "@exweiv/weiv-data", - "version": "4.5.1", + "version": "4.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@exweiv/weiv-data", - "version": "4.5.1", + "version": "4.7.0", "license": "Apache-2.0", "dependencies": { "crypto-js": "^4.2.0", diff --git a/app/package.json b/app/package.json index cdc77cf1..04dff28e 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "@exweiv/weiv-data", - "version": "4.5.1", + "version": "4.7.0", "description": "Custom API Library for Wix sites to connect MongoDB. Designed to easily switch from wix-data APIs.", "main": "./lib/index.js", "files": [ diff --git a/app/src/Apps/wix_blog.ts b/app/src/Apps/wix_blog.ts new file mode 100644 index 00000000..e213784c --- /dev/null +++ b/app/src/Apps/wix_blog.ts @@ -0,0 +1,321 @@ +import type { Document } from "mongodb"; +import { kaptanLogar } from "../Errors/error_manager"; +import { insert } from "../Functions/insert"; +import { native } from "../Functions/native"; +import { sleep } from "./sleep"; + +//@ts-ignore +import wixData from 'wix-data'; +import { getWeivDataConfigs } from "../Config/weiv_data_config"; + +const logCollection = "WeivDataWixAppsSyncLogs/WixBlog"; + +// BLOG POSTS +export async function onPostCreated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const postId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Post Created - ${postId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const readyBlogPost = await getBlogPostData(postId); + (await native(`${syncDatabase}/WixBlogPosts`, true)).insertOne(readyBlogPost, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog post couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create blog post when syncing Wix Blog: ${err}`); + } +} + +export async function onPostUpdated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const postId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Post Updated - ${postId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: postId } }; + const readyBlogPost = await getBlogPostData(postId); + (await native(`${syncDatabase}/WixBlogPosts`, true)).updateOne(filter, { $set: readyBlogPost }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog post couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update blog post when syncing Wix Blog: ${err}`); + } +} + +export async function onPostDeleted(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + + // Get required information + const postId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Post Deleted - ${postId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: postId } }; + (await native(`${syncDatabase}/WixBlogPosts`, true)).deleteMany(filter, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog post couldn't be deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't delete blog post when syncing Wix Blog: ${err}`); + } +} + +// BLOG CATEGORIES +export async function onCategoryCreated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const categoryId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Category Created - ${categoryId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const readyCategoryData = await getBlogCategoryData(categoryId); + (await native(`${syncDatabase}/WixBlogCategories`, true)).insertOne(readyCategoryData, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog category couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create blog category when syncing Wix Blog: ${err}`); + } +} + +export async function onCategoryUpdated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const categoryId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Category Updated - ${categoryId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: categoryId } }; + const readyCategoryData = await getBlogCategoryData(categoryId); + (await native(`${syncDatabase}/WixBlogCategories`, true)).updateOne(filter, { $set: readyCategoryData }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog category couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update blog category when syncing Wix Blog: ${err}`); + } +} + +export async function onCategoryDeleted(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + + // Get required information + const categoryId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Category Deleted - ${categoryId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: categoryId } }; + (await native(`${syncDatabase}/WixBlogCategories`, true)).deleteMany(filter, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog category couldn't be deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't delete blog category when syncing Wix Blog: ${err}`); + } +} + +// BLOG TAGS +export async function onTagCreated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const tagId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Tag Created - ${tagId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const readyTagData = await getBlogTagData(tagId); + (await native(`${syncDatabase}/WixBlogTags`, true)).insertOne(readyTagData, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog tag couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create blog tag when syncing Wix Blog: ${err}`); + } +} + +export async function onTagUpdated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const tagId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Tag Updated - ${tagId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: tagId } }; + const readyTagData = await getBlogTagData(tagId); + (await native(`${syncDatabase}/WixBlogTags`, true)).updateOne(filter, { $set: readyTagData }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog tag couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update blog tag when syncing Wix Blog: ${err}`); + } +} + +export async function onTagDeleted(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + + // Get required information + const tagId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Blog Tag Deleted - ${tagId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: tagId } }; + (await native(`${syncDatabase}/WixBlogTags`, true)).deleteMany(filter, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Blog tag couldn't be deeleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't delete blog tag when syncing Wix Blog: ${err}`); + } +} + +// HELPER FUNCTIONS +async function getBlogPostData(postId: string): Promise { + try { + if (!postId) { + kaptanLogar("00024", `postId is required but it's undefined or invalid when syncing Wix Blog`); + } + + const blogPost = await wixData.get("Blog/Posts", postId, { suppressAuth: true, consistentRead: true }); + const readyBlogPost = { ...blogPost, entityId: blogPost._id }; + delete readyBlogPost._id; + return readyBlogPost; + } catch (err) { + kaptanLogar("00024", `failed to get blog post data when syncing Wix Blog: ${err}`); + } +} + +async function getBlogCategoryData(categoryId: string): Promise { + try { + if (!categoryId) { + kaptanLogar("00024", `categoryId is required but it's undefined or invalid when syncing Wix Blog`); + } + + const blogCategory = await wixData.get("Blog/Categories", categoryId, { suppressAuth: true, consistentRead: true }); + const readyBlogCategory = { ...blogCategory, entityId: blogCategory._id }; + delete readyBlogCategory._id; + return readyBlogCategory; + } catch (err) { + kaptanLogar("00024", `failed to get blog category data when syncing Wix Blog: ${err}`); + } +} + +async function getBlogTagData(tagId: string): Promise { + try { + if (!tagId) { + kaptanLogar("00024", `tagId is required but it's undefined or invalid when syncing Wix Blog`); + } + + const blogTag = await wixData.get("Blog/Tags", tagId, { suppressAuth: true, consistentRead: true }); + const readyBlogTag = { ...blogTag, entityId: blogTag._id }; + delete readyBlogTag._id; + return readyBlogTag; + } catch (err) { + kaptanLogar("00024", `failed to get blog tag data when syncing Wix Blog: ${err}`); + } +} \ No newline at end of file diff --git a/app/src/Apps/wix_ecom.ts b/app/src/Apps/wix_ecom.ts new file mode 100644 index 00000000..2e981f8d --- /dev/null +++ b/app/src/Apps/wix_ecom.ts @@ -0,0 +1,169 @@ +import type { Document } from "mongodb"; +import { kaptanLogar } from "../Errors/error_manager"; +import { insert } from "../Functions/insert"; +import { native } from "../Functions/native"; +import { sleep } from "./sleep"; + +//@ts-ignore +import { orders } from 'wix-ecom-backend'; //@ts-ignore +import { elevate } from "wix-auth"; //@ts-ignore +import wixData from 'wix-data'; +import { getWeivDataConfigs } from "../Config/weiv_data_config"; + +const logCollection = "WeivDataWixAppsSyncLogs/WixeCom"; + +export async function onOrderCreated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const orderId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix eCom Order Created - ${orderId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const readyOrder = await getOrderData(orderId); + (await native(`${syncDatabase}/WixeComOrders`, true)).insertOne(readyOrder, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "eCommerce order couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create order when syncing Wix eCom: ${err}`); + } +} + +export async function onOrderUpdated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const orderId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix eCom Order Updated - ${orderId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const readyOrder = await getOrderData(orderId); + const filter = { "entityId": { $eq: orderId } }; + (await native(`${syncDatabase}/WixeComOrders`, true)).updateOne(filter, { $set: readyOrder }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "eCommerce order couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update order when syncing Wix eCom: ${err}`); + } +} + +// ABANDONED CHECKOUTS +export async function onAbandonedCheckoutCreated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + // Get required information + const abandonedCheckoutId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix eCom Abandoned Checkout Created - ${abandonedCheckoutId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const readyAbandonedCheckout = { ...event.entity, entityId: abandonedCheckoutId }; + delete readyAbandonedCheckout._id; + + (await native(`${syncDatabase}/WixeComAbandonedCheckouts`, true)).insertOne(readyAbandonedCheckout, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "eCommerce abandoned checkout couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create abandoned checkout when syncing Wix eCom: ${err}`); + } +} + +export async function onAbandonedCheckoutRecovered(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + // Get required information + const abandonedCheckoutId = event.data.abandonedCheckout._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix eCom Abandoned Checkout Updated/Recovered - ${abandonedCheckoutId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const filter = { "entityId": { $eq: abandonedCheckoutId } }; + const readyAbandonedCheckout = { ...event.data.abandonedCheckout, entityId: abandonedCheckoutId }; + delete readyAbandonedCheckout._id; + + (await native(`${syncDatabase}/WixeComAbandonedCheckouts`, true)).updateOne(filter, { $set: readyAbandonedCheckout }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "eCommerce abandoned checkout couldn't be updated", + entityId: event.data.abandonedCheckout._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update abandoned checkout when syncing Wix eCom: ${err}`); + } +} + +// HELPER FUNCTIONS +export async function getOrderData(orderId: string): Promise { + try { + if (!orderId) { + kaptanLogar("00024", "orderId is required but it's undefined"); + } + + const elevatedGetOrder = elevate(orders.getOrder); + const order = await elevatedGetOrder(orderId); + const readyOrder = { ...order, entityId: order._id }; + delete readyOrder._id; + return readyOrder; + } catch (err) { + kaptanLogar("00024", `failed to get the order data when syncing Wix eCom: ${err}`); + } +} \ No newline at end of file diff --git a/app/src/Apps/wix_marketing.ts b/app/src/Apps/wix_marketing.ts new file mode 100644 index 00000000..78871c63 --- /dev/null +++ b/app/src/Apps/wix_marketing.ts @@ -0,0 +1,128 @@ +import type { Document } from "mongodb"; +import { kaptanLogar } from "../Errors/error_manager"; +import { insert } from "../Functions/insert"; +import { native } from "../Functions/native"; +import { sleep } from "./sleep"; + +//@ts-ignore +import wixData from 'wix-data'; +import { getWeivDataConfigs } from "../Config/weiv_data_config"; + +const logCollection = "WeivDataWixAppsSyncLogs/WixMarketing"; + +export async function onCouponCreated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const couponId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Marketing Coupon Created - ${couponId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const readyCoupon = await getCouponData(couponId); + (await native(`${syncDatabase}/WixMarketingCoupons`, true)).insertOne(readyCoupon, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Marketing coupon couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create coupon when syncing Wix Marketing: ${err}`); + } +} + +export async function onCouponUpdated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const couponId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Marketing Coupon Updated - ${couponId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const filter = { "entityId": { $eq: couponId } }; + const readyCoupon = await getCouponData(couponId); + (await native(`${syncDatabase}/WixMarketingCoupons`, true)).updateOne(filter, { $set: readyCoupon }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Marketing coupon couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update coupon when syncing Wix Marketing: ${err}`); + } +} + +export async function onCouponDeleted(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + // Get required information + const couponId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Marketing Coupon Deleted - ${couponId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const filter = { "entityId": { $eq: couponId } }; + (await native(`${syncDatabase}/WixMarketingCoupons`, true)).deleteMany(filter, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Marketing coupon couldn't be deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't delete coupon when syncing Wix Marketing: ${err}`); + } +} + +// HELPER FUNCTIONS +async function getCouponData(couponId: string): Promise { + try { + if (!couponId) { + kaptanLogar("00024", "couponId is undefined or invalid when syncing Wix Marketing"); + } + + const coupon = await wixData.get("Marketing/Coupons", couponId, { supressAuth: true, consistentRead: true }); + const readyCoupon = { ...coupon, entityId: coupon._id }; + delete readyCoupon._id; + return readyCoupon; + } catch (err) { + kaptanLogar("00024", `failed to get coupon data when syncing Wix Marketing: ${err}`); + } +} \ No newline at end of file diff --git a/app/src/Apps/wix_members.ts b/app/src/Apps/wix_members.ts index 78a46f90..377483f5 100644 --- a/app/src/Apps/wix_members.ts +++ b/app/src/Apps/wix_members.ts @@ -1,4 +1,3 @@ -// Sync eCom Orders to MongoDB with CUD Operations import type { Document } from "mongodb"; import { kaptanLogar } from "../Errors/error_manager"; import { insert } from "../Functions/insert"; @@ -9,17 +8,27 @@ import { sleep } from "./sleep"; import wixData from 'wix-data'; import { getWeivDataConfigs } from "../Config/weiv_data_config"; +const logCollection = "WeivDataWixAppsSyncLogs/WixMembers"; + export async function onMemberCreated(event: Document): Promise { try { + if (!event) { + kaptanLogar("00025"); + } + // Wait 1s before inserting (in case of data not inserted in Wix app collection yet) await sleep(1000); // Get required information const memberId = event.entity._id; - const { syncDatabase } = getWeivDataConfigs(); + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Members Created - ${memberId}`); + } if (!syncDatabase) { - kaptanLogar("00024", "You didn't configure any database name to sync Wix apps data!"); + kaptanLogar("00026"); } const { readyFullData, readyPrivateData, readyPublicData } = await getMemberData(memberId); @@ -32,7 +41,7 @@ export async function onMemberCreated(event: Document): Promise { ]); } catch (err) { // Log Error (fire and forget) - insert("WeivDataWixAppsSyncLogs/WixMembers", { + insert(logCollection, { message: "Member couldn't be created", entityId: event.entity._id, metadata: event.metadata @@ -45,15 +54,23 @@ export async function onMemberCreated(event: Document): Promise { export async function onMemberUpdated(event: Document): Promise { try { + if (!event) { + kaptanLogar("00025"); + } + // Wait 1s before update (in case of data not updated in Wix app collection yet) await sleep(1000); // Get required information const memberId = event.entity._id; - const { syncDatabase } = getWeivDataConfigs(); + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Members Updated - ${memberId}`); + } if (!syncDatabase) { - kaptanLogar("00024", "You didn't configure any database name to sync Wix apps data!"); + kaptanLogar("00026"); } const { readyFullData, readyPrivateData, readyPublicData } = await getMemberData(memberId); @@ -61,13 +78,13 @@ export async function onMemberUpdated(event: Document): Promise { // Insert to MongoDB (fire and forget) Promise.all([ - (await native(`${syncDatabase}/WixMembersPublicData`, true)).updateOne(find, readyPublicData, { retryWrites: true }), - (await native(`${syncDatabase}/WixMembersPrivateData`, true)).updateOne(find, readyPrivateData, { retryWrites: true }), - (await native(`${syncDatabase}/WixMembersFullData`, true)).updateOne(find, readyFullData, { retryWrites: true }), + (await native(`${syncDatabase}/WixMembersPublicData`, true)).updateOne(find, { $set: readyPublicData }, { retryWrites: true }), + (await native(`${syncDatabase}/WixMembersPrivateData`, true)).updateOne(find, { $set: readyPrivateData }, { retryWrites: true }), + (await native(`${syncDatabase}/WixMembersFullData`, true)).updateOne(find, { $set: readyFullData }, { retryWrites: true }), ]); } catch (err) { // Log Error (fire and forget) - insert("WeivDataWixAppsSyncLogs/WixMembers", { + insert(logCollection, { message: "Member couldn't be updated", entityId: event.entity._id, metadata: event.metadata @@ -80,25 +97,33 @@ export async function onMemberUpdated(event: Document): Promise { export async function onMemberDeleted(event: Document): Promise { try { + if (!event) { + kaptanLogar("00025"); + } + // Get required information const memberId = event.metadata.entityId; - const { syncDatabase } = getWeivDataConfigs(); + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Members Deleted - ${memberId}`); + } if (!syncDatabase) { - kaptanLogar("00024", "You didn't configure any database name to sync Wix apps data!"); + kaptanLogar("00026"); } const find = { "entityId": { $eq: memberId } }; // Insert to MongoDB (fire and forget) Promise.all([ - (await native(`${syncDatabase}/WixMembersPublicData`, true)).deleteMany(find, { retryWrites: true }), - (await native(`${syncDatabase}/WixMembersPrivateData`, true)).deleteMany(find, { retryWrites: true }), - (await native(`${syncDatabase}/WixMembersFullData`, true)).deleteMany(find, { retryWrites: true }), + (await native(`${syncDatabase}/WixMembersPublicData`, true)).deleteMany(find, { ordered: false, retryWrites: true }), + (await native(`${syncDatabase}/WixMembersPrivateData`, true)).deleteMany(find, { ordered: false, retryWrites: true }), + (await native(`${syncDatabase}/WixMembersFullData`, true)).deleteMany(find, { ordered: false, retryWrites: true }), ]); } catch (err) { // Log Error (fire and forget) - insert("WeivDataWixAppsSyncLogs/WixMembers", { + insert(logCollection, { message: "Member couldn't deleted", entityId: event.metadata.entityId, metadata: event.metadata @@ -109,6 +134,108 @@ export async function onMemberDeleted(event: Document): Promise { } }; +// BADGES +export async function onBadgeCreated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const badgeId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Members Badge Created - ${badgeId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const readyBadgeData = await getBadgeData(badgeId); + (await native(`${syncDatabase}/WixMembersBadges`, true)).insertOne(readyBadgeData, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Member badge couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create badge when syncing: ${err}`); + } +} + +export async function onBadgeUpdated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const badgeId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Members Badge Updated - ${badgeId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const readyBadgeData = await getBadgeData(badgeId); + const find = { "entityId": { $eq: badgeId } }; + (await native(`${syncDatabase}/WixMembersBadges`, true)).updateOne(find, { $set: readyBadgeData }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Member badge couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update badge when syncing: ${err}`); + } +} + +export async function onBadgeDeleted(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + // Get required information + const badgeId = event.metadata.entityId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Members Badge Deleted - ${badgeId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const find = { "entityId": { $eq: badgeId } }; + (await native(`${syncDatabase}/WixMembersBadges`, true)).deleteMany(find, { ordered: false, retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Member badge couldn't deleted", + entityId: event.metadata.entityId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't delete badge when syncing: ${err}`); + } +} + // HELPER FUNCTIONS type SyncMemberData = { readyPublicData: Document, @@ -123,9 +250,9 @@ async function getMemberData(memberId: string): Promise { } const [publicData, privateData, fullData] = await Promise.all([ - wixData.get("Members/PublicData", memberId, { suppressAuth: true, suppressHooks: true }), - wixData.get("Members/PrivateMembersData", memberId, { suppressAuth: true, suppressHooks: true }), - wixData.get("Members/FullData", memberId, { suppressAuth: true, suppressHooks: true }), + wixData.get("Members/PublicData", memberId, { suppressAuth: true, suppressHooks: true, consistentRead: true }), + wixData.get("Members/PrivateMembersData", memberId, { suppressAuth: true, suppressHooks: true, consistentRead: true }), + wixData.get("Members/FullData", memberId, { suppressAuth: true, suppressHooks: true, consistentRead: true }), ]); const readyPublicData = { ...publicData, entityId: publicData._id }; @@ -145,4 +272,20 @@ async function getMemberData(memberId: string): Promise { } catch (err) { kaptanLogar("00024", `Couldn't get member data when syncing: ${err}`) } +} + +async function getBadgeData(badgeId: string): Promise { + try { + if (!badgeId) { + kaptanLogar("00024", "Badge ID is undefined when syncing Wix Members Badges"); + } + + const badgeData = await wixData.get("Members/Badges", badgeId, { suppressAuth: true, consistentRead: true }); + const readyBadgeData = { ...badgeData, entityId: badgeData._id }; + delete readyBadgeData._id; + + return readyBadgeData; + } catch (err) { + kaptanLogar("00024", `Couldn't get badge data when syncing: ${err}`) + } } \ No newline at end of file diff --git a/app/src/Apps/wix_pricingplans.ts b/app/src/Apps/wix_pricingplans.ts new file mode 100644 index 00000000..3e461f49 --- /dev/null +++ b/app/src/Apps/wix_pricingplans.ts @@ -0,0 +1,121 @@ +import type { Document } from "mongodb"; +import { kaptanLogar } from "../Errors/error_manager"; +import { insert } from "../Functions/insert"; +import { native } from "../Functions/native"; +import { sleep } from "./sleep"; + +//@ts-ignore +import wixData from 'wix-data'; +import { getWeivDataConfigs } from "../Config/weiv_data_config"; + +const logCollection = "WeivDataWixAppsSyncLogs/WixPricingPlans"; + +export async function onPlanCreated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const planId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Pricing Plans Created - ${planId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const readyPlan = await getPlanData(planId); + (await native(`${syncDatabase}/WixPricingPlansPlans`, true)).insertOne(readyPlan, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Pricing plan couldn't be created", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create pricing plan when syncing Wix Pricing Plans: ${err}`); + } +} + +export async function onPlanUpdated(event: Document): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const planId = event.entity._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Pricing Plans Updated - ${planId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: planId } }; + const readyPlan = await getPlanData(planId); + (await native(`${syncDatabase}/WixPricingPlansPlans`, true)).updateOne(filter, { $set: readyPlan }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Pricing plan couldn't be updated", + entityId: event.entity._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update pricing plan when syncing Wix Pricing Plans: ${err}`); + } +} + +export async function onPlanArchived(event: Document, deletePlan?: boolean): Promise { + try { + if (!event) { kaptanLogar("00025"); } + await sleep(1000); + + // Get required information + const planId = event.data.plan._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Pricing Plans Archived - ${planId}`); + } + + if (!syncDatabase) { kaptanLogar("00026"); } + + const filter = { "entityId": { $eq: planId } }; + + if (deletePlan === true) { + (await native(`${syncDatabase}/WixPricingPlansPlans`, true)).deleteOne(filter, { retryWrites: true }); + } else { + const readyPlan = await getPlanData(planId); + (await native(`${syncDatabase}/WixPricingPlansPlans`, true)).updateOne(filter, { $set: readyPlan }, { retryWrites: true }); + } + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Pricing plan couldn't be archived/deleted", + entityId: event.data.plan._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't archive/deleted pricing plan when syncing Wix Pricing Plans: ${err}`); + } +} + +// HELPER FUNCTIONS +async function getPlanData(planId: string): Promise { + try { + if (!planId) { + kaptanLogar("00024", `planId is undefined or invalid but it's required to get the plan data when syncing Wix Pricing Plans`); + } + + const plan = await wixData.get("PaidPlans/Plans", planId, { supressAuth: true, consistentRead: true }); + const readyPlans = { ...plan, entityId: plan._id }; + delete readyPlans._id; + return readyPlans; + } catch (err) { + kaptanLogar("00024", `failed to get plan data when syncing Wix Pricing Plans: ${err}`); + } +} \ No newline at end of file diff --git a/app/src/Apps/wix_stores.ts b/app/src/Apps/wix_stores.ts new file mode 100644 index 00000000..48b82a29 --- /dev/null +++ b/app/src/Apps/wix_stores.ts @@ -0,0 +1,333 @@ +import type { Document, AnyBulkWriteOperation } from "mongodb"; +import { kaptanLogar } from "../Errors/error_manager"; +import { insert } from "../Functions/insert"; +import { native } from "../Functions/native"; +import { sleep } from "./sleep"; + +//@ts-ignore +import wixData from 'wix-data'; +import { getWeivDataConfigs } from "../Config/weiv_data_config"; + +const logCollection = "WeivDataWixAppsSyncLogs/WixStores"; + +// PRODUCTS, VARIANTS, INVENTORY ITEMS +export async function onProductCreated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const productId = event._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Stores Product Created - ${productId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const { readyInventoryData, readyProductData, readyVariantsData } = await getProductData(productId); + + const variantsWrites = readyVariantsData.map((variant: Document): AnyBulkWriteOperation => { return { insertOne: { document: variant } } }); + const inventoryItemsWrites = readyInventoryData.map((inventoryItem: Document): AnyBulkWriteOperation => { return { insertOne: { document: inventoryItem } } }); + + Promise.all([ + (await native(`${syncDatabase}/WixStoresProducts`, true)).insertOne(readyProductData, { retryWrites: true }), + (await native(`${syncDatabase}/WixStoresVariants`, true)).bulkWrite(variantsWrites, { ordered: false, retryWrites: true }), + (await native(`${syncDatabase}/WixStoresInventoryItems`, true)).bulkWrite(inventoryItemsWrites, { ordered: false, retryWrites: true }) + ]); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Stores product couldn't be created", + entityId: event._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create product when syncing Wix Stores: ${err}`); + } +} + +export async function onProductUpdated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const productId = event.productId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Stores Product Updated - ${productId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const { readyInventoryData, readyProductData, readyVariantsData } = await getProductData(productId); + const filter = { "entityId": { $eq: productId } }; + + const variantsWrites = readyVariantsData.map((variant: Document): AnyBulkWriteOperation => { + return { + updateOne: { + filter: { "entityId": { $eq: variant.entityId } }, + update: { $set: variant } + } + } + }); + + const inventoryItemsWrites = readyInventoryData.map((inventoryItem: Document): AnyBulkWriteOperation => { + return { + updateOne: { + filter: { "entityId": { $eq: inventoryItem.entityId } }, + update: { $set: inventoryItem } + } + } + }); + + Promise.all([ + (await native(`${syncDatabase}/WixStoresProducts`, true)).updateOne(filter, { $set: readyProductData }, { retryWrites: true }), + (await native(`${syncDatabase}/WixStoresVariants`, true)).bulkWrite(variantsWrites, { ordered: false, retryWrites: true }), + (await native(`${syncDatabase}/WixStoresInventoryItems`, true)).bulkWrite(inventoryItemsWrites, { ordered: false, retryWrites: true }) + ]); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Stores product couldn't be updated", + entityId: event.productId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update product when syncing Wix Stores: ${err}`); + } +} + +export async function onProductDeleted(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + // Get required information + const productId = event.productId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Stores Product Deleted - ${productId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const filter = { "productId": { $eq: productId } }; + + Promise.all([ + (await native(`${syncDatabase}/WixStoresProducts`, true)).deleteMany({ "entityId": { $eq: productId } }, { ordered: false, retryWrites: true }), + (await native(`${syncDatabase}/WixStoresVariants`, true)).deleteMany(filter, { ordered: false, retryWrites: true }), + (await native(`${syncDatabase}/WixStoresInventoryItems`, true)).deleteMany(filter, { ordered: false, retryWrites: true }) + ]); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Stores product couldn't deleted", + entityId: event.productId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't delete product when syncing Wix Stores: ${err}`); + } +} + +// COLLECTIONS +export async function onCollectionCreated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + // Wix Stores Collections are Buggy + await sleep(4000); + + // Get required information + const collectionId = event._id; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Stores Collection Created - ${collectionId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const collection = await getCollectionData(collectionId); + (await native(`${syncDatabase}/WixStoresCollections`)).insertOne(collection, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Stores collection couldn't be created", + entityId: event._id, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't create collection when syncing Wix Stores: ${err}`); + } +} + +export async function onCollectionUpdated(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + await sleep(1000); + + // Get required information + const collectionId = event.collectionId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Stores Collection Updated - ${collectionId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const collection = await getCollectionData(collectionId); + const filter = { "entityId": { $eq: collection.entityId } }; + (await native(`${syncDatabase}/WixStoresCollections`)).updateOne(filter, { $set: collection }, { retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Stores collection couldn't be updated", + entityId: event.collectionId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't update collection when syncing Wix Stores: ${err}`); + } +} + +export async function onCollectionDeleted(event: Document): Promise { + try { + if (!event) { + kaptanLogar("00025"); + } + + // Get required information + const collectionId = event.collectionId; + const { syncDatabase, enableSyncLogs } = getWeivDataConfigs(); + + if (enableSyncLogs) { + console.info(`Wix Stores Collection Deleted - ${collectionId}`); + } + + if (!syncDatabase) { + kaptanLogar("00026"); + } + + const filter = { "entityId": { $eq: collectionId } }; + (await native(`${syncDatabase}/WixStoresCollections`)).deleteMany(filter, { ordered: false, retryWrites: true }); + } catch (err) { + // Log Error (fire and forget) + insert(logCollection, { + message: "Stores collection couldn't deleted", + entityId: event.collectionId, + metadata: event.metadata + }, { suppressAuth: true, suppressHooks: true }); + + kaptanLogar("00024", `Couldn't delete collection when syncing Wix Stores: ${err}`); + } +} + +// HELPER FUNCTIONS +type ProductData = { + readyProductData: Document, + readyVariantsData: Document, + readyInventoryData: Document +} + +async function getProductData(productId: string): Promise { + try { + if (!productId) { + kaptanLogar("00024", "ProductId is undefined when syncing Wix Stores"); + } + + const options = { + suppressAuth: true, + consistentRead: true, + appOptions: { includeVariants: true, includeHiddenProducts: true }, + omitTotalCount: false + } + + const productData = await wixData.query("Stores/Products").eq("_id", productId).include("collections").find(options); + + if (productData.items.length === 0) { + kaptanLogar("00024", `Product not found when syncing Wix Stores products: ${productId}`) + } + + const product = { ...productData.items[0], collections: productData.items[0].collections.map((c: Document) => c._id), entityId: productData.items[0]._id } + delete product._id; + + const variantsResults = await wixData.query("Stores/Variants").eq("productId", productId).limit(100).find(options); + let variants = variantsResults.items; + while (variantsResults.hasNext()) { + const nextPage = await variantsResults.next(); + variants = variants.concat(nextPage.items); + } + + variants = variants.map((variant: Document) => { + const vr: Document = { ...variant, entityId: variant._id }; + delete vr._id; + return vr; + }); + + const inventoryItemsResult = await wixData.query("Stores/InventoryItems").eq("productId", productId).limit(100).find(options); + let inventoryItems = inventoryItemsResult.items; + while (inventoryItemsResult.hasNext()) { + const nextPage = await inventoryItemsResult.next(); + inventoryItems = inventoryItems.concat(nextPage.items); + } + + inventoryItems = inventoryItems.map((item: Document) => { + const it: Document = { ...item, entityId: item._id }; + delete it._id; + return it; + }); + + return { + readyInventoryData: inventoryItems, + readyProductData: product, + readyVariantsData: variants + } + } catch (err) { + kaptanLogar("00024", `Couldn't get product data when syncing Wix Stores: ${err}`); + } +} + +async function getCollectionData(collectionId: string): Promise { + try { + if (!collectionId) { + kaptanLogar("00024", "CollectionId is undefined when syncing Wix Stores"); + } + + const collection = await wixData.get("Stores/Collections", collectionId, { suppressAuth: true, consistentRead: true }); + const readyCollectionData = { ...collection, entityId: collection._id }; + delete readyCollectionData._id; + + return readyCollectionData; + } catch (err) { + kaptanLogar("00024", `Couldn't get collection data when syncing Wix Stores: ${err}`); + } +} \ No newline at end of file diff --git a/app/src/Errors/errors.ts b/app/src/Errors/errors.ts index df378c53..5e350cbd 100644 --- a/app/src/Errors/errors.ts +++ b/app/src/Errors/errors.ts @@ -25,7 +25,9 @@ export const errorsList: Errors.ErrorsList = { "00021": "Error on WeivData config object, make sure you pass correct config object", "00022": "Collection Manager Error", "00023": "Aggegration Error", - "00024": "Wix Application Sync Error" + "00024": "Wix Application Sync Error", + "00025": "Wix Application Sync Error - Event data not found, don't forget to pass the event object from the Wix event function", + "00026": "You didn't configure any database name to sync Wix apps data!" } export default errorsList; \ No newline at end of file diff --git a/app/src/index.ts b/app/src/index.ts index f35f7a35..4e385cf4 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -42,10 +42,12 @@ const query = (collectionId: CollectionID) => new QueryResult(collectionId); const filter = () => new WeivDataFilter(); const _version = () => npm.version; -import { onMemberCreated, onMemberDeleted, onMemberUpdated } from "./Apps/wix_members"; +import { onMemberCreated, onMemberUpdated, onMemberDeleted, onBadgeCreated, onBadgeUpdated, onBadgeDeleted } from "./Apps/wix_members"; +import { onCollectionCreated, onCollectionUpdated, onCollectionDeleted, onProductCreated, onProductUpdated, onProductDeleted } from "./Apps/wix_stores"; const SyncWixApps = { - wixMembers: { onMemberCreated, onMemberUpdated, onMemberDeleted } + wixMembers: { onMemberCreated, onMemberUpdated, onMemberDeleted, onBadgeCreated, onBadgeUpdated, onBadgeDeleted }, + wixStores: { onCollectionCreated, onCollectionUpdated, onCollectionDeleted, onProductCreated, onProductUpdated, onProductDeleted } }; export { diff --git a/app/weivdata.d.ts b/app/weivdata.d.ts index bfeb3d7c..1c0c9d74 100644 --- a/app/weivdata.d.ts +++ b/app/weivdata.d.ts @@ -1940,6 +1940,18 @@ declare module '@exweiv/weiv-data' { * This is the name of the database that you want to use to insert the data of Wix app collections. It can be any database you want. */ syncDatabase?: string + + /** + * @description + * + * When enabled we will log information about sync operations (errors are always logged). + * + * Example: + * ```curl + * Wix Members Created Runs - {entityId} + * ``` + */ + enableSyncLogs?: boolean } /** @@ -2163,6 +2175,21 @@ declare module '@exweiv/weiv-data' { * ``` */ "00024": "Wix Application Sync Error" + + /** + * @description + * + * Another common WixSync plug-in error, this means that event object is undefined and due to this sync function can't work. Make sure you pass the event object that's exported from the native Wix event hook. + */ + "00025": "Wix Application Sync Error - Event data not found, don't forget to pass the event object from the Wix event function" + + /** + * @description + * + * This is also another error from WixSync plug-in it means that you didn't configure the database name you want to use for your sync operations. + * The selected database name will be used when saving/deleting the Wix applications data. + */ + "00026": "You didn't configure any database name to sync Wix apps data!" } } @@ -2205,15 +2232,216 @@ declare module '@exweiv/weiv-data' { * * In case of an error you can find logs in `WeivDataWixAppsSyncLogs` database in your MongoDB cluster. In this database you will have multiple collections to collect logs about each individual application. * You can find error logs and it's details there. Plugin only save unexpected error logs not any other logs. + * + * **Note:** + * _id fields are automatically generated and they are ObjectId, instead of _id fields you can use entityId field which will be the equivalent of actual item _id. + * + * Basically if you want to find a member by it's _id then use entityId field not _id field. */ namespace SyncWixApps { + /** + * ## Wix Members Sync Plug-in + * + * This plug-in allows you to sync Wix Members collections into your MongoDB cluster. In this was you can perform your data works with WeivData for Wix Members app data. + * You can also perform lookups easily (references/includes) with any other collection you have in your MongoDB. + * + * And there aren't any filtering etc. limit when you use WeivData, you can filter and query data however you want. + * + * Right now you have 6 functions to sync these collections: + * + * - Badges (WixMembersBadges) + * - FullData (WixMembersFullData) + * - PrivateMembersData (WixMembersPrivateData) + * - PublicData (WixMembersPublicData) + * + * The database name depends on your choice. You can configure it with config function. + * + * --- + * + * We do not suggest writing to these collections, use these collections to only read data from it. + * + * --- + * + * Functions are designed to work with `wix-members.v2` APIs Events. Define events inside the `events.js` file and point the functions you import from this plugin. + */ interface wixMembers { - onMemberCreated(event: any): void; - onMemberUpdated(event: any): void; - onMemberDeleted(event: any): void; + onMemberCreated(event: any): Promise; + onMemberUpdated(event: any): Promise; + onMemberDeleted(event: any): Promise; + + onBadgeCreated(event: any): Promise; + onBadgeUpdated(event: any): Promise; + onBadgeDeleted(event: any): Promise; + } + + /** + * ## Wix Stores Sync Plug-in + * + * This plug-in allows you to sync some Wix Stores collections into your MongoDB cluster. In this way you can perform queries, lookups and all other things easily with WeivData. + * There isn't any filtering etc. limit for these collections which you may see when you use WixData. + * + * Right now you have 6 functions to sync these collections: + * + * - Collections (WixStoresCollections) + * - InventoryItems (WixStoresInventoryItems) + * - Products (WixStoresProducts) + * - Variants (WixStoresVariants) + * + * The database name depends on your choice. You can configure it with config function. + * + * --- + * + * We do not suggest writing to these collections, use these collections to only read data from it. + * + * --- + * + * Functions are designed to work with `wix-stores-backend` APIs Events. Define events inside the `events.js` file and point the functions you import from this plugin. + */ + interface wixStores { + onProductCreated(event: any): Promise; + onProductUpdated(event: any): Promise; + onProductDeleted(event: any): Promise; + + onCollectionCreated(event: any): Promise; + onCollectionUpdated(event: any): Promise; + onCollectionDeleted(event: any): Promise; + } + + /** + * ## Wix eCommerce Sync Plug-in + * + * This plug-in allows you to sync orders and abandoned checkouts collections into your MongoDB cluster. In this way you can perform queries, lookups and all other things easily with WeivData. + * There isn't any filtering etc. limit for these collections which you may see when you use WixData. + * + * Right now you have 4 functions to sync these collections: + * + * - Orders (WixeComOrders) + * - AbandonedCheckouts (WixeComAbandonedCheckouts) + * + * > Orders collection is normally inside the Wix Stores database in Wix so the "orders" collection we are referring here is that collection. + * + * The database name depends on your choice. You can configure it with config function. + * + * --- + * + * We do not suggest writing to these collections, use these collections to only read data from it. + * + * --- + * + * Functions are designed to work with `wix-ecom-backend` APIs Events. Define events inside the `events.js` file and point the functions you import from this plugin. + */ + interface wixEcom { + onOrderCreated(event: any): Promise; + onOrderUpdated(event: any): Promise; + + onAbandonedCheckoutCreated(event: any): Promise; + onAbandonedCheckoutRecovered(event: any): Promise; + } + + /** + * ## Wix Marketing Sync Plug-in + * + * This plug-in allows you to sync coupons collection into your MongoDB cluster. In this way you can perform queries, lookups and all other things easily with WeivData. + * There isn't any filtering etc. limit for these collections which you may see when you use WixData. + * + * Right now you have 3 functions to sync these collections: + * + * - Coupons (WixMarketingCoupons) + * + * The database name depends on your choice. You can configure it with config function. + * + * --- + * + * We do not suggest writing to these collections, use these collections to only read data from it. + * + * --- + * + * Functions are designed to work with `wix-marketing.v2` APIs Events. Define events inside the `events.js` file and point the functions you import from this plugin. + */ + interface wixMarketing { + onCouponCreated(event: any): Promise; + onCouponUpdated(event: any): Promise; + onCouponDeleted(event: any): Promise; + } + + /** + * ## Wix Pricing Plans Sync Plug-in + * + * This plug-in allows you to sync plans collection into your MongoDB cluster. In this way you can perform queries, lookups and all other things easily with WeivData. + * There isn't any filtering etc. limit for these collections which you may see when you use WixData. + * + * Right now you have 3 functions to sync these collections: + * + * - Plans (WixPricingPlansPlans) + * + * The database name depends on your choice. You can configure it with config function. + * + * --- + * + * We do not suggest writing to these collections, use these collections to only read data from it. + * + * --- + * + * Functions are designed to work with `wix-pricing-plans.v2` APIs Events. Define events inside the `events.js` file and point the functions you import from this plugin. + */ + interface wixPricingPlans { + onPlanCreated(event: any): Promise; + onPlanUpdated(event: any): Promise; + onPlanArchived(event: any, deletePlan?: boolean): Promise; + } + + /** + * ## Wix Blog Sync Plug-in + * + * This plug-in allows you to sync some collections into your MongoDB cluster. In this way you can perform queries, lookups and all other things easily with WeivData. + * There isn't any filtering etc. limit for these collections which you may see when you use WixData. + * + * Right now you have 9 functions to sync these collections: + * + * - Posts (WixBlogPosts) + * - Categories (WixBlogCategories) + * - Tags (WixBlogTags) + * + * The database name depends on your choice. You can configure it with config function. + * + * --- + * + * We do not suggest writing to these collections, use these collections to only read data from it. + * + * --- + * + * Functions are designed to work with `wix-blog-backend` APIs Events. Define events inside the `events.js` file and point the functions you import from this plugin. + */ + interface wixBlog { + onPostCreated(event: any): Promise; + onPostUpdated(event: any): Promise; + onPostDeleted(event: any): Promise; + + onCategoryCreated(event: any): Promise; + onCategoryUpdated(event: any): Promise; + onCategoryDeleted(event: any): Promise; + + onTagCreated(event: any): Promise; + onTagUpdated(event: any): Promise; + onTagDeleted(event: any): Promise; } /**@internal */ const wixMembers: wixMembers; + + /**@internal */ + const wixStores: wixStores; + + /**@internal */ + const wixEcom: wixEcom; + + /**@internal */ + const wixMarketing: wixMarketing; + + /**@internal */ + const wixPricingPlans: wixPricingPlans; + + /**@internal */ + const wixBlog: wixBlog; } } \ No newline at end of file diff --git a/docs/assets/navigation.js b/docs/assets/navigation.js index 24783e2a..64e795ee 100644 --- a/docs/assets/navigation.js +++ b/docs/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA52Y227bOBCG30XXQbdb9IDmLnU3WaPtpo23yEVRFIw0krmhSIWi0gRF3n0h0bYonmbcS3t+fhxJw5/kfPtVGHgwxWmxGnqj2svOcCX74qTomNkWp0WrqkFA/8ci/GxrWlGcFLdcVsXpy5Oi3HJRaZDF6bcZqGTNmwDIpQFdszJgLvTLGV68ev104oIllKPsKPhyTG6Ca+D375lhNqOZbh67ALzUetTnb9/8+erF03eH/ZfWSkfer/2f9GKt9CPvjZ/aDjIL8Hz+Vuo2ks70NymbUblScvrlpWMhjiCezjKZf1gLadAYxSisNqBXapBOQvUgp6+/Z82aJe31Sx90zmV1KQFB7VQY7AKwnC4AzegCzJmsrqBV91harpSK7QQrqdxJSwN/7SpmaFwrxbBrWWpoAf3KBx0O7EHjtFGEoT4NwvBOPCKwvQzDfR6EQFCjBMf0WxTTbzHMlwE09mSTBgORSphWvLSyJRYsqVTRIr2BWmlAfMgRoSjUiRYyFJf1ooOEgiG4Uaglg5EPGxET0dhnDrUomOBJnpCAzLuSq0JhuC8tdSgw70yzhgDKedOsQUGIOzkiFEUramI5EwuZWsK04kXLVslzxsWgM6SDJMC458jNoyyv+cNZ10VOk06QdKb8yR8+QXsDOn60d3GzNDzRu/ntz+lnTaOhWbw6hxyoKNeEg/gK+kEYGthqKfhzLgzoLNVKKDBvdURYkaWRQV1BDRpkCRXh4aMjjpiKOgGGfTeIW2uYPtNeN/w4duEY9dYF0jw3TuFtWI42Ryks6wJpmhvHeCtWbmEDwl6mex/nhVGaEjvp+n2AcmIYZy1LMVRwefMflMEjLoIoyUAbAAy0lHHhM9h/sbGfeQeCS9gY1gR330XwKNIHeMzCPoC/zgPeFbBqpWQJWvooJ4RT9ks+9naXURJLc9kkUfsgRkqvMPrqCm19CJtSFpqRUmdByL9Hm1Ysgpw0R3Ivf0p314pyJ82RXG8Hi3JjexjGvdbcYO9h0vwOl/IyZiF1Bm8/Rcojrsbm+nEPuudKxs6H+1juPh0et+bxLH7KWp52D3txjDBHEUT6KD9HEcToCinAGEOGpw/sczSDKJW8B23W1b/KbmTrKsaKyGjQjRl9E0FaUQ6ogRmYd+4oz9NkcBUIwHC+JoOrvZP0DKkjB2hvaLL3UqP9lloM/dbzWWf8IZpBNPFmTZNt0DRIa6ahtWMarBHTEJsvDdJ2aWitFp5rsnBCY4Un/YRjXsJ314Kdi6YZB0kO1s92HCU58QxG8N7M9d/HSJ4kA2szHaIW7wpJZni82GwkM7RLtJK6fPuoSzSOunyz6C7VJrpDWkN3y200iSB9Op1cmxpblXp3ocxUoifJwiRrEaP1NVlc0is06hI7xSHtaEEHogywT2zcfX7TNnqQZcKp9rHM8CFpc0Pc4L7/D/LCmdGMHwAA" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA52Y227cNhBA/0XPRnNBkqJ+czaxs0hSO94WfgiCgpZGWtYUKVOULyjy74XE3RXF28zmcXcOjyhpNCTn+3+FgSdTnBaroTeqvewMV7IvToqOmW1xWrSqGgT0Lxbh37amFcVJccdlVZy+OSnKLReVBlmcfp+FSta8CYRcGtA1KwPngl9e4fXbdz9PXLGEcsSOki/H5C5wA/zhAzPMzmi2m+cuEC9Zz/ryj99fvX3984fj/qi10pHna/8nPViLfuG98ae2k8wAPp9PSt1FpjP9TZrNSK6UnH5507ESB4hPZzmZP1kLadEYxSysNqBXapDOhOpBTm9/75qZpe3dG190zmV1KQFR7ShMdgHYnC4AndEFmDNZXUOrHrBpuShV2wlWUr0TSxP/3VXM0LwWxbRrWWpoAX3LBw4X9qBx2whhqq+DMLwTz4hsj2G6q0EIRDUiuKbfopp+i2m+DaCxO5sYTERKYVry0tKWmLCkVEWT9BZqpQGpQw6EqtBKtMBQXbYWHRCKhlCNQpYsRl5sBCaqsdccsqiYUJM8kKDMVyWXQmV4XVpyqDBfmWaGIMrVpplBRUh1ciBURUtqYjoTE5mawrTkRdNWyXPGxaAzpgMSaNx95OZZljf86azrIrtJJ0jaUz7yp/dCOTtvZ1/vunZcbi//yJ8+lqqlqEYOUX1l+g4Ml6SpHWBMCu0t6PgpxldaFBFeaV5y2VwJljgbeVaXR9QbozSQpJYMdW7S7A9PZ02joVnksyMOKMrZ7QBfQz8IQxNblqI/58KAzlotQpF5JSviitSrjOoaatAgS6gINx8dccSlqBfAtO8HcWdXMd9pz4B+HDsFjrwtzWmfG6f4Nixnm6MUly3NaZsbx3wrVm5hA8J2OHpf54VRmxI7dP0hUDkxzLOWpRgquLz9F8rgFhdB1GSgDQQG/GodHRfeg/0XG3vFOxBcwsawJmhILIJHmT7Dc1b2GfzvPPBdA6tWSpagpa9yQrhl/8nHnu4ySnJpLpukah/ETOkvjP51hWV9CDuFVppBqVdBzL9mm75YRDkxR3ovH6W7akW9E3Ok11vBot7YGoZ5bzQ32HOYmF/xUh7GDFKv4K2nSHrEaexa/zyA7rlyPv95076P5Zoc4XZrHs/iu6zlEeSwFscMcxRRpM9XcxRRjFUhJRhjyPD0KWqOZhSlkg+gzbr6S9mFbF3FXBGMJt0YvThrRJUWygk1MAPzyh31eUxGV4EATOczGV3t7aRnSR3ZQHtDkw2xGm2C1WLot16ddcYfohlFE++gNdmuWYP0yxpaj6zBumMNsSPWIL2whtb/4rnOFyd0u3iynnCslvDdsWBXRdOOA5KT9XM5jpqceEYjeG/m/O9jJg/JyNpM267FW3WSGR5PNhvJDO0S/b0u39PrEt28Lt/Bu0/17u6Rft39chlNKkivTie/TY19lXp3oMxkoodkZZK1SKH1mawuWSs0WiV2xGHa0YQOoIywTyzcfX7RNnqQZaJS7WOZ4UOyzA3xAvfjf6rxB3MhIQAA" \ No newline at end of file diff --git a/docs/assets/search.js b/docs/assets/search.js index ec9b8c8a..b6c30572 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAA81dTZPjRo79L+prbY0Afol98/aMZx27M+Oxd9YHhcOhltjVHKuosj662+Pwf9/IpCghkY8kWNJhfHFHMRMAMx8BJB5I/Tbb7z4fZm+Xv81+rpvN7G2ePsya1XM1eztbPT3tq6fVsZo9zE777ezt7MOpWR/rXXP4w+Xa48fj83b2MFtvV4dDdZi9nc1+fwDCPtTbY7UHktoLVjG/nKr9r0CK/7tVyPvT9udvmkO1PwJJ14tTxH1XPe8+oYW6Xpwi7vtVrzB3aYqof7xs8BZeL1rFrffV6li92223lZcBhOohVtGbaluNiNZD7MhrNn9r0Aqcr5gFbU+Hj+9W649Q1uWiVdxTheD3VJlx91Qdv2o2vciTl6eKfNmu1kMy/fVpQntxKC+bkbhrPlX74zeb/919f9zXzROCoh7zCuF/e//Pan38ZjMsvhtlVVA36331XDVo/y/X7MJ6HFk9yYm1o7+rPlT7qoF7r0aYBR8uU9AyystWkdv6cLz6gQOQqkZYBT+ftsf6ZYsCTHfJKqpZHWv4WLYXrGJeTtstEOL+bBdx+AhFHD5OirqD26hGWAXv+5zXfpLbakcPgVeNsAt2/xgMS3qIXXSfk91Pc6/n4Zd7Q89DNMYq/IATkcOEJOS4PzVr7Pq7S1ZRp74YcpoUPX76VO0PeDe7S4OieF4WlPFF3jfNenvaVG0YuAg9/vpSHf4QXBs2MMuS/GrjT27+qLA3l3FC5sPsZbV34UWbdlVGc5YBrwPvX90fjDofo2kGEx7P9vZY8qGutptJRsgZd9C/21f1U/O1E2o3IZx0uxWrg1m3H3q7xufVl2+O1bNdr5hwu/b17tQcp+kPptxuwWG3H39wO93nwa/SestD7hWfRf4HTbKgtbnfg/mDy/dVlFG1Fqmrk3zjNTB+80ctVlya5m+P1XMkrf3jZDlAys1eupMx6pyd/n7zvqtWm3e7Zl3tG6VBXJl0wz9U9ac/ro6rv72gbVZXb10GJG5sRbSBfY/r6eVlXx0OX52OH+1aH9U0kwljjuMs8r92u58t66lN6ebdw5Z9L16GLAln3cOOy8l4yoIEk15pxQjcw9INNGm8gDOi44d9fRzR4YfcouPvQQ0U6vj7aDl0RMffPjeiWgt1+CE3r5VB0XXcJG3+sOPqPsDNB9emS/Vn3D6xl4uT5H5bv1Tbuqm+P66eqv+u9P7qy6+XPSR4WGqWRzv5VUQR1M2x2n9YrcU2fmXiCnieLkQGvK6ajazrjcp9I+cMO5Cr1X3qP01S/OkOKn1CO0FpN/5GtZvqFSsdTLrRAMULjSu/TLhR8dN+d3qZoLcbf6Pabf1cT9nnbvyNap9XXyYobUffqrJupqisx9OPUZX70xSV7egbVR5+rqeg6Dz8VqWn5yk6/ehbVQaxw6D0PP4VaofizHfV4bS1PT/t0MGYE6StdVCAMEp+Uw/WIPrs7zFhWzVP4kRjteEy7bVGBDv9cXX4a/Vl8hq/uc67ixnNa2xobjMg2IyXc2Y02Qgx8fUrgTPn64xTM3yIByPvdaDvE2093KObeP1Bf8SaaYd+JOzWQ/eYgVMO4NPtGz2Mj5k34WBusg65969Hk8Cvx7uEwgy+2RjFvWmHDt/Z2cAeZe+r4+eqGkw9pMLr8FuUrnfNcVU3gxFLahXjb1FbNZvDD/VwkJJqxfib1P5iVvjLjaqG0xypypDgjKgajHCBqvGwNqzq4+rwlWglGFF3GX2jyu93z+bVvA6/RWl9+NPzy/FXq9Lr8NuU/nV3nKhXzLhF9da8vttbl3Y47w5U3YrW4axLqjKkWSOqdubbaofeomxnDXJvduMljmFVh+Nqf5zkqoMZU1WDqB7WqYFyS5XaHtOv0kwhvbXu9RFdqLMG9EGVlngudJrD+aBSSzQXSs3BfFjpYCyX6sZD+aCi4UguFBkC+bCiQRciFY07kEFF41FcKDMG8TGFYzE81GgK4YMqDRFcqLQG8BGVlvgdaDWH70HFw9FbKDQE72FFVoQaQvegouHILRQZAvewouG4LTUZwvagquGoLTQZgvagIlvMFgonhOxBxSa+TQZYMwE0EvNGSK8g4FkIr0F1NrJL6JxAdA0rrg/Hullbb1UMv0Gp74y0phOXwTcpNCdo56G3uOy2y83qry+jb/GhY9SddKMm2m7YGYwwS9INWFilSFlfyj7OsYhhdn5lfdo7i74d4ZG07DfhPMM9Dpf1R2meyAAbxWNQPU7vRLqN1I5B+cvqqfq+/te0tReTbjbguDuutu9MHl+aEEy7jxEOStMgEEy72Yif2h6GaSaISbcbYOG2YgvMxBY2YSrLGBlgZRht6r/dV5+mqj/PuVn9GLkZ6TYRmwbFL1Nv+uWWOw5JzP+8vFmtIkxLSOnLt9KVUN4YRxnZ2BdF/JhKvyQypPNRzLEpH6H2Onm+8z3i90ymXKfe3aJX23OLNQhy351f/euBnLx8D8hF8iyQC2zsZZrdGIS4PpWP1yk21aNctxfXj7cxQyxwG7YHbbD7IkLv9l4v3mNzlTTL1grrpvsSrM/mScTcW/3IiBmWbTVb414t7XUgPYYEc+5gQ/s66YQNuU54nXYE6/aLCL3AlpfvAe1IngXcgY2TF7NPpWk5g8mm7ewD1ZgZ48AatqWvmevv4Tvzw/1cePC9WroGpFu7unrupmdLdvtN72sf48Y8dtNfY9IIWKJz8mTjph6aR+0bqNR0c801m3DCHbtj++VPK6CoO7pHMQHZNL2s0GvYK8+1yKqJJ1yzScazbo9JU069NpOM519kz5STsM0Y45kYGTPldDxgTBgfevPZ++WyE/NYQw4bvwzXlzS2I8cUjYb0c+qp84reBE2Mn677qjh80fd5tzltq8Mf/F+HN0V83uHDsdqHvuv6AZJW0nUIECpsbq3p1/Jn+I0xoePP8HNjEzT0fkdPKOn9nN4EPWEzEVTT1040QUvvx9SEmt4vqk3Q0/sxNKGn94toNj3vK/ddkmGYiTE36hkC2mXEjTpGoCYH3ahpGGxizI16RuAmB92oaQRwctCrNe2ar1f19rTvV3MZcfO69X07K1y4vu9oTfIIw6rkoBvv6uveD1XKu/q696OVE+5qTJUcdLtnGPr6o3IRQ1+CnBb3xpVGI+9ypzZs/nnkQ5qT79WE0/toDe/A4MH+PPwl0lfc63iUvovOLqT0f64zDD39n+6clFGNqQuH3Xh3f+n/5qW8ub/0f/9ywr2NKgtG3Xhn3+IPX8q7+hZ/BHPCHQ0quYy4+U7gV0DDO4FfBJ10JwNKLiOm6dBHW3f93a45yuN/e25r1YjrE29m/MyLNYwdfaNZfSfSzXvw3cJBnY+XKUbVo696DnzGcdgS46ccp1p0OlT76IQ+bMllyv0s+G63rXRZfdyIbtar7UDYH9wYd3Ea6q/3+e50OO6eNYXQlSeCq8Mv3IhS87td07Sg0HJFkSwUHU0Zvp/Q6p4tXG2e6+bdtq6a4412vIGiDKbF8qyu53YLH0cc1JhYcM89C/1cPb+v9vdZaSzr32qpgYndxzXTV682uu+e5f5UH+rj7k7r3SPs32rBkY3diuevXnF4530R0g/yH/n77rTFPaCmWwGC/q2WWtvXLTO/epmjOx7nt9/tmg/1kwp3oZ5w6OuChSH1G1U6lgUOCehtZPm1WbtR71eH19rzqGRMN26YTG7HTPE91+F3iOzrECAT9L65TDWiWdzmHR45bM3kSC3ErDWUrqv0p/1+t49zuvbPw8mcejDbKf9TH/Th6yzren14c88WTXoEsYqx5y6a1QeluftvisrHboZR8cg5wwmjyfr7P+L9Gv08WX9/OHiN/mSy/uSu+tPJ+vvzvNfozybrz+6qP5+svz/reo3+YrL+4q76F5P1L+6qv5ysv7ynfprs/+iu/o8m+z+6q/+jyf6P7ur/aLL/o7v6P5rs/+iu/o8m+z+6q/+jyf6P7ur/aLL/o7v6P5rs/+iu/o8m+z+6q//jyf6P7+r/eLL/47v6P57s//iu/o8n+z++q//jyf6Pb/J/V+Xf/9qsf6i/fPXyEh/TxDVz4f1z/eUvvo4Iz+RS4nXk8IlNWtjTBbtrWknv/C+4wg85DGh+E08ftUTIGDPqH+odkolG/WPkfZLXGfVH/4O0rzbqOn2aUT8+zOpmU32Zvf1t1v2Q3dsZPyaPzp2eP+Txdtla+zBb757PzQSb3frk//njedj/VeujO8i/Xbaj/zCfPSznDxk/ztMff3xYdnP93/0fOhHXv/h5NHtY0kOaP5YJBxMpmkjBRJ49LBko5GgeB/OS2cMyAfOSaF4SzEtnD8sUzEujeWkwL5s9LDMwL4vmZcG8fPawzMG8PJqXB/OK2cOyAPOKaF4RzFvMHpYLMG8RzVsE88rZw7IE88poXhluvMMBIchQjBlSoPGoITQXwCbEjcvcl4SQQzF0KMSOy7qXhNBDMXwoxI/LmJeEEEQxhCjEkMt2l4RQRDGMKMSRy1SXhJBEMZQoxJLLMpeE0EQxnCjEk8sQl4QQRTGkKMSUy+6WhFBFMawoxJXLzJaMcMUxrjjElcuqloxwxTGuWPkj75CgRwIuKcSVy2aWjHDFMa44xJXLRJaMcMUxrjjEFTuoMMIVx7jiEFfsoMIIVxzjikNcsYMKI1xxjCsOccUOKoxwxTGuOMQVO6gwwhXHuOIQV4mDSoJwlcS4SkJcJQ4qCcJVEuMqCXGVOKgkCFdJjKtExTof7GC0A+EuxFXioJIgXCUxrpIQV4mDSoJwlcS4SkJcJQ4qCcJVEuMqCXGVOKgkxQPnj0lJ4eQYWEkIrMRhJVmgXCSJkZWEyEocWBKErCRGVhIiK3VgSRGy0hhZaYis1IElRchKY2SlIbJSB5YUISuNkZWGyEodWFKErDRGVqoyKZ9KwVwKJFMhstKsd4fTGFppCK3UoSVFsExjaKUhtFIHlhTBMo2RlYbISh1WUuTu0hhYaQis1GElhahMY2SlIbKyee9qZTG0shBamYdW+ZAmj1lehJNjbGUhtjIHlwxhOouxlYXYypJ+q2NwZSG4MoeXDJ4oshhdmcrVfbKOnogMpOshuLK8f7lidGUhujIHmCyBVsfwykJ4ZQ4xGXqeshheWQivzAEmg+eTGF1ZiK7c4SWDZ5QYXHkIrtzBJUOPRB5jKw+xlXtsoQwgj7GVh9jKHVoy5KfzGFp5CK3cgSVHmM5jZOUhsnIHlhz56TxGVq5Ogv4oiFCZg8NgCKzcQSVHfjqPcZWHuModVPIUgTKPgZWHwModVnIErDwGVh4Cq3BYyXOkuIiRVYTIKhxY8gJOjqFVhNAquK8cUcTQKkJoFQ4tOYJlEUOrCKFVeGiVUHGMrSLEVuHgUiBcFjG2ihBbhYNLgXBZxNgqVKXBlxoQLgtQbAixVTi0FNDhFTG2ihBbhYNLgRxeEWOrCLG1cGgpEC4XMbQWIbQWDiwFcniLGFmLEFkLB5aiQOFhEUNrEUJr4dBSwCRgEWNrEWJr4dBSwMC0iLG1CLG1cHBZzOHkGFyLEFwLh5cFAtciBtciBNfC4WWBwLWIwbVQpaxFbwKxAOWsEFyLsjeBWMToWoToKue9SUAZw6sM4VVSbxJQxvgqQ3yVDjEL5OjLGF5lCK/SAWYBHX0Zw6sM4VU6wCwyODmGVxnCq/Twgp6+jOFVhvAqPbygpy9jfJUhvkqPL/hIlTHAyhBgpS+WQnddxgArVcHUV0zncDIomuqqqcNMiSvmc1Q4VZVT1zi2LBnPB8XTuaqeusavZQlB2l7T81UF1TVuLUuItfaanq+qqK7xallCuLXX9HxVSXWNU8sSIq69pueraqprfFqWEHTtNT1fVVRd49KyhLhrr+n5qqrqGo+WJYRee03PV5XVuS+tziH82otagALguW6Pi+8AgFHpnnqdDcHqvQKgL8ljf0Oogq9L+L4sj10OoSq+LuP70jz2OoQq+bqU78vz2PEQqubrcr4v0WPfQ6iir0v6vkyP3Q+hqr4u6/tSfY8DQpV9Xdr35foeB4Sq+6q8T75k3+OAQIWfVImffNm+xwGBKj+pMj/50n2PAwKVflKlfvLl+x4HBKr9pMr95Ev4PQ4IVPxJlfzJl/F7HBCo+pMq+5Mv5fc4IFD5J1X6J1/O73NAoPxPqv5P3H8sJUABkOIAyNf18emSAA1AigcgX9vHB0wCVAApLoB8fZ/mkIEEdAApPoB8jZ/mmIUEAFScAPk6P80hEwloAVK8APlaP80hGwmoAVLcAPl6Pz7rEqAHSPED5Gv++OhIgCIgxRFQ0gIQMqKAJSBFE5Cv/NMcsqKAKCDFFJCv/uOTIAGygBRbQJ4BwIdBAoQBKcaA0hZ/PQIAABVtQGkLQLyDgDogxR2Q5wMIc/+APiDFH5DnBAjz/4BCIMUhkKcFCPcAABaBFI1AnhrAJ2oCTAIpKoE8PUCEIQzoBFJ8AnmOAJ/KCVAKpDgF8jQBEfahgFYgxSuQpwqopyMBYFBRC+TZAnxCJ0AukGIXyDMGRNiJA4aBFMVAWQtB7MUBy0CKZqCsxSDsbwBEAymmgbIWg7DHAZANpNgG8gRC3woCDCrCgTyHQLBPggDlQIpzIM8jEOyVIEA7kOIdyHMJBPslCFAPpLgHyvupLQL0Ayn+gXIaQBDgIEiREJTzAIIAEUGKiSDPLhDs+iBARpBiI8gzDMT4IQaMBClKgjzNQLD9gwArQYqWoHwAgoCZIEVNUF4M3QDAoOInKG8xiHubAAYVRUF5i0EYyQFLQYqmoGIAg4CoIMVUkCcfCPayEOAqSJEV1LIVPZEcEBakGAsqWghCJwRIC1KsBXkioieVAbwFKeKCPBnRl0kA8oIUe0HFQCQGBAYpBoOKoUgMWAxSNAYVA5EYEBmkmAwqhiIxYDNI0RnkKQqCfUkEGA1SlAYtBiIxYDVI0RrkmQrcn0CA2CDFbNCibamEcQRwG6TIDfJ8BcH+KAL0Bil+gzxlQbBHigDDQYriIE9bEOyTIsBykKI5yFMXBHulCDAdpKgO8uwFwX4pAmQHKbaDPIFBCfSBgO8gRXhQ2eIP+jBAeZDiPMjTGAQ7pwiwHqRoD/JUBsHuKQLMBynqgzybQbCDigD5QYr9oLJt64X4A/wHKQKEPKdBsJOKAAVCigMhT2sQ7KYiwIKQokHIMxsEm6IIECGkmBDy5AbBxigCXAgpMoQ8v0GwOYoAHUKKD2HPb1AK+0EBH8KKD2HPb1AKe0IBH8KKD2HPbxDsdGLAh7DiQ9jzG5TBnmPAh7DiQ9jzGwR7lhjwIaz4EJ63reWw9xjwIaz4EPb8BsHuIwZ8CCs+hD2/QbADiQEfwooPYc9vEOxCYsCHsOJDuOVDYCcSAzqEFR3CLR0Cu5EY0CGs6BBuX2WAHUkM6BBWdAi3rzPAriQGdAgrOoTbVxpgZxIDOoQVHcLtaw2wO4kBHcKKDuH21QbYocSADmFFh/D59QaIP0CHsKJDmPpbhhnQIazoEG5fc4CNTgzoEFZ0CHt6A3cdM6BDWL/t4OkNyiH+0QsP+o0HT29QDvGPXnqI3npo8QfxD198UPjjFn8Q/+jlB/32g6c3CHY/MXoBQr8B4ekNgh1QjF6C0G9BcP8hmNGLEPpNiJYOgV1UjF6G0G9DcH+vC6MXIvQbEZ7doAI+f+ilCMWGcNJ/BmbAhrBiQ7hlQ2AzFwM2hBUbwu3rET36Af4UG8ItGwIbwhiwIazYEG5fk+jRD/Cn2BBOWvzB5xewIazYEPbsBhXw+QVsCCs2hFs2pIDPL2BDWLEhnAzgD7AhrNgQTlr8wecfsCGs2BD27AYt4PMP2BBWbAin/edfBmQIKzKEWzIENrkx4EJYcSHcciGw0Y0BF8KKC+GWC4GNXwy4EFZcCLdcyAK/Pwbwp7gQ9twGLeDzA7gQVlwIt1zIAuIfUCGsqBBO29cMIf4BFcKKCuGWCllA/AMmhBUTwi0TsoD4BUwIKyaEPbNBJcQvYEJYMSHcMiElxB8gQlgRIdwSISXEH+BBWPEg3PIgJcQf4EFY8SDc8iAlxB/gQVjxIOx5DSrxO4wAf4oH4ZYHKSH+AA/CigfhlgcpIf4AD8KKB+GWBykh/gAPwooHYc9rUAnxB3gQVjwIe1qD5xB/gAZhRYOwZzUYtoMxYEFYsSDsWQ2G3RAMWBBWLAh7UoNhNwQDEoQVCcKe1GDYDcGABGFFgrAnNRh2QzAgQViRIOw5DYbdCAw4EFYcCHtOg2E3AgMOhBUHwp7T4DnEH+BAWHEgXLSvW0P8AQ6EFQfCRfvKNcQf4EC6v/kvWXxqf5et/aLFctl9yfG32U/nr1y4uOz1uA9euND79rfff79+1+Ltb7+LT1u4a07X+euKUgpLKYlZCodSEiklNUtJQimplJKZpaShlExKyc1SslBKLqUUZil5KKWQUhZmKUUoZSGllGYpi1BKKaRkc7OUMpBSzqUUskqhELulxG5mxi6F2C0ldjMzdinEbimxm5mxSyF2S4ndzIxdCrFbSuxmZuxSiN1SYjczY5dC7JYSu5kZuxRit5TYzczYpRC7pcRubsYuBdh1mYCQYsYuz0MpEru5GbtMoRSJ3dyMXeZQisRubsYuJ6EUid3cjF1OQykSu7kNu913jq9iEoG6pGintaHgYdY6rYdZ+6g/zBaL9v/U+iH3j/z8j3ze/aP7S9H9ZXEe7Mqd7T847/5xFugqOed/cPePtPtHNya1AeinD/X26L9ZJvBMAkNkjJc/vdQv1bZuqlASS0m2vfup+z6WXHexe4lNjP8JjfYD+LvuW+nCskI+J2wU6X5Pad3+OKcQlQiYO5baLOpD3Wx2asVS6Uvmtk30wp4qZVUqrbIBvhO0ajb788+1CYmZfITmNscdSDz/2J0UKT3EfMLStSJP598KlBLlvs5tnstLrK+/4CbFyTAztwWIs7j2d0/ljmRyRyas3/PlB9ikZRIoNAEoL/4nw4SkXG4CTdiEF/8zalKSXHyasPi/tL/dKhcrl4tli+9eFACu9NqujWKCrBiyqRRWThAGwJospCxb4rB6etpXT0qSSB2MUpqNnC/PNuU5fpEVVIFjTQUEjDFodVhXzaZunqScXFiUd5GUjTj4FIoSz11u27D25/mAqy/lhtlur5UFfb3MPue2jWulaWefzqVdtqzvIqnH28u0a26LH6FI4O5lWjm3JRZSJvL3MmueT7nzHocvTydzG946eZHHlw8DlTZ/2ArDLl/ihabgJfb5cidoyk7ETl/uAE3ZgdjrpzJfLKeYBRCcyrystMW1Thjw+zIglVOAAVCbyuSstD5cx89VFaTFsphUpp3bNm7BaftzDFmxZEa4XsTsq8NpG+KfZMZjLOo4gfFmivUyouIiBtkl3bixHOIEHlahVeL2pgkBNrGEvrEU4cTF6BJANSLrIgbZJffQePxeu18cO1TnXycN8wMhzlj+bA9xXua+/d02eY6TYSqxReTrD6fWQRYkH3FjUfUqyk+UT5Jw1pdTOdn2tfuhLHmf0vcnVuOcGHj6lceaxLgNl5+XgxJlFGDbM7reNcdVHQqSle0y61yazRetd56YqDeBRHnGzM5FFWPqdhF43O3e/9Pd/CYMffLRmCrxcNyrnDd0l0Z5KkXNhU2XohJbYXdqjvWxeg6fWLF+Rgpm7b8nf302pDiBPCOKT3v3cfWX1VMYQ+fyIJbYAvLa/5obwm8uj9Nsu83Ne/3QkzxruI5Dkxj/TXm8WuKptwqDpym56l05MrEF5E19ONbNOowKskBMRrdbNZvD5/oYZI+Sgiq7YqgxH61+CSSJpSqLTpLtKarOv9Yn/ZnEQ2J7floxW/9TfZLakq4xtS26/9R/FFKETUaTzr8sEGydTIsTW7LYVokDMUJIO6mwigpLDyTr++6TB1Yh6jQt1sa4NNvT4aPPKkJzhDXGvXKHv6fGL3XgOIUo47aHLk5yomVXAzEGGl0hkPtle1x7KwPy0bfteX9FIMDiBFnoTCWpBpuo/e70ErhJ4UMKoznBMkv+ua0Nuh2zxbmPq8MqPKVLHprmXTA3RpWPq0NTfQmskz5t0R0X0wsFZQPWx9XhZV99CsEltzHtnG9hvu/DLnR0kjqneRevjMnzx93uZ5dZqpsnWVFk41PkZMUxXqbibHsCnKDQDSfyHhc2wDpC67RR/komjckkOW1OG0QYkT0ajwW4jCZzMxsMQPlMVsdtoG+FVGGACdL07EyZkpFh7yS6tFgdLCi4y472JSPpLgUrsfK+jS0fXQnmQ7WvGuVjg5q1Tdqhen45BqU52YdC824R2RbW6kOzO8Yipceed8vHxn05XG422G0O6sE2UcfqOYjbYv3TzokvjLvqYRJIEyAx9lhFkJDH9UW39klnWmGDyDZ0sdJtzLtgYPRm26p5CvN4WZ5YdFtpTHq29XMdnmEFYovONCPt77Lv61EqWEUO2EmbtDC6S0cy74oTxsPi8+pLsGACp4Xt1p5XX+LDuYgixi7LZ/+bZAOdEZIaYJvzfq7DM75YaWNagQgPDiwxiWlWxzpMV1my27atUgcL6eXnXbZkLHHpFEyeBRcdgtIuty9s/qHZhbCU8WLeZV/GnpZd82FVb097lUtL71DaMLBrzsjqfkxQOmWZMhk52E7epvvJPfkkSy9f2J7lTt6p+11BaZ887Rc2t7ULzsOyB5DmF8LaBv7dfqNO1yz31HiDrkJ2qP+l0kMpKO1SaeNZHfV0ycZuY1tRJ+ZwVDU8yUIae/ICWT9Xgb+Q7SdGpiI+ygTHyEuItUFW06wctIwbRYT0Kgc9tyYREa0q3ahdQk9+Jflx25btq9VmvWvW1T4IErLTrWuR7Dokjb0wVxN1DidJaWPnqRfmSvJaluy0MvbCxlUTloyLzaG2QsKzjExvydg8fZaDTjAyJSJjF3XHqoKjhvRaxl6dfeUm4OI3S69sewJBjSkRS2YseZ+lXO4xWLRErJmx8r0/hemReCCNRRLNPMvqgbGG60SA06bMj8gI7x4KW660+9yVSdTPdVCCK4Q5RRdGjaWIwy4sIMguC2MbcxSiCuF/rbd0XO2PEdNB86Cy3EUWoy84nAKXJFlvY2w6nF5e9tXhsDqFdsmG2ex82DL2SHYio+KW7PMytlccfm3Wm9Vx9X51CBOYQgYc4yHQCftcf1n53/aWaV4QvGwrf9wdV9u4E1BmjJR2Z3LjSc7L9OlamHtIhBip1uP+1KxVKVw2pBsJlbieLvvHjWVFkFmTTIEo7w7zxrU/CwROK2DebZHm9HIuuIUGSndOC+NqHaq9bgWQ0DIWnpyY/U53tEiulo11ik/1oT7uhg718hRsjKWfq/qTeyRhx7FsGjfuZiQvDiHyYV/Ybj2WekINKvJNUmPvfScZdePIrNz4iHXiYi5Vvp9qbN7thIE7lR2xxpcXlbSIFJW9wMYsUYncfW7Ce5YvVhhL8UpkdMyRrbvGt/CUyM/7OkS5fOXF2JOHRMZ3LxJlI1/QyY2bZucBtzvtxtVJD7kOeXIhY+2mRzxqL5Rx1Mi5KumRTBnmydgr97n+0paHVMYgnbEl0Pz4cK2avF3++Pvv/w+PSvi7rfsAAA=="; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAA82dXZPjxpGu/wvnts+YmQABYu7kseVV7NqWpfXxBUOh4JCYbuywAQokZ0ar0H8/gQJBZmW9ABIkT4R1o4lGVWYW8CLr46kCf5vV1ZfD7N3qt9mnotzO3iXx06xcv+azd7P183OdP6+P+expdqp3s3ezj6dycyyq8vCHy7W3L8fX3expttmtD4f8MHs3m/3+BIx9LHbHvAaW2gtWM7+c8vpXYMX93Wrkw2n36bvykNdHYOl6cYq5H/LX6jO6UdeLU8z9uO411lyaYuqf+y1+hNeLVnObOl8f8/fVbpc7G8CoLmI1vc13+YhpXcSuvHL79xLdgfMVs6Hd6fDyfr15gbYuF63mnnMkv+fcrLvn/PhNue1Vnrw81eR+t94M2XTXpxnt1aG8bFZiVX7O6+N32/+ufjzWRfmMpKjL3GD87x/+J98cv9sOm+9KWR0U5abOX/MSPf/LNbuxnkRWTEpibekf8o95nZfw2asSZsOHSxV0G+Vlq8ldcThe88ABWFUlrIZfT7tjsd+hDqa7ZDVVro8FfC3bC1Yz+9NuB4w0f7abOLxAE4eXSb3u4GNUJayG677kVU9KW23pIfGqEnbDzT8GuyVdxG66L8nW09Lrufilbeh9CMpYjR/wQOQwYRByrE/lBqf+7pLV1KmvDzlN6j1+/pzXB/w0u0uDpniepbTgi73vys3utM3bbuBi9PjrPj/8wbs2HOBiESXXGH9u6o8ae3MpJ2w+zfbruuledGhXZzRn2eF14v1b8wejz7dBNUMIb8/x9kTysch320lByBoP8F/VefFcftsYtYfgV7o/ivXB7NsVvd/j6/rrd8f81e5XVLjf+6Y6lcdp/r0q90dwqOrxF7fzfS58k9d7XnLn+Gzy/9CkCNqY+zOYm7j8mAcjqjYidXVSbrx2jN/9SZsVl6bl22P+Glhr/zjZDrByd5bubIwm58Z/f3g/5Ovt+6rc5HWpPIgrkxr8r7z4/Kf1cf33PXrM6uq9twGZG7sjOsC+1/W039f54fDN6fhi9/pWVTOFMJY4zib/o6o+We6nDqWr94hY6l69DEXi13pEHJeZ8ZQb4lW6MYoRuftLNzCk8QWcER//qovjiA9X5B4f//DWQKGPf4wuh474+PuXUqzWQh+uyN33yuDoWm6SNzfZadZ9QJr3rk236ua4fWYvFyfZ/b7Y57uizH88rp/z/8z189WXb7c9ZHjY6iIJnuQ3ASIoymNef1xvxGP8xsQKeB4vxQh4k5dbua43aveNrDOcQK5R97n/PMnx5we4dAPaCU678ne63eY33Gmv0p0BKC407vxS4U7Hz3V12k/w25W/0+2ueC2mPOeu/J1uX9dfJzhtS9/rsiinuCzGhx+jLuvTFJdt6TtdHj4VU1R0Ln6v09PrFJ+u9L0uvb7D4PRc/ga3Q/3MD/nhtLO9P23RwT7HG7YW3gKE0fKbYnANoi/+nhB2efksZjTWGC7Vbg3Ce9Iv68Pf8q+T7/Gba72HhFHeEkN5XwDew9ifR0aTgxAVb78TeOR8rXEqhyfxoOSjJvR9pq2Te9SI2yf6I9FMm/QjY/dOuscCnDIBnx7f6GR8LLwJE3NTdCi9fzs6CPx2fJeQP4Ivt0Zzb9qiwy07B9jj7EN+/JLng0MP6fBa/B6nm6o8rotysMeSXkX5e9zm5fbwr2K4k5JuRfm73P5idvjLna6GhznSlWGAM+JqsIfzXI13a8OuXtaHb8RWghF3l9J3uvyxejXfzWvxe5wWhz+/7o+/Wp1ei9/n9G/VcaJfUeMe1zvz/d3de2uHx92eq3vVOjzqkq4Mw6wRV5W5WW3Re5xV1k7uTTW+xDHs6nBc18dJqdqrMdU16NX9dWrg3LJKbe/Tr9ZMXXob3e09unBn7dAHXVr6c+HT3J0POrX05sKpuTMfdjrYl0t34135oKPhnlw4MnTkw44GU4h0NJ5ABh2N9+LCmbETH3M41of7Hk1d+KBLQw8uXFo78BGXlv7b82ruvgcdD/fewqGh8x52ZFWooesedDTccwtHho572NFwvy09GbrtQVfDvbbwZOi0Bx3Z+mzhcEKXPejYxNtkB2sGQCN93gj08jo8C/AadGeDXcLnBNA17Lg4HItyY22qKH6HU7cz0jqcuBS+y6F5gHYuek/Kbne5WfP1pfQ9OXQM3ck0asJ2w8lghCzJNGChSoGzviH7OGMRxex8ZXOqm4i+H+FI2vYbv56hjcPL+qOYJwjAhngMrsfxTuDbiHYMzvfr5/zH4n+n3XtR6e4AjtVxvXtvyvgyBK/aY4JopDRNAl61u4P4ud3DMC0EUen+ACxsK4zADLZwCFMpYxCAlTDa3H9f55+nuj/Xudv9GNwMfJvApsHxfmqj9/e02IeYf7ycrFY9TAuk9OV7cSW0N8Yogxj7ehFXJteHRIZ8vhV1bM5H0F5nz+18D/ieKZRr1YdHdHM890SDJPfD+ehfj+Tk5UdILrBnkZwXYy9pbsogxfW5fHutYnM9yrqduX69jQVikdtwPOgBN19E6H2814uPeLjKmuXRiuim5xLsz5ZJRN1788hIGJbHao6mOVram0B6AvHqPCCG9jjphAdyrXCbdyTr9osIvcKWlx8h7cCeRdxejJNvZp9L0+30KpseZ5+oxsIYF9ZwLH2buf7hn5kf3s+FCz9qS9eAdeuurp7W9DySqt72HvsYD+ZtV/2WkEbEEsyTJwc3ddI8Gt/ASk1X17xm41d44O7YfvvTFlBUix6xmIBimr6s0BvYjfNaFNXEGa45JONctyekKbNeW0jG+S+KZ8pM2BaMcU6MgpkyOx4Ixu8fesezjxvLThzHGsaw4WG4vkFjW3LM0WiXfh566nFF7wBNlJ/u++rYP+j7Wm1Pu/zwB/fX4YciPu/w8ZjXfu66foCktXQtAoyKmNto+r38BX5jTPj4C/zc2AQPvd/RE056P6c3wY+/mQi66dtONMFL78fUhJveL6pN8NP7MTThp/eLaDY/H/LmuyTDMhNl7vQzJLRLiTt9jEhNFrrT07DYRJk7/YzITRa609OI4GShmz1V5bfrYneq+91cStx93/q+neXfuL7vaE3KCMOuZKE7W/Vt74cqZau+7f1o5YRWjbmShe7PDENff1QpYuhLkNP6vXGnQcmHtNSmzb+MfEhzcltNOn2MV78Fhgz2l+Evkd7Q1vFe+iE+uy6l/3OdftfT/+nOSSOqMXd+sTtb99f+b17Kxv21//uXE9o26swrdWfLvscfvpSt+h5/BHNCiwadXErc3RL4FVC/JfCLoJNaMuDkUmKaDz21ba6/r8qjnP6387bWjbg+sTHjc17sYWzqG9Tqm5FuP4DvFg76fHupYnQ9etRz4DOOw5EYP+U4NaLTIa+DGfpwJJcqj4vgh2qX62X18SC6WjfHgbQ/+GCai9NUf23n+9PhWL1qhNAtT3hXhw/ciKXm91VZtqLQdsUimW86qDLcHj/qnke43r4W5ftdkZfHO+N4A00ZQgvtWVPP/RG+HUlQY2ZBm3tu9Gv++iGvH3Onsa1/q1sNQuw+rhnffLdRu3tu9+fiUByrB93vHmP/Vjccxdjd8eTmOw5b3tdDukLuI38/nHZ4D6ipKcDQv9Wt1vF1t5lvvs1Bi8f59vuq/Fg8q+7O9+MXva2zMAz9Rp2OjQKHDPRuZPm13DSlPqwPt8bzVtmYHtzI8Cgv1x92+Y+/lpv/qp6Dz/5aowysPCJONRL5WDxPyZHX4g8YgWx8IU/w++ZS1fjWiWY+IDXgaCaPKISZjZb89S79ua6rOhx7tn8eHnSqBNJW+a/ioCeJZ1vX68MP9xzRpFSBXYzlh6BWn5TmzX9TXL7tahgdj7zwjTGa7L//Y+O3+OfJ/vu7rVv8R5P9Rw/1H0/23z8evcX/YrL/xUP9J5P9948Ob/GfTvafPtT/crL/5UP9Z5P9Z4/0T5PzHz00/9Hk/EcPzX80Of/RQ/MfTc5/9ND8R5PzHz00/9Hk/EcPzX80Of/RQ/MfTc5/9ND8R5PzHz00/9Hk/EcPzX88Of/xQ/MfT85//ND8x5PzHz80//Hk/McPzX88Of/xQ/MfT85//ND8x5PzH9+V/67OmxWKfxVfv9nvw2mquGYGJF+Kr391671wTUJavJYcnrHKCHt2K1dla+m9+6Vd+MGNAc9vwuqjkQgbY0H9U531mRjUP0fO/dwW1J/cDwffHNS1+mOC+uN6+5zf/PRU7QeGdPOzU7UfGNLNT07VvjUk/13/8VjVmBkoS23BB7zp39fV9rQ52sUiPL8B1U334dzMsaDscoFBTdSLMSi7YGBQExUzFtT1B+5ufYLIwoNDu/U5IgsPDu3Wp4ks3BianwH+vKngD28oM02xB7z9f28OOdqVc/H6Jqhqarxr3HAwdq0EwUwUyXAw33xYl9uqzLfvX/LNp+o0IUPKwAbM/P8I8od8U33O6/vDlIZuClSNYdf1p/zY81U83Ul2ZR+g7/fVaT8lNfq+34QGbN38pbUjgU0YD+HApg6JrIFNGBXhwKYOjFBgvoS+r4tNUT5/v1tjPqssyuKPGCbt1hNkFDh/oy2Y7orX5MHQ7ELqC22ikuyhfVNvXorPd8UmTNwVnC+oP+4qSzpqij1CQNVhQhdycfpG1zTdANe0wVDsgtGhTBTKeCj2XKNDmZhkhkN5vz7mz1X9620PKaz9wJBue1hh7QeGdNtDC2s/IKT/Xj/f9sy8io8J5LYn5VV8TCC3PR+v4oRAfnqaFeU2/zp799vsc14fiqqcvZvx2+htgzXOH/59t2ojfJptqtfz4aNttTm5f/50LvZ/882x2VDzbtWW/sN89rSaPy2it5xmP/30tOoquwvuD52N619cRZo9regpXr6llLyKFFQkryLPnlaMPHJQkb2K0expFaGKUVAx8irGs6dVjCrGQcXYq7iYPa0WqOIiqLjwKiazp1WCKiZBxcSrmM6eVimqmAYVU6/icva0WqKKy6Di0quYzZ5WGaqYBRUzXwCNHghqh0LxkFKPkw/BykBAvoIamL4iqCEKRUS+ihoSviKoIwqFRL6SGoy9IqglCsVEvpoaBr0iqCcKBUW+ohqAvCKoKQpFRb6qGvq7IqgrCoVFvrIadLsiqC0KxUW+uhruuiKoLwoFRr7CGmi6YqgwDhXGvsIa4rliqDAOFcYqR7kkhbMUSFO+whrWuGKoMA4Vxr7CGlC4YqgwDhXGvsIayrdiqDAOFca+whpEt2KoMA4Vxr7CuNEMQ4VxqDD2FcaNZhgqjEOFsa8wbjTDUGEcKox9hUWNZiKosChUWOQrLGo0E0GFRaHCIl9hUaOZCCosChUWqZ7QdYW4LwSdoa+wqNFMBBUWhQqLfIVFjWYiqLAoVFjkKyxqNBNBhUWhwiJfYVGjmSh94vRtwqpyqLDIV1jUaCZaouFKFCos8hUWNZqJoMKiUGGRr7C40UwMFRaHCot9hcWNZmKosDhUWOwrLG40E0OFxaHCYl9hcaOZGCosDhUWq/GWG3DhERcYcvkKixe9zzkOFRb7CosbzcRQnnGosNhXWNxoJobyjEOFxb7C4kYzMUyAcaiw2FdY3GgmhvKMQ4XFvsIW894btggVtvAVtnAKy57i+O1ynviVQ4UtfIUtGs0soLYXocIWvsIWUX/YocIWvsIWjWYWcPqxCBW2UKN6N6yHL8YCDOx9hS2S/hsWKmzhK2zRaGYRwbBDhS18hS0azSzgW7UIFbbwFbZoNLPAc5lQYQtfYUmjmQWez4QKS3yFJY1mFvDFSEKFJb7CEqcwODJIQoUlvsKSRjMLmLeTUGGJr7Ck0UwCtZ2ECkt8hSWNZhKYt5NQYYmaO7rJI5RnAqaPvsKSRjMJzNtJqLDEV1jSaCaJkTyTUGGJr7Ck0UwCFZaECkt8haWNZpIEeU5DhaW+wtJGM0kKK4cKS32Fpdy3jpGGAkt9gaWNZBKozjQUWOoLLHUCy6DnUGCpL7C0kUwK1ZmGAkt9gaWNZFKozjQUWKoWKNwKBVRnCtYofIGljWRSmP/SUGCpL7C0kUwK818aCiz1BbZsJJNCdS5DgS19gS0byaQw/y1DgS19gS0bzaQp6jCWocKWvsKWjWZSODBYhgpb+gpbNppJYVe1DBW29BW2bDSznMPKocKWvsKWjWaWUGHLUGFLX2HLRjNLqLBlqLClWgZb9o4qlmAlzFfYMusdVSxDhS19hWXz3oFBFios8xWWUe/AIAsVlvkKyxrNLGHOz0KFZb7CskYzS5jzs1Bhma+wrNHMcgErhwrLfIVlTmEw52ehwjJfYZlTGMz5WaiwzFdY5hQG36osVFjmKyxzK60wb2ehwjK12OpWW+ewMlhv1QuujWgyvOg+R0uuas21OQS6yhjXB6uuc7Xs2hziXGVQpe01XV+tvDaHMFcZ1Fp7TddXi6/NIcpVBuXWXtP11fprcwhylUHFtdd0fbUE2xxiXGVQdO01XV+twjaHEFcZ1F17TddXC7HNIcJVBqXXXtP11Vrs3C3GzqH82ovagBLgecm/Z9keKDBY9afebENw3V8p0K3l44RDaOlfr/275Xyccwit/uvlf7eij9MOIQCgCYBb1MeZhxAD0BDArevj5EMIA2gO4Jb2cf4hRAI0CnCr+z0ZCMEATQPcAn9PBkI8QAEBcmv8PRkIIAFSTIDcMn9PBgJUgBQWILfS35OBABggRQbILfb3ZCDABkjBAXLr/T0ZCOABUnyA3JJ/TwYChIAUIiC36t+TgQAkIEUJyC3892UgAApIkQLi/ikqAVZAChaQW//HE00CuIAULyCHAPBckwAxIIUMyFEAmmN2CagBKWxAjgTQvIdfAgkqdECOBtAcM0xAD0jhA3JEgOaYYwKCQAohkKMCeOZLACKQogjkwACeRRLgCKRAAkWtCDFLBSyBFEwgxwdojnkq4AmkgAI5RoDnhASQAimmQA4T4GkhAapACitQ3KqwxwBQoUILFLcqxM8Q0AVSeIEcMaCe3QOAMJBCDOSoAfXsIACUgRRmIEcOqGcXASANpFADOXqAp9cEYAMp2kAOIBBhHQPgQIo4kIMIeIpOgDmQgg7kOAIRTqaAO5ACD+RYAvXtaAA6VPCBHE/As3UC+IEUfyCHFIhwOgcIghSDoEUrQ5zPAYYgxSFo0coQ744AKIIUi6BFK0O8QwLgCFI8ghxi6LuHQIYKSZCjDIR3WRCgEqSwBDnSQHinBQEyQQpNkKMNhHdbEKATpPAEJf0EjACgIEUoKKEBGQFIQYpSUMIDMgKgghSpIAcfCO8ZIQArSNEKcgCCGL/LAFiQIhbkIAThvSMEoAUpakHJgA4BtyAFLihJh1oAdKjgBSWtDnu2SAEdKoBBSatD3K0DhkEKYlA6oEOAMUhxDHJogvBGGAIogxTLoBZm9PTrgGeQAhqUtjrE2QgwDVJQgxyn6BnZAKxBimuQQxV9AwuANkixDUoHemVAN0jhDUqHemVAOEghDkoHemUAOUhRDkqHemUAOkiRDnLwgvCmJgKwgxTtoOVArwx4ByngQY5h4N0MBJAHKeZBy3aHJu5QAPYgxT3IoQzCm6sIoA9S7IMcziC8wYoA/iDFP8ghDcKbrAggEFIMhBzWILzRigAGIcVByKENwputCKAQUiyEHN6gCGdDgENI8RDKWh3ibAaQCCkmQg5zEN54RQCLkOIi5FAH4c1XBNAIKTZCDncQ3oBFAI+Q4iOUtfuFsRIBIiHFSMhhD8IbsQhgElKchBz6ILwZiwAqIcVKyOEPwnuqCOASUryEHAIhvK+KADIhxUzIYRDCe6sIYBNS3IQdB6EYbzAF4IQVOGEHQijGm0wBOWFFTtiREMJbpRigE1bohB0KoQXezgzYCSt2wo6FEN71xACesIInPG93r+NtzYCesKIn7GgI4Q1MDPAJK3zCDocQ3sTEgJ+w4ifseAjhjUwMAAorgMItQMGbmRgAFFYAhVuAgjc0MQAorAAKt+cm8KYmBgSFFUHh9uwE3tjEAKGwQijcnp/Am5sYMBRWDIXbMxR4gxMDiMIKonB7jgJvcmJAUVhRFD6fpcBKBBiFFUZh6t+LzACjsMIo3B6pwJulGHAUVhyFHRfB+5kZcBTWByscF6EEvwnobIU+XOHACCX4TUDnK4IDFq0O8ZsAz1goHXKrQ/wmoHMW+qCFYyOEN1ExOmuhD1s4OEJ4IxWj8xb6wAX3T5kZHbnQZy5amoI3YzE6dqHPXXD/bhlGJy/00QtHRyjFbyI6faFwCkf9M2YGOIUVTuEWp+BNYQxwCiucwu0xjJ4AgAoVTeGWpuCNZQxoCiuawu1pjJ4AgAgVTOGoFSF+kwFNYUVT2NERSvGbDHAKK5zCLU5J8ZsMcAornMItTknxmwxwCiucwu0BjZ57CFSocAo7PEJLnAkAT2HFU7jlKXjDGwOewoqncDwgQ4BTWOEUbnEK3jTHAKewwinc4hS8iYwBTmGFU7jFKcueg2tAhwqnsMMjtMRvEuAprHgKtzxlid8EwFNY8RSO24OO+E0AQIUVUOEWqCzxmwCACiugwi1QWeI3AQAVVkCFHSChDCsZEBVWRIVbopJhJQOiwoqocEtUMqxEQFRYERVuiUqGlQiICiuiwi1RybASAVFhRVTYERLKeo5RAiUqpMItUsmwEgFSYYVUuEUqGVYiQCqskAq3SCXDSgRIhRVSYYdIKMNKBEyFFVNhh0h4jpUImAorpsIOkTDea8aAqbBiKuwQCeOtFgyYCiumwg6RMN5qwYCpsGIq7BAJ460WDJgKK6bCjpEw3mrBAKqwgirsGAnjrQ4MoAorqMKOkTDe6sAAqrCCKuwYCc+xEgFUYQVVOG0PgWMlAqrCiqpw2h4Ex0oEVIUVVWEHSRhvNWBAVVhRFXaQhPFWAwZUhRVVYUdJGH+0gAFWYYVV2FESxh8uYIBVWGEVdpiEMepnwFVYcRV2mITxBwwYcBVWXIUdJ2H8EQMGYIUVWGHHSRijegZghRVYYcdJGKN6BmCFFVhhB0q454MGgKywIiu8bD9LgJUI0AortMKOlHDPhw0AWmGFVtiREu75uAFAK6zQCjtSwj0fOABohRVaYUdKuOcjBwCtsEIr7EgJ93zoAKAVVmiFHSnhno8dALTCCq2wIyXc98EDoESFVtiREu776AFQokIr7EgJY0bIAK2wQivsSAljRscArbBCK5y1H8nASgRohRVaYUdKGDM6BmiFFVphR0oYMzoGaIUVWmFHShgzOgZohRVaYUdKGDM6BmiFFVphR0oYMzoGaIUVWmFHShgzOgZohRVaiRwp4Z6PIwC0Eim0EjlSwpjRRQCtRAqtRI6UMGZ0EUArkUIrkSMljBldBNBKpNBKNG8/2YI/xwHQSqTQSuRICWNGFwG0Eim0EjlSwpjRRQCtRAqtRI6UMGZ0EUAr3d/ch8k+5/Ux337XfqBstep+IO+32c/nj5Y1CxPOUfP9smbp4d1vv/9+/UzZu99+F18qa641vs4/WietRNJKbLbCvpVYWlmYrUS+lYW0kpitxL6VRFpJzVYWvpVUWlmarSS+laW0kpmtpL6VTFhZzM1Wlp6VbC6tkNlK5lshaYWtVsjXbia1uzBrl3ztZlK7C7N2ydduJrW7MGuXfO1mUrsLs3bJ124mtbswa5d87WZSuwuzdsnXbia1uzBrl3ztZlK7iVm75Gm3WfIQVszapcy3IrWbmLXLc9+K1G5i1i6Tb0VqNzFrl9m3IrWbmLXLkW9Fajcxa5dj34rUbmLWLi98K1K7iVm7nPhWpHYTm3a7n7K9molELFHaVms7t6dZm4afZm3yepotl+3/qc2szT+S8z+SefeP7i9p95fluXADs9t/cNL942ywwXPnf3D3j7j7R3b+R2wT888fi93R/SyTeENJvBVkHAH8vC/2+a4oc98SS0s2Nf7cfXpV3nehx8hmZr19Lcr2t9ir7uewRWSpfPPZaPLjMa831an55qswFYkXt9miaDb1sXC/VuAZi2V2nNvymjP2nKuoYhmV7RXuDK3LbZ2/Vp/90BYyKcxtr7Nncb9bb5RJmfPmE25da/LkPjfsW5TPdW7Lxc5iUW7q3P3ZMyeTz9yWNs7mDnmtnshCPpEJ9+/1tDsW+92vfmRSKDRBKPvTbudZSuRDoAkPYX86vPiW5M2nCTf/l1Ne+82LEnmzbFnfmQLClVm72T87wVYo2VgayyYYA2KNltKWbSi0fn6u82dlSQyGjFbKrawvZ2vZuf8iq6i8xBoLCcRWA5u83LpfVbnaSURESdeTslEHn31T4r0z9vwf8o9VnYNUn8kHZmteawvmejkmmdseXGtNJ/t4LuOydf0XSz3ZXg4k57b+wzcJ0r0cKM9tAwtpE+V7OQ+YT2l5T8KXY9a5TW+dvSDjy5eBMls+bI3hlC/1QlP0EuZ8+SRoypMIk758AjTlCYRZP5bjxWxKWEDBsRyXZbZ+rTMG8r7skLIpwgCqjeXgLLO+XMcvee4Ni+XyWBZ3adv4CE67T6FkxS0zyvVips4Pp52vf5IjHuMyVWMwfJjifhlVcTGD4pJp3LjA0xg8rP2oRPOmGQExsZS+cXGlMReqSwjVqKyLGRSXfIbGBYXNevOSH/LzTwv64wNhzrig207inM26+QVifx4nu6nI1iNvLr96WHijIPmKG5eJr6ZcRfkmiWQddTN3sj3XTVV+LJ79dsrhfmRLP60ZOPuVPUlkbWpZtk2FFmUvwLZ3dFOVx3XhG5Jr9dmiS2m2XLSpHCQptp5FOcdcnBdVjEO3i8FjVX34n6bxW7/rk6/GVIuHY63GvH66NNpTQ9RExHRZVGKr7E7lsTjmr/4bK+6fESpt3O8RXd8NaU5Mn4y6O9XNj/bs189+HyrXBcn6RpwOx+oV6TeR7xfbmrn9oF96knON5jiJyYz7rSJ8t8R7ajUGZ1PyrnfLkZGtQ94Wh2NRbvxeQS55kzHt5uX6wy4//FpudtWzyh5ymSWyvUp5uT18KY7eYFQyuqxbWzUOb/NfPEvizmdpZ8n2UuZ1Xakl1aVMj7HtxrdmdsXBv/VLuchinGK7H6UKeigh+ciWIc6/geUpQY6yI9vYs1109swII22l1GrKX8kgCUCaj2pZjajJubg3xluzOx1e3CDFD0dEY3vsbi75XLpb7eVhYcqoID9jSmicdUsqxn5LLzjI52WTYO9Cg8wktmfev8DgaXGCLTRFk+TCZqquTnsv64ockhrD8W6zBPRZ1j0xW7f5sj6s/Um/BPU078YGxk7qZX0o869edHJIuuxmn/GFaNmE9bI+7Ov8sy8u+RjjLvmm5nYfKj/Ryb0FNO+6P+NY/KWqPjUDVdV4kguUbHyLGlvhkEGOxdn2BjSG/DQcyTYubYJt+Nhpq/KVHINGk+y0Q2SvhxGDUWOvjlfl5FDPJgOwGicX222ib43kfgfjjfoXZwJLxi0IncVmlK3mKeS1sqPIZNyVIA0rs7Ldxj0x3YrOx7zOS5VjvSVwm7VD/ro/eit9cqMOzbubyLZurTiU1TE0KTP2vLt9bHwuh0tjvafN3vKyzdQxf/X6bXH/4y6JL41P1cnEsyZEYtyEFkhCjraX3b3vdg4034qyWN35KVamjXnXGRiz2S4vn/1xvFyfWHaP0jjo2RWvhT8lFopNu9CMuwia0fd1ZubdRfZgp82a37vLRDLv1jqMc8/X9Vfvhgmdpramva6/hnN90YsYd5C85q8f8npgo4UkDWxL3q+Fv2Qg7rRxWIH4CXuRmMyU62PhD1dZwnLbo1ITC5nl591oybhipodgclq57BQUd2P71JYfysqXpewv5t3oy7hFpirXH9ZuMrXdvOSbT9XpuOl+nFo+CTnfN9J9YLpBtM1iujIuB1RG3F+VH9bb5xzFKmcCbHy3zua23a9QS3MSMBrHtGdzp+7XtaU5ueBkfDmqcnP+BXPUXgktIyNRvVpETZYriJER+14tolbLPBUZd/lU5TWRQ03KfG7sn6VN1HIvWxhH5dImaruXxpb2tp/2uN2RHOIYF8Y7e6jNcibCRujd2UPtjeRzMe5tqMqP62J3qtWUXg5SMuurd+7gUGaQkRnXqzp7MDVItRiHYZ09mBvkkzAuQFRlVW9xcyUJZOOOwrM5qGOZCI3b4apyv1uX63rzUnzWMpHmzA+3MQdfCpn0jXsHWmtQwvLOGXF/Ve6rA+w8JVlk4w601hpMznOZSq2vf2MNtlSuKRu3NFXlvq62pw1srJy9NkeFJxmEKVmmu6W5va1B+JZJcrS0Nvm4foadbyzvn1V3x/UzfLRy+cW4YdcZg12u3IJt3A9WeWv88uAHzS97+qymtooYyGEepbYH2UDEQ/G/aslLGoq75UFjPkfb3iWhMebJzszhqDCn3KhlPIjh2fqUe3MguUPXuJkjXJ71lsYvywa2d1PvRGPvnKDRhL8DzeuXbG0Kdp7JntJuoWfNyDv2YrJW5+vtpio3ee1NfOVhgO4USXeIxLhd+BqiXpeS+/aMx42csWbXgrYlN6MbD0CFJMibFNqyVWvEX5+VCZ6MJ+bOdtCqrJwWkPHoXLfxDCyf+pNTo7WmAt4fwLLzsb2BgJtF3hhgipVLG72bJofsxs0B9clf8hEvpHGSrDfnyXmIkUs3JsAKupzFkVHePbv85J0m42Dh8KnwsGIqwkm7btSIVw6VD0XkeMN40ivoolKRf61NOq7rY7B7g+YeLe96FmMuOJy8lCQ3Bhr7psNpv6/zw2F98uOSZ4oW5wVk47pSZzIAdnIrvHEHarNzZrs+rj+sD/4AJpUdjnFhuzH2pfi63u/9Re25TCip7Wkeq+N6Fx6WkKYo7jiDcQXN2XTDNX/sIRVi3I12rE9uMclLDnJJymYm3CMgJ4lGVApG1iSHQJR0gMJ4XvZsECQtb3Oirac57c8Q0Q9QpnMyrjidDnmtd0tKmRphWmOmrvSmX7mdjY3s5XNxKI7VEKiQqy/GvvRLXnxuXkl4KEueqzM+zcBe2IXIl904fQ2tntAeXvn5EON6TGcZbViWo3LjK9aZC/eHyY+SGNcAO2OgpfLQkPGLFcpasNFLHpcyjhKVyepL6bdZnj01bi9QJoNpjjzdZPz0gjL5pS58lctTwcZjC8hk2HoxUDbugejshueK5t5+tWkNVzM9lDrkzIWMRwx7zKMTGLIfNS7jKuuBTflpBjIeAPhSfP2wq7y3nOXRWjaeivpSfM03lTdYY28ZwHiG/kvx9XVdf8qParMz+5Mi49MuvrYL6Yrxy9VC4yLTl+Lrvi42RfncrAf79uS4g40H3L4UXw/Hqs5VZN5s2XDnf3q6Lla9W/30++//D4BAtVPSJAEA"; \ No newline at end of file diff --git a/docs/functions/Hooks.afterCount.html b/docs/functions/Hooks.afterCount.html index 301fd666..6f47de22 100644 --- a/docs/functions/Hooks.afterCount.html +++ b/docs/functions/Hooks.afterCount.html @@ -3,10 +3,10 @@
  • context: HookContext

    Contextual information about the hook.

  • Returns Promise<number> | number

    The count to return to count() instead of the original count. Returning a rejected promise will not block the operation, but will return a rejected promise to the caller as well as trigger the onFailure() hook.

    A hook that is triggered after a count() operation.

    -