Merge remote-tracking branch 'origin/develop' into release/next

This commit is contained in:
Sebastian Rindom
2021-02-25 14:38:47 +01:00
7 changed files with 397 additions and 40 deletions
@@ -5,6 +5,7 @@ import Brightpearl from "../utils/brightpearl"
class BrightpearlService extends BaseService {
constructor(
{
manager,
oauthService,
totalsService,
productVariantService,
@@ -18,6 +19,7 @@ class BrightpearlService extends BaseService {
) {
super()
this.manager_ = manager
this.options = options
this.productVariantService_ = productVariantService
this.regionService_ = regionService
@@ -189,8 +191,12 @@ class BrightpearlService extends BaseService {
.retrieveBySKU(sku)
.catch((_) => undefined)
if (variant && variant.manage_inventory) {
await this.productVariantService_.update(variant.id, {
inventory_quantity: onHand,
await this.manager_.transaction((m) => {
return this.productVariantService_
.withTransaction(m)
.update(variant.id, {
inventory_quantity: onHand,
})
})
}
}
@@ -19,8 +19,16 @@ const checkContentTypes = async (container) => {
if (product && product.fields) {
const productFields = product.fields
const customProductFields = Object.keys(
contentfulService.options_.custom_product_fields || {}
)
const keys = Object.values(productFields).map((f) => f.id)
if (!requiredProductFields.every((f) => keys.includes(f))) {
const missingKeys = requiredProductFields.filter(
(rpf) => !keys.includes(rpf) && !customProductFields.includes(rpf)
)
if (missingKeys.length) {
throw Error(
`Contentful: Content type ${`product`} is missing some required key(s). Required: ${requiredProductFields.join(
", "
@@ -32,8 +40,16 @@ const checkContentTypes = async (container) => {
if (variant && variant.fields) {
const variantFields = variant.fields
const customVariantFields = Object.keys(
contentfulService.options_.custom_variant_fields || {}
)
const keys = Object.values(variantFields).map((f) => f.id)
if (!requiredVariantFields.every((f) => keys.includes(f))) {
const missingKeys = requiredVariantFields.filter(
(rpf) => !keys.includes(rpf) && !customVariantFields.includes(rpf)
)
if (missingKeys.length) {
throw Error(
`Contentful: Content type ${`productVariant`} is missing some required key(s). Required: ${requiredVariantFields.join(
", "
@@ -47,13 +63,13 @@ const requiredProductFields = [
"title",
"variants",
"options",
"objectId",
"medusaId",
"type",
"collection",
"tags",
"handle",
]
const requiredVariantFields = ["title", "sku", "prices", "options", "objectId"]
const requiredVariantFields = ["title", "sku", "prices", "options", "medusaId"]
export default checkContentTypes
@@ -111,10 +111,27 @@ class ContentfulService extends BaseService {
return assets
}
getCustomField(field, type) {
const customOptions = this.options_[`custom_${type}_fields`]
if (customOptions) {
return customOptions[field] || field
} else {
return field
}
}
async createProductInContentful(product) {
try {
const p = await this.productService_.retrieve(product.id, {
relations: ["variants", "options", "tags", "type", "collection"],
relations: [
"variants",
"options",
"tags",
"type",
"collection",
"images",
],
})
const environment = await this.getContentfulEnvironment_()
@@ -122,46 +139,92 @@ class ContentfulService extends BaseService {
const variantLinks = this.getVariantLinks_(variantEntries)
const fields = {
title: {
[this.getCustomField("title", "product")]: {
"en-US": p.title,
},
variants: {
[this.getCustomField("variants", "product")]: {
"en-US": variantLinks,
},
options: {
[this.getCustomField("options", "product")]: {
"en-US": p.options,
},
objectId: {
[this.getCustomField("medusaId", "product")]: {
"en-US": p.id,
},
}
if (p.images.length > 0) {
const imageLinks = await this.createImageAssets(product)
const thumbnailAsset = await environment.createAsset({
fields: {
title: {
"en-US": `${p.title}`,
},
description: {
"en-US": "",
},
file: {
"en-US": {
contentType: "image/xyz",
fileName: p.thumbnail,
upload: p.thumbnail,
},
},
},
})
await thumbnailAsset.processForAllLocales()
const thumbnailLink = {
sys: {
type: "Link",
linkType: "Asset",
id: thumbnailAsset.sys.id,
},
}
fields.thumbnail = {
"en-US": thumbnailLink,
}
if (imageLinks) {
fields.images = {
"en-US": imageLinks,
}
}
}
if (p.type) {
const type = {
"en-US": p.type.value,
}
fields.type = type
fields[this.getCustomField("type", "product")] = type
}
if (p.collection) {
const collection = {
"en-US": p.collection.title,
}
fields.collection = collection
fields[this.getCustomField("collection", "product")] = collection
}
if (p.tags) {
const tags = {
"en-US": p.tags,
}
fields.tags = tags
fields[this.getCustomField("tags", "product")] = tags
}
if (p.handle) {
const handle = {
"en-US": p.handle,
}
fields.handle = handle
fields[this.getCustomField("handle", "product")] = handle
}
const result = await environment.createEntryWithId("product", p.id, {
@@ -189,19 +252,19 @@ class ContentfulService extends BaseService {
v.id,
{
fields: {
title: {
[this.getCustomField("title", "variant")]: {
"en-US": v.title,
},
sku: {
[this.getCustomField("sku", "variant")]: {
"en-US": v.sku,
},
prices: {
[this.getCustomField("prices", "variant")]: {
"en-US": v.prices,
},
options: {
[this.getCustomField("options", "variant")]: {
"en-US": v.options,
},
objectId: {
[this.getCustomField("medusaId", "variant")]: {
"en-US": v.id,
},
},
@@ -240,7 +303,14 @@ class ContentfulService extends BaseService {
}
const p = await this.productService_.retrieve(product.id, {
relations: ["options", "variants", "type", "collection", "tags"],
relations: [
"options",
"variants",
"type",
"collection",
"tags",
"images",
],
})
const variantEntries = await this.getVariantEntries_(p.variants)
@@ -248,46 +318,86 @@ class ContentfulService extends BaseService {
const productEntryFields = {
...productEntry.fields,
title: {
[this.getCustomField("title", "product")]: {
"en-US": p.title,
},
options: {
[this.getCustomField("options", "product")]: {
"en-US": p.options,
},
variants: {
[this.getCustomField("variants", "product")]: {
"en-US": variantLinks,
},
objectId: {
[this.getCustomField("medusaId", "product")]: {
"en-US": p.id,
},
}
if (p.thumbnail) {
const thumbnailAsset = await environment.createAsset({
fields: {
title: {
"en-US": `${p.title}`,
},
description: {
"en-US": "",
},
file: {
"en-US": {
contentType: "image/xyz",
fileName: p.thumbnail,
upload: p.thumbnail,
},
},
},
})
await thumbnailAsset.processForAllLocales()
const thumbnailLink = {
sys: {
type: "Link",
linkType: "Asset",
id: thumbnailAsset.sys.id,
},
}
productEntryFields.thumbnail = {
"en-US": thumbnailLink,
}
}
if (p.type) {
const type = {
"en-US": p.type.value,
}
productEntryFields.type = type
productEntryFields[this.getCustomField("type", "product")] = type
}
if (p.collection) {
const collection = {
"en-US": p.collection.title,
}
productEntryFields.collection = collection
productEntryFields[
this.getCustomField("collection", "product")
] = collection
}
if (p.tags) {
const tags = {
"en-US": p.tags,
}
productEntryFields.tags = tags
productEntryFields[this.getCustomField("tags", "product")] = tags
}
if (p.handle) {
const handle = {
"en-US": p.handle,
}
productEntryFields.handle = handle
productEntryFields[this.getCustomField("handle", "product")] = handle
}
productEntry.fields = productEntryFields
@@ -333,19 +443,19 @@ class ContentfulService extends BaseService {
const variantEntryFields = {
...variantEntry.fields,
title: {
[this.getCustomField("title", "variant")]: {
"en-US": v.title,
},
sku: {
[this.getCustomField("sku", "variant")]: {
"en-US": v.sku,
},
options: {
[this.getCustomField("options", "variant")]: {
"en-US": v.options,
},
prices: {
[this.getCustomField("prices", "variant")]: {
"en-US": v.prices,
},
objectId: {
[this.getCustomField("medusaId", "variant")]: {
"en-US": v.id,
},
}
@@ -377,7 +487,8 @@ class ContentfulService extends BaseService {
}
let update = {
title: productEntry.fields.title["en-US"],
title:
productEntry.fields[this.getCustomField("title", "product")]["en-US"],
}
// Get the thumbnail, if present
@@ -421,7 +532,10 @@ class ContentfulService extends BaseService {
const updatedVariant = await this.productVariantService_.update(
variantId,
{
title: variantEntry.fields.title["en-US"],
title:
variantEntry.fields[this.getCustomField("title", "variant")][
"en-US"
],
}
)
@@ -2,18 +2,32 @@ export default async (req, res) => {
try {
const customerService = req.scope.resolve("customerService")
const limit = parseInt(req.query.limit) || 10
const limit = parseInt(req.query.limit) || 50
const offset = parseInt(req.query.offset) || 0
const selector = {}
if ("q" in req.query) {
selector.q = req.query.q
}
let expandFields = []
if ("expand" in req.query) {
expandFields = req.query.expand.split(",")
}
const listConfig = {
relations: [],
relations: expandFields.length ? expandFields : [],
skip: offset,
take: limit,
}
const customers = await customerService.list({}, listConfig)
const [customers, count] = await customerService.listAndCount(
selector,
listConfig
)
res.json({ customers, count: customers.length, offset, limit })
res.json({ customers, count, offset, limit })
} catch (error) {
throw error
}
+45
View File
@@ -3,6 +3,7 @@ import Scrypt from "scrypt-kdf"
import _ from "lodash"
import { Validator, MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import { Brackets } from "typeorm"
/**
* Provides layer to manipulate customers.
@@ -132,6 +133,50 @@ class CustomerService extends BaseService {
return customerRepo.find(query)
}
async listAndCount(
selector,
config = { relations: [], skip: 0, take: 50, order: { created_at: "DESC" } }
) {
const customerRepo = this.manager_.getCustomRepository(
this.customerRepository_
)
let q
if ("q" in selector) {
q = selector.q
delete selector.q
}
const query = this.buildQuery_(selector, config)
if (q) {
const where = query.where
delete where.email
delete where.first_name
delete where.last_name
query.join = {
alias: "customer",
}
query.where = qb => {
qb.where(where)
qb.andWhere(
new Brackets(qb => {
qb.where(`customer.first_name ILIKE :q`, { q: `%${q}%` })
.orWhere(`customer.last_name ILIKE :q`, { q: `%${q}%` })
.orWhere(`customer.email ILIKE :q`, { q: `%${q}%` })
})
)
}
}
const [customers, count] = await customerRepo.findAndCount(query)
return [customers, count]
}
/**
* Return the total number of documents in database
* @return {Promise} the result of the count operation