From ba2de6906aae0e5850747fe72528d186ec8b14af Mon Sep 17 00:00:00 2001 From: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> Date: Thu, 14 Oct 2021 17:00:27 +0200 Subject: [PATCH] Feat:contentful plugin archive on delete * extend plugin with methods for archival in contentful * add events to services * rename entities * eventbusservice to delete * Update packages/medusa-plugin-contentful/src/services/contentful.js Co-authored-by: Sebastian Rindom * Update packages/medusa-plugin-contentful/src/services/contentful.js Co-authored-by: Sebastian Rindom * Update packages/medusa-plugin-contentful/src/services/contentful.js Co-authored-by: Sebastian Rindom * testing * adjust options * Update packages/medusa-plugin-contentful/src/services/contentful.js Co-authored-by: Sebastian Rindom * adjust options * Update packages/medusa-plugin-contentful/src/services/contentful.js Co-authored-by: Sebastian Rindom * Update packages/medusa-plugin-contentful/src/services/contentful.js Co-authored-by: Sebastian Rindom Co-authored-by: Sebastian Rindom --- .../__mocks__/contentful-management.js | 10 + .../__mocks__/contentful-management.js | 6 + .../src/services/__tests__/contentful.js | 179 ++++++++++++++++++ .../src/services/contentful.js | 94 ++++++++- .../src/subscribers/contentful.js | 12 ++ .../medusa/src/services/__tests__/product.js | 1 + .../medusa/src/services/product-variant.js | 7 + packages/medusa/src/services/product.js | 7 + packages/medusa/src/services/region.js | 7 + 9 files changed, 319 insertions(+), 4 deletions(-) create mode 100644 packages/medusa-plugin-contentful/__mocks__/contentful-management.js create mode 100644 packages/medusa-plugin-contentful/src/services/__mocks__/contentful-management.js create mode 100644 packages/medusa-plugin-contentful/src/services/__tests__/contentful.js diff --git a/packages/medusa-plugin-contentful/__mocks__/contentful-management.js b/packages/medusa-plugin-contentful/__mocks__/contentful-management.js new file mode 100644 index 0000000000..3b953c0469 --- /dev/null +++ b/packages/medusa-plugin-contentful/__mocks__/contentful-management.js @@ -0,0 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ContentfulMock = void 0; +var ContentfulMock = { + createClient: jest.fn() +}; +exports.ContentfulMock = ContentfulMock; \ No newline at end of file diff --git a/packages/medusa-plugin-contentful/src/services/__mocks__/contentful-management.js b/packages/medusa-plugin-contentful/src/services/__mocks__/contentful-management.js new file mode 100644 index 0000000000..8100c741d1 --- /dev/null +++ b/packages/medusa-plugin-contentful/src/services/__mocks__/contentful-management.js @@ -0,0 +1,6 @@ +export const createClient = jest.fn() +const mock = jest.fn().mockImplementation(() => { + return { createClient } +}) + +export default mock diff --git a/packages/medusa-plugin-contentful/src/services/__tests__/contentful.js b/packages/medusa-plugin-contentful/src/services/__tests__/contentful.js new file mode 100644 index 0000000000..1d0fd3f3cf --- /dev/null +++ b/packages/medusa-plugin-contentful/src/services/__tests__/contentful.js @@ -0,0 +1,179 @@ +import ContentfulService from "../contentful" + +describe("ContentfulService", () => { + describe("delete in medusa", () => { + const regionService = { + retrieve: jest.fn((id) => { + if (id === "exists") { + return Promise.resolve({ id: "exists" }) + } + return Promise.resolve(undefined) + }), + } + const productService = { + retrieve: jest.fn((id) => { + if (id === "exists") { + return Promise.resolve({ id: "exists" }) + } + return Promise.resolve(undefined) + }), + } + const redisClient = { + get: async (id) => { + // const key = `${id}_ignore_${side}` + if (id === `ignored_ignore_contentful`) { + return { id } + } + return undefined + }, + set: async (id) => { + return undefined + }, + } + const productVariantService = { + retrieve: jest.fn((id) => { + if (id === "exists") { + return Promise.resolve({ id: "exists" }) + } + return Promise.resolve(undefined) + }), + } + const eventBusService = {} + + const service = new ContentfulService( + { + regionService, + productService, + redisClient, + productVariantService, + eventBusService, + }, + { + space_id: "test_id", + environment: "master", + access_token: "test_token", + } + ) + + const entry = { + unpublish: jest.fn(async () => { + return { + id: "id", + } + }), + archive: jest.fn(async () => { + return { + id: "id", + } + }), + } + + service.contentful_ = { + getSpace: async (space_id) => { + return { + getEnvironment: async (env) => { + return { + getEntry: async (id) => { + if (id === "onlyMedusa") { + throw new Error("doesn't exist") + } + return entry + }, + } + }, + } + }, + } + + beforeEach(() => { + jest.clearAllMocks() + }) + + describe("archiveProductInContentful", () => { + it("Calls entry.unpublish and entry.archive", async () => { + await service.archiveProductInContentful({ id: "test" }) + + expect(entry.unpublish).toHaveBeenCalledTimes(1) + expect(entry.archive).toHaveBeenCalledTimes(1) + }) + + it("Doesn't call entry.unpublish and entry.archive if the product still exists in medusa", async () => { + await service.archiveProductInContentful({ id: "exists" }) + + expect(entry.unpublish).toHaveBeenCalledTimes(0) + expect(entry.archive).toHaveBeenCalledTimes(0) + }) + + it("Doesn't call productService if request should be ignored", async () => { + await service.archiveProductInContentful({ id: "ignored" }) + + expect(productService.retrieve).toHaveBeenCalledTimes(0) + expect(entry.unpublish).toHaveBeenCalledTimes(0) + expect(entry.archive).toHaveBeenCalledTimes(0) + }) + }) + + describe("archiveProductVariantInContentful", () => { + it("Calls entry.unpublish and entry.archive", async () => { + await service.archiveProductVariantInContentful({ id: "test" }) + + expect(entry.unpublish).toHaveBeenCalledTimes(1) + expect(entry.archive).toHaveBeenCalledTimes(1) + }) + + it("Doesn't call entry.unpublish and entry.archive if the variant still exists in medusa", async () => { + await service.archiveProductVariantInContentful({ id: "exists" }) + + expect(entry.unpublish).toHaveBeenCalledTimes(0) + expect(entry.archive).toHaveBeenCalledTimes(0) + }) + + it("Doesn't call productVariantService if request should be ignored", async () => { + await service.archiveProductVariantInContentful({ id: "ignored" }) + + expect(productVariantService.retrieve).toHaveBeenCalledTimes(0) + expect(entry.unpublish).toHaveBeenCalledTimes(0) + expect(entry.archive).toHaveBeenCalledTimes(0) + }) + }) + + describe("archiveRegionInContentful", () => { + it("Calls entry.unpublish and entry.archive", async () => { + await service.archiveRegionInContentful({ id: "test" }) + + expect(entry.unpublish).toHaveBeenCalledTimes(1) + expect(entry.archive).toHaveBeenCalledTimes(1) + }) + + it("Doesn't call entry.unpublish and entry.archive if the region still exists in medusa", async () => { + await service.archiveRegionInContentful({ id: "exists" }) + + expect(entry.unpublish).toHaveBeenCalledTimes(0) + expect(entry.archive).toHaveBeenCalledTimes(0) + }) + + it("Doesn't call RegionService if request should be ignored", async () => { + await service.archiveRegionInContentful({ id: "ignored" }) + + expect(regionService.retrieve).toHaveBeenCalledTimes(0) + expect(entry.unpublish).toHaveBeenCalledTimes(0) + expect(entry.archive).toHaveBeenCalledTimes(0) + }) + }) + + describe("archiveEntryWidthId", () => { + it("Calls archive if entry exists", async () => { + await service.archiveEntryWidthId("exists") + + expect(entry.unpublish).toHaveBeenCalledTimes(1) + expect(entry.archive).toHaveBeenCalledTimes(1) + }) + it("Doesnt call archive if entry doesn't exists", async () => { + await service.archiveEntryWidthId("onlyMedusa") + + expect(entry.unpublish).toHaveBeenCalledTimes(0) + expect(entry.archive).toHaveBeenCalledTimes(0) + }) + }) + }) +}) diff --git a/packages/medusa-plugin-contentful/src/services/contentful.js b/packages/medusa-plugin-contentful/src/services/contentful.js index 8e185187c9..210abfd082 100644 --- a/packages/medusa-plugin-contentful/src/services/contentful.js +++ b/packages/medusa-plugin-contentful/src/services/contentful.js @@ -91,7 +91,7 @@ class ContentfulService extends BaseService { async createImageAssets(product) { const environment = await this.getContentfulEnvironment_() - let assets = [] + const assets = [] await Promise.all( product.images .filter((image) => image.url !== product.thumbnail) @@ -646,6 +646,92 @@ class ContentfulService extends BaseService { } } + async archiveProductVariantInContentful(variant) { + let variantEntity + try { + const ignore = await this.shouldIgnore_(variant.id, "contentful") + if (ignore) { + return Promise.resolve() + } + + try { + variantEntity = await this.productVariantService_.retrieve(variant.id) + } catch (err) { + // ignore + } + + if (variantEntity) { + return Promise.resolve() + } + + return await this.archiveEntryWidthId(variant.id) + } catch (error) { + throw error + } + } + + async archiveProductInContentful(product) { + let productEntity + try { + const ignore = await this.shouldIgnore_(product.id, "contentful") + if (ignore) { + return Promise.resolve() + } + + try { + productEntity = await this.productService_.retrieve(product.id) + } catch (err) {} + + if (productEntity) { + return Promise.resolve() + } + + return await this.archiveEntryWidthId(product.id) + } catch (error) { + throw error + } + } + + async archiveRegionInContentful(region) { + let regionEntity + try { + const ignore = await this.shouldIgnore_(region.id, "contentful") + if (ignore) { + return Promise.resolve() + } + + try { + regionEntity = await this.regionService_.retrieve(region.id) + } catch (err) {} + + if (regionEntity) { + return Promise.resolve() + } + + return await this.archiveEntryWidthId(region.id) + } catch (error) { + throw error + } + } + + async archiveEntryWidthId(id) { + const environment = await this.getContentfulEnvironment_() + // check if product exists + let entry = undefined + try { + entry = await environment.getEntry(id) + } catch (error) { + return Promise.resolve() + } + + const unpublishEntry = await entry.unpublish() + const archivedEntry = await entry.archive() + + await this.addIgnore_(id, "medusa") + + return archivedEntry + } + async sendContentfulProductToAdmin(productId) { const ignore = await this.shouldIgnore_(productId, "medusa") if (ignore) { @@ -658,7 +744,7 @@ class ContentfulService extends BaseService { const product = await this.productService_.retrieve(productId) - let update = {} + const update = {} const title = productEntry.fields[this.getCustomField("title", "product")]["en-US"] @@ -741,9 +827,9 @@ class ContentfulService extends BaseService { isArray = false } - let output = [] + const output = [] for (const obj of input) { - let transformed = Object.assign({}, obj) + const transformed = Object.assign({}, obj) transformed.medusaId = obj.id output.push(transformed) } diff --git a/packages/medusa-plugin-contentful/src/subscribers/contentful.js b/packages/medusa-plugin-contentful/src/subscribers/contentful.js index 6431ea60c4..3f959e3eec 100644 --- a/packages/medusa-plugin-contentful/src/subscribers/contentful.js +++ b/packages/medusa-plugin-contentful/src/subscribers/contentful.js @@ -18,10 +18,18 @@ class ContentfulSubscriber { await this.contentfulService_.updateRegionInContentful(data) }) + this.eventBus_.subscribe("region.deleted", async (data) => { + await this.contentfulService_.updateRegionInContentful(data) + }) + this.eventBus_.subscribe("product-variant.updated", async (data) => { await this.contentfulService_.updateProductVariantInContentful(data) }) + this.eventBus_.subscribe("product-variant.deleted", async (data) => { + await this.contentfulService_.archiveProductVariantInContentful(data) + }) + this.eventBus_.subscribe("product.updated", async (data) => { await this.contentfulService_.updateProductInContentful(data) }) @@ -29,6 +37,10 @@ class ContentfulSubscriber { this.eventBus_.subscribe("product.created", async (data) => { await this.contentfulService_.createProductInContentful(data) }) + + this.eventBus_.subscribe("product.deleted", async (data) => { + await this.contentfulService_.archiveProductInContentful(data) + }) } } diff --git a/packages/medusa/src/services/__tests__/product.js b/packages/medusa/src/services/__tests__/product.js index af1c0ae7cb..c6932fe9cd 100644 --- a/packages/medusa/src/services/__tests__/product.js +++ b/packages/medusa/src/services/__tests__/product.js @@ -415,6 +415,7 @@ describe("ProductService", () => { const productService = new ProductService({ manager: MockManager, + eventBusService, productRepository, }) diff --git a/packages/medusa/src/services/product-variant.js b/packages/medusa/src/services/product-variant.js index 055efbde95..f0750a3661 100644 --- a/packages/medusa/src/services/product-variant.js +++ b/packages/medusa/src/services/product-variant.js @@ -11,6 +11,7 @@ class ProductVariantService extends BaseService { static Events = { UPDATED: "product-variant.updated", CREATED: "product-variant.created", + DELETED: "product-variant.deleted", } /** @param { productVariantModel: (ProductVariantModel) } */ @@ -588,6 +589,12 @@ class ProductVariantService extends BaseService { await variantRepo.softRemove(variant) + await this.eventBus_ + .withTransaction(manager) + .emit(ProductVariantService.Events.DELETED, { + id: variantId, + }) + return Promise.resolve() }) } diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index d46a5968c5..7381cea269 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -11,6 +11,7 @@ class ProductService extends BaseService { static Events = { UPDATED: "product.updated", CREATED: "product.created", + DELETED: "product.deleted", } constructor({ @@ -475,6 +476,12 @@ class ProductService extends BaseService { await productRepo.softRemove(product) + await this.eventBus_ + .withTransaction(manager) + .emit(ProductService.Events.DELETED, { + id: productId, + }) + return Promise.resolve() }) } diff --git a/packages/medusa/src/services/region.js b/packages/medusa/src/services/region.js index 44d611ca0e..8c22ef735f 100644 --- a/packages/medusa/src/services/region.js +++ b/packages/medusa/src/services/region.js @@ -10,6 +10,7 @@ class RegionService extends BaseService { static Events = { UPDATED: "region.updated", CREATED: "region.created", + DELETED: "region.deleted", } constructor({ @@ -389,6 +390,12 @@ class RegionService extends BaseService { await regionRepo.softRemove(region) + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.DELETED, { + id: regionId, + }) + return Promise.resolve() }) }