diff --git a/packages/medusa-plugin-contentful/__mocks__/contentful-management.js b/packages/medusa-plugin-contentful/__mocks__/contentful-management.js index f988c3f87b..c613964699 100644 --- a/packages/medusa-plugin-contentful/__mocks__/contentful-management.js +++ b/packages/medusa-plugin-contentful/__mocks__/contentful-management.js @@ -1,7 +1,26 @@ -module.exports = { - mockClient: { - - }, - createClient: () => { - } +export const MockEntry = { + update: jest.fn(() => Promise.resolve(MockEntry)), + publish: jest.fn(() => Promise.resolve({ sys: { id: "test" } })), } + +export const MockAsset = { + processForAllLocales: jest.fn(() => Promise.resolve(MockAsset)), + publish: jest.fn(() => Promise.resolve({ sys: { id: "test" } })), +} + +export const MockEnvironment = { + createAsset: jest.fn((d) => Promise.resolve(MockAsset)), + createEntryWithId: jest.fn(() => Promise.resolve(MockEntry)), + getEntry: jest.fn(() => Promise.resolve(MockEntry)), + getContentType: jest.fn(() => Promise.resolve({})), +} + +export const MockSpace = { + getEnvironment: jest.fn(() => Promise.resolve(MockEnvironment)), +} + +export const MockClient = { + getSpace: jest.fn(() => Promise.resolve(MockSpace)), +} + +export const createClient = jest.fn(() => MockClient) diff --git a/packages/medusa-plugin-contentful/src/services/__tests__/contentful.js b/packages/medusa-plugin-contentful/src/services/__tests__/contentful.js index a684753239..881fa4c54c 100644 --- a/packages/medusa-plugin-contentful/src/services/__tests__/contentful.js +++ b/packages/medusa-plugin-contentful/src/services/__tests__/contentful.js @@ -1,9 +1,97 @@ +import ContentfulService from "../contentful" +import { MockEnvironment } from "../../../__mocks__/contentful-management" + +jest.mock("contentful-management") + describe("contentfulService", () => { const redisClient = { set: jest.fn(), - get: jest.fn() + get: jest.fn(), } - describe("", () => { + const ProductVariantService = { + retrieve: jest.fn(() => + Promise.resolve({ + id: "variant_medusa", + title: "testvar", + }) + ), + } + + const ProductService = { + retrieve: jest.fn(() => + Promise.resolve({ + id: "product_medusa", + title: "test", + subtitle: "subtest", + description: "something long", + variants: [ + { + id: "variant_medusa", + title: "testvar", + }, + ], + }) + ), + } + + describe("createProductInContentful", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("resolves", async () => { + const contentfulService = new ContentfulService( + { + redisClient, + productVariantService: ProductVariantService, + productService: ProductService, + }, + {} + ) + + await contentfulService.updateProductInContentful({ + id: "testId", + fields: ["title"], + }) + }) + }) + describe("createImageAssets", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("successfully creates assets", async () => { + const contentfulService = new ContentfulService( + { + redisClient, + }, + {} + ) + + await contentfulService.createImageAssets({ + title: "testprod", + images: [{ url: "test123" }], + }) + + expect(MockEnvironment.createAsset).toHaveBeenCalledTimes(1) + expect(MockEnvironment.createAsset).toHaveBeenCalledWith({ + fields: { + title: { + "en-US": `testprod - 0`, + }, + description: { + "en-US": "", + }, + file: { + "en-US": { + contentType: "image/xyz", + fileName: "test123", + upload: "test123", + }, + }, + }, + }) + }) }) }) diff --git a/packages/medusa-plugin-contentful/src/services/contentful.js b/packages/medusa-plugin-contentful/src/services/contentful.js index 55dfacc40f..38f7b4edb3 100644 --- a/packages/medusa-plugin-contentful/src/services/contentful.js +++ b/packages/medusa-plugin-contentful/src/services/contentful.js @@ -2,7 +2,7 @@ import _ from "lodash" import { BaseService } from "medusa-interfaces" import { createClient } from "contentful-management" -const IGNORE_THRESHOLD = 1 // seconds +const IGNORE_THRESHOLD = 2 // seconds class ContentfulService extends BaseService { constructor( @@ -28,7 +28,12 @@ class ContentfulService extends BaseService { async addIgnore_(id, side) { const key = `${id}_ignore_${side}` - return await this.redis_.set(key, 1, "EX", IGNORE_THRESHOLD) + return await this.redis_.set( + key, + 1, + "EX", + this.options_.ignore_threshold || IGNORE_THRESHOLD + ) } async shouldIgnore_(id, side) { @@ -293,6 +298,114 @@ class ContentfulService extends BaseService { } } + async createRegionInContentful(region) { + const hasType = await this.getType("region") + .then(() => true) + .catch(() => false) + if (!hasType) { + return + } + try { + const r = await this.regionService_.retrieve(data.id, { + relations: ["countries", "payment_providers", "fulfillment_providers"], + }) + + const environment = await this.getContentfulEnvironment_() + + const fields = { + [this.getCustomField("name", "region")]: { + "en-US": r.name, + }, + [this.getCustomField("countries", "region")]: { + "en-US": r.countries, + }, + [this.getCustomField("payment_providers", "region")]: { + "en-US": r.payment_providers, + }, + [this.getCustomField("fulfillment_providers", "region")]: { + "en-US": r.fulfillment_providers, + }, + } + + const result = await environment.createEntryWithId("region", p.id, { + fields, + }) + + return result + } catch (error) { + throw error + } + } + + async updateRegionInContentful(data) { + const hasType = await this.getType("region") + .then(() => true) + .catch(() => false) + if (!hasType) { + return + } + + const updateFields = [ + "name", + "currency_code", + "countries", + "payment_providers", + "fulfillment_providers", + ] + + const found = data.fields.find((f) => updateFields.includes(f)) + if (!found) { + return + } + + try { + const ignore = await this.shouldIgnore_(data.id, "contentful") + if (ignore) { + return + } + + const r = await this.regionService_.retrieve(data.id, { + relations: ["countries", "payment_providers", "fulfillment_providers"], + }) + + const environment = await this.getContentfulEnvironment_() + // check if product exists + let regionEntry = undefined + try { + regionEntry = await environment.getEntry(data.id) + } catch (error) { + return this.createRegionInContentful(r) + } + + const regionEntryFields = { + ...regionEntry.fields, + [this.getCustomField("name", "region")]: { + "en-US": r.name, + }, + [this.getCustomField("countries", "region")]: { + "en-US": r.countries, + }, + [this.getCustomField("payment_providers", "region")]: { + "en-US": r.payment_providers, + }, + [this.getCustomField("fulfillment_providers", "region")]: { + "en-US": r.fulfillment_providers, + }, + } + + regionEntry.fields = regionEntryFields + + const updatedEntry = await regionEntry.update() + const publishedEntry = await updatedEntry.publish() + + await this.addIgnore_(data.id, "medusa") + + return publishedEntry + } catch (error) { + throw error + } + } + async updateProductInContentful(data) { const updateFields = [ "variants", diff --git a/packages/medusa/src/services/region.js b/packages/medusa/src/services/region.js index cec4c6690a..33a56ff3c7 100644 --- a/packages/medusa/src/services/region.js +++ b/packages/medusa/src/services/region.js @@ -8,11 +8,17 @@ import { countries } from "../utils/countries" * @implements BaseService */ class RegionService extends BaseService { + static Events = { + UPDATED: "region.updated", + CREATED: "region.created", + } + constructor({ manager, regionRepository, countryRepository, storeService, + eventBusService, currencyRepository, paymentProviderRepository, fulfillmentProviderRepository, @@ -33,6 +39,9 @@ class RegionService extends BaseService { /** @private @const {StoreService} */ this.storeService_ = storeService + /** @private @const {EventBus} */ + this.eventBus_ = eventBusService + /** @private @const {CurrencyRepository} */ this.currencyRepository_ = currencyRepository @@ -60,6 +69,7 @@ class RegionService extends BaseService { currencyRepository: this.currencyRepository_, countryRepository: this.countryRepository_, storeService: this.storeService_, + eventBusService: this.eventBus_, paymentProviderRepository: this.paymentProviderRepository_, paymentProviderService: this.paymentProviderService_, fulfillmentProviderRepository: this.fulfillmentProviderRepository_, @@ -117,6 +127,13 @@ class RegionService extends BaseService { const created = regionRepository.create(regionObject) const result = await regionRepository.save(created) + + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.CREATED, { + id: result.id, + }) + return result }) } @@ -168,6 +185,14 @@ class RegionService extends BaseService { } const result = await regionRepository.save(region) + + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.UPDATED, { + id: result.id, + fields: Object.keys(update), + }) + return result }) } @@ -390,6 +415,14 @@ class RegionService extends BaseService { region.countries = [...(region.countries || []), country] const updated = await regionRepo.save(region) + + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.UPDATED, { + id: updated.id, + fields: ["countries"], + }) + return updated }) } @@ -419,6 +452,12 @@ class RegionService extends BaseService { ) const updated = await regionRepo.save(region) + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.UPDATED, { + id: updated.id, + fields: ["countries"], + }) return updated }) } @@ -458,6 +497,14 @@ class RegionService extends BaseService { region.payment_providers = [...region.payment_providers, pp] const updated = await regionRepo.save(region) + + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.UPDATED, { + id: updated.id, + fields: ["payment_providers"], + }) + return updated }) } @@ -497,6 +544,12 @@ class RegionService extends BaseService { region.fulfillment_providers = [...region.fulfillment_providers, fp] const updated = await regionRepo.save(region) + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.UPDATED, { + id: updated.id, + fields: ["fulfillment_providers"], + }) return updated }) } @@ -525,6 +578,12 @@ class RegionService extends BaseService { ) const updated = await regionRepo.save(region) + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.UPDATED, { + id: updated.id, + fields: ["payment_providers"], + }) return updated }) } @@ -553,23 +612,15 @@ class RegionService extends BaseService { ) const updated = await regionRepo.save(region) + await this.eventBus_ + .withTransaction(manager) + .emit(RegionService.Events.UPDATED, { + id: updated.id, + fields: ["fulfillment_providers"], + }) return updated }) } - - /** - * Decorates a region - * @param {object} region - the region to decorate - * @param {[string]} fields - the fields to include - * @param {[string]} expandFields - the fields to expand - * @return {Region} the region - */ - async decorate(region, fields, expandFields = []) { - const requiredFields = ["id", "metadata"] - const decorated = _.pick(region, fields.concat(requiredFields)) - const final = await this.runDecorators_(decorated) - return final - } } export default RegionService