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>
This commit is contained in:
Sebastian Rindom
2021-01-26 10:26:14 +01:00
co-authored by Oliver Windall Juhl
parent 5f819486fc
commit f1baca3cbd
499 changed files with 25909 additions and 16128 deletions
@@ -46,21 +46,16 @@ class ContentfulService extends BaseService {
}
}
async getVariantEntries_(productId) {
async getVariantEntries_(variants) {
try {
const productVariants = await this.productService_.retrieveVariants(
productId
)
const contentfulVariants = await Promise.all(
productVariants.map((variant) =>
variants.map((variant) =>
this.updateProductVariantInContentful(variant)
)
)
return contentfulVariants
} catch (error) {
console.log(error)
throw error
}
}
@@ -118,79 +113,33 @@ class ContentfulService extends BaseService {
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_(product._id)
const variantEntries = await this.getVariantEntries_(p.variants)
const variantLinks = this.getVariantLinks_(variantEntries)
const fieldsObject = {
title: {
"en-US": product.title,
},
variants: {
"en-US": variantLinks,
},
options: {
"en-US": product.options,
},
objectId: {
"en-US": product._id,
},
}
if (product.images.length > 0) {
const imageLinks = await this.createImageAssets(product)
const thumbnailAsset = await environment.createAsset({
fields: {
title: {
"en-US": `${product.title}`,
},
description: {
"en-US": "",
},
file: {
"en-US": {
contentType: "image/xyz",
fileName: product.thumbnail,
upload: product.thumbnail,
},
},
const result = await environment.createEntryWithId("product", p.id, {
fields: {
title: {
"en-US": p.title,
},
})
await thumbnailAsset.processForAllLocales()
const thumbnailLink = {
sys: {
type: "Link",
linkType: "Asset",
id: thumbnailAsset.sys.id,
variants: {
"en-US": variantLinks,
},
}
fieldsObject.thumbnail = {
"en-US": thumbnailLink,
}
if (imageLinks) {
fieldsObject.images = {
"en-US": imageLinks,
}
}
}
const result = await environment.createEntryWithId(
"product",
product._id,
{
fields: {
...fieldsObject,
options: {
"en-US": p.options,
},
}
)
objectId: {
"en-US": p.id,
},
},
})
const ignoreIds = (await this.getIgnoreIds_("product")) || []
ignoreIds.push(product._id)
ignoreIds.push(product.id)
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
return result
} catch (error) {
@@ -200,33 +149,37 @@ class ContentfulService extends BaseService {
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",
variant._id,
v.id,
{
fields: {
title: {
"en-US": variant.title,
"en-US": v.title,
},
sku: {
"en-US": variant.sku,
"en-US": v.sku,
},
prices: {
"en-US": variant.prices,
"en-US": v.prices,
},
options: {
"en-US": variant.options,
"en-US": v.options,
},
objectId: {
"en-US": variant._id,
"en-US": v.id,
},
},
}
)
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
ignoreIds.push(variant._id)
ignoreIds.push(v.id)
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
return result
} catch (error) {
@@ -238,12 +191,12 @@ class ContentfulService extends BaseService {
try {
const ignoreIds = (await this.getIgnoreIds_("product")) || []
if (ignoreIds.includes(product._id)) {
const newIgnoreIds = ignoreIds.filter((id) => id !== product._id)
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)
ignoreIds.push(product.id)
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
}
@@ -251,27 +204,31 @@ class ContentfulService extends BaseService {
// check if product exists
let productEntry = undefined
try {
productEntry = await environment.getEntry(product._id)
productEntry = await environment.getEntry(product.id)
} catch (error) {
return this.createProductInContentful(product)
}
const variantEntries = await this.getVariantEntries_(product._id)
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": product.title,
"en-US": p.title,
},
options: {
"en-US": product.options,
"en-US": p.options,
},
variants: {
"en-US": variantLinks,
},
objectId: {
"en-US": product._id,
"en-US": p.id,
},
}
@@ -290,15 +247,15 @@ class ContentfulService extends BaseService {
try {
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
if (ignoreIds.includes(variant._id)) {
const newIgnoreIds = ignoreIds.filter((id) => id !== variant._id)
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)
ignoreIds.push(variant.id)
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
}
@@ -307,32 +264,31 @@ class ContentfulService extends BaseService {
let variantEntry = undefined
// if not, we create a new one
try {
variantEntry = await environment.getEntry(variant._id)
variantEntry = await environment.getEntry(variant.id)
} catch (error) {
return this.createProductVariantInContentful(variant)
}
const cleanPrices = variant.prices.map((price) => ({
...price,
sale_amount: price.sale_amount || undefined,
}))
const v = await this.productVariantService_.retrieve(variant.id, {
relations: ["prices", "options"],
})
const variantEntryFields = {
...variantEntry.fields,
title: {
"en-US": variant.title,
"en-US": v.title,
},
sku: {
"en-US": variant.sku,
"en-US": v.sku,
},
options: {
"en-US": variant.options,
"en-US": v.options,
},
prices: {
"en-US": cleanPrices,
"en-US": v.prices,
},
objectId: {
"en-US": variant._id,
"en-US": v.id,
},
}
@@ -1,5 +1,12 @@
class ContentfulSubscriber {
constructor({ contentfulService, eventBusService }) {
constructor({
contentfulService,
productVariantService,
productService,
eventBusService,
}) {
this.productVariantService_ = productVariantService
this.productService_ = productService
this.contentfulService_ = contentfulService
this.eventBus_ = eventBusService