Files
medusa-store/packages/medusa-plugin-contentful/src/services/contentful.js
T
Sebastian RindomandOliver Windall Juhl f1baca3cbd Replaces MongoDB support with PostgreSQL (#151)
- All schemas have been rewritten to a relational model
- All services have been rewritten to accommodate the new data model
- Adds idempotency keys to core endpoints allowing you to retry requests with no additional side effects
- Adds staged jobs to avoid putting jobs in the queue when transactions abort
- Adds atomic transactions to all methods with access to the data layer

Co-authored-by: Oliver Windall Juhl <oliver@mrbltech.com>
2021-01-26 10:26:14 +01:00

378 lines
9.6 KiB
JavaScript

import _ from "lodash"
import { BaseService } from "medusa-interfaces"
import { createClient } from "contentful-management"
class ContentfulService extends BaseService {
constructor(
{ productService, redisClient, productVariantService, eventBusService },
options
) {
super()
this.productService_ = productService
this.productVariantService_ = productVariantService
this.eventBus_ = eventBusService
this.options_ = options
this.contentful_ = createClient({
accessToken: options.access_token,
})
this.redis_ = redisClient
}
async getIgnoreIds_(type) {
return new Promise((resolve, reject) => {
this.redis_.get(`${type}_ignore_ids`, (err, reply) => {
if (err) {
return reject(err)
}
return resolve(JSON.parse(reply))
})
})
}
async getContentfulEnvironment_() {
try {
const space = await this.contentful_.getSpace(this.options_.space_id)
const environment = await space.getEnvironment(this.options_.environment)
return environment
} catch (error) {
throw error
}
}
async getVariantEntries_(variants) {
try {
const contentfulVariants = await Promise.all(
variants.map((variant) =>
this.updateProductVariantInContentful(variant)
)
)
return contentfulVariants
} catch (error) {
throw error
}
}
getVariantLinks_(variantEntries) {
return variantEntries.map((v) => ({
sys: {
type: "Link",
linkType: "Entry",
id: v.sys.id,
},
}))
}
async createImageAssets(product) {
const environment = await this.getContentfulEnvironment_()
let assets = []
await Promise.all(
product.images
.filter((image) => image !== product.thumbnail)
.map(async (image, i) => {
const asset = await environment.createAsset({
fields: {
title: {
"en-US": `${product.title} - ${i}`,
},
description: {
"en-US": "",
},
file: {
"en-US": {
contentType: "image/xyz",
fileName: image,
upload: image,
},
},
},
})
await asset.processForAllLocales()
assets.push({
sys: {
type: "Link",
linkType: "Asset",
id: asset.sys.id,
},
})
})
)
return assets
}
async createProductInContentful(product) {
try {
const p = await this.productService_.retrieve(product.id, {
relations: ["variants", "options"],
})
const environment = await this.getContentfulEnvironment_()
const variantEntries = await this.getVariantEntries_(p.variants)
const variantLinks = this.getVariantLinks_(variantEntries)
const result = await environment.createEntryWithId("product", p.id, {
fields: {
title: {
"en-US": p.title,
},
variants: {
"en-US": variantLinks,
},
options: {
"en-US": p.options,
},
objectId: {
"en-US": p.id,
},
},
})
const ignoreIds = (await this.getIgnoreIds_("product")) || []
ignoreIds.push(product.id)
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
return result
} catch (error) {
throw error
}
}
async createProductVariantInContentful(variant) {
try {
const v = await this.productVariantService_.retrieve(variant.id, {
relations: ["prices", "options"],
})
const environment = await this.getContentfulEnvironment_()
const result = await environment.createEntryWithId(
"productVariant",
v.id,
{
fields: {
title: {
"en-US": v.title,
},
sku: {
"en-US": v.sku,
},
prices: {
"en-US": v.prices,
},
options: {
"en-US": v.options,
},
objectId: {
"en-US": v.id,
},
},
}
)
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
ignoreIds.push(v.id)
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
return result
} catch (error) {
throw error
}
}
async updateProductInContentful(product) {
try {
const ignoreIds = (await this.getIgnoreIds_("product")) || []
if (ignoreIds.includes(product.id)) {
const newIgnoreIds = ignoreIds.filter((id) => id !== product.id)
this.redis_.set("product_ignore_ids", JSON.stringify(newIgnoreIds))
return
} else {
ignoreIds.push(product.id)
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
}
const environment = await this.getContentfulEnvironment_()
// check if product exists
let productEntry = undefined
try {
productEntry = await environment.getEntry(product.id)
} catch (error) {
return this.createProductInContentful(product)
}
const p = await this.productService_.retrieve(product.id, {
relations: ["options", "variants"],
})
const variantEntries = await this.getVariantEntries_(p.variants)
const variantLinks = this.getVariantLinks_(variantEntries)
const productEntryFields = {
...productEntry.fields,
title: {
"en-US": p.title,
},
options: {
"en-US": p.options,
},
variants: {
"en-US": variantLinks,
},
objectId: {
"en-US": p.id,
},
}
productEntry.fields = productEntryFields
const updatedEntry = await productEntry.update()
const publishedEntry = await updatedEntry.publish()
return publishedEntry
} catch (error) {
throw error
}
}
async updateProductVariantInContentful(variant) {
try {
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
if (ignoreIds.includes(variant.id)) {
const newIgnoreIds = ignoreIds.filter((id) => id !== variant.id)
this.redis_.set(
"product_variant_ignore_ids",
JSON.stringify(newIgnoreIds)
)
return
} else {
ignoreIds.push(variant.id)
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
}
const environment = await this.getContentfulEnvironment_()
// check if product exists
let variantEntry = undefined
// if not, we create a new one
try {
variantEntry = await environment.getEntry(variant.id)
} catch (error) {
return this.createProductVariantInContentful(variant)
}
const v = await this.productVariantService_.retrieve(variant.id, {
relations: ["prices", "options"],
})
const variantEntryFields = {
...variantEntry.fields,
title: {
"en-US": v.title,
},
sku: {
"en-US": v.sku,
},
options: {
"en-US": v.options,
},
prices: {
"en-US": v.prices,
},
objectId: {
"en-US": v.id,
},
}
variantEntry.fields = variantEntryFields
const updatedEntry = await variantEntry.update()
const publishedEntry = await updatedEntry.publish()
return publishedEntry
} catch (error) {
throw error
}
}
async sendContentfulProductToAdmin(productId) {
try {
const environment = await this.getContentfulEnvironment_()
const productEntry = await environment.getEntry(productId)
const ignoreIds = (await this.getIgnoreIds_("product")) || []
if (ignoreIds.includes(productId)) {
const newIgnoreIds = ignoreIds.filter((id) => id !== productId)
this.redis_.set("product_ignore_ids", JSON.stringify(newIgnoreIds))
return
} else {
ignoreIds.push(productId)
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
}
let update = {
title: productEntry.fields.title["en-US"],
}
// Get the thumbnail, if present
if (productEntry.fields.thumbnail) {
const thumb = await environment.getAsset(
productEntry.fields.thumbnail["en-US"].sys.id
)
update.thumbnail = thumb.fields.file["en-US"].url
}
const updatedProduct = await this.productService_.update(
productId,
update
)
return updatedProduct
} catch (error) {
throw error
}
}
async sendContentfulProductVariantToAdmin(variantId) {
try {
const environment = await this.getContentfulEnvironment_()
const variantEntry = await environment.getEntry(variantId)
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
if (ignoreIds.includes(variantId)) {
const newIgnoreIds = ignoreIds.filter((id) => id !== variantId)
this.redis_.set(
"product_variant_ignore_ids",
JSON.stringify(newIgnoreIds)
)
return
} else {
ignoreIds.push(variantId)
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
}
const updatedVariant = await this.productVariantService_.update(
variantId,
{
title: variantEntry.fields.title["en-US"],
}
)
return updatedVariant
} catch (error) {
throw error
}
}
}
export default ContentfulService