fix(medusa): Upsert product images (#200)
This commit is contained in:
1 parent
0601b34765
commit
a031f1f338
5 files changed
+84
-6
No files matched your search
@@ -42,6 +42,7 @@ describe("/admin/products", () => {
|
|||||||
const manager = dbConnection.manager;
|
const manager = dbConnection.manager;
|
||||||
await manager.query(`DELETE FROM "product_option_value"`);
|
await manager.query(`DELETE FROM "product_option_value"`);
|
||||||
await manager.query(`DELETE FROM "product_option"`);
|
await manager.query(`DELETE FROM "product_option"`);
|
||||||
|
await manager.query(`DELETE FROM "image"`);
|
||||||
await manager.query(`DELETE FROM "money_amount"`);
|
await manager.query(`DELETE FROM "money_amount"`);
|
||||||
await manager.query(`DELETE FROM "product_variant"`);
|
await manager.query(`DELETE FROM "product_variant"`);
|
||||||
await manager.query(`DELETE FROM "product"`);
|
await manager.query(`DELETE FROM "product"`);
|
||||||
@@ -62,6 +63,7 @@ describe("/admin/products", () => {
|
|||||||
title: "Test product",
|
title: "Test product",
|
||||||
description: "test-product-description",
|
description: "test-product-description",
|
||||||
type: { value: "test-type" },
|
type: { value: "test-type" },
|
||||||
|
images: ["test-image.png", "test-image-2.png"],
|
||||||
collection_id: "test-collection",
|
collection_id: "test-collection",
|
||||||
tags: [{ value: "123" }, { value: "456" }],
|
tags: [{ value: "123" }, { value: "456" }],
|
||||||
options: [{ title: "size" }, { title: "color" }],
|
options: [{ title: "size" }, { title: "color" }],
|
||||||
@@ -91,6 +93,15 @@ describe("/admin/products", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: "Test product",
|
title: "Test product",
|
||||||
handle: "test-product",
|
handle: "test-product",
|
||||||
|
images: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
url: "test-image.png",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
url: "test-image-2.png",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
thumbnail: "test-image.png",
|
||||||
tags: [
|
tags: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
value: "123",
|
value: "123",
|
||||||
@@ -137,13 +148,15 @@ describe("/admin/products", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates a product (update tags, delete collection, delete type)", async () => {
|
it("updates a product (update tags, delete collection, delete type, replaces images)", async () => {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
collection_id: null,
|
collection_id: null,
|
||||||
type: null,
|
type: null,
|
||||||
tags: [{ value: "123" }],
|
tags: [{ value: "123" }],
|
||||||
|
images: ["test-image-2.png"],
|
||||||
|
type: { value: "test-type-2" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
@@ -160,6 +173,12 @@ describe("/admin/products", () => {
|
|||||||
|
|
||||||
expect(response.data.product).toEqual(
|
expect(response.data.product).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
images: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
url: "test-image-2.png",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
thumbnail: "test-image-2.png",
|
||||||
tags: [
|
tags: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
value: "123",
|
value: "123",
|
||||||
@@ -167,6 +186,9 @@ describe("/admin/products", () => {
|
|||||||
],
|
],
|
||||||
type: null,
|
type: null,
|
||||||
collection: null,
|
collection: null,
|
||||||
|
type: expect.objectContaining({
|
||||||
|
value: "test-type-2",
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const {
|
|||||||
Product,
|
Product,
|
||||||
ShippingProfile,
|
ShippingProfile,
|
||||||
ProductVariant,
|
ProductVariant,
|
||||||
|
Image,
|
||||||
} = require("@medusajs/medusa");
|
} = require("@medusajs/medusa");
|
||||||
|
|
||||||
module.exports = async (connection, data = {}) => {
|
module.exports = async (connection, data = {}) => {
|
||||||
@@ -36,6 +37,13 @@ module.exports = async (connection, data = {}) => {
|
|||||||
|
|
||||||
await manager.save(type);
|
await manager.save(type);
|
||||||
|
|
||||||
|
const image = manager.create(Image, {
|
||||||
|
id: "test-image",
|
||||||
|
url: "test-image.png",
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.save(image);
|
||||||
|
|
||||||
await manager.insert(Region, {
|
await manager.insert(Region, {
|
||||||
id: "test-region",
|
id: "test-region",
|
||||||
name: "Test Region",
|
name: "Test Region",
|
||||||
@@ -43,7 +51,7 @@ module.exports = async (connection, data = {}) => {
|
|||||||
tax_rate: 0,
|
tax_rate: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
await manager.insert(Product, {
|
const p = manager.create(Product, {
|
||||||
id: "test-product",
|
id: "test-product",
|
||||||
title: "Test product",
|
title: "Test product",
|
||||||
profile_id: defaultProfile.id,
|
profile_id: defaultProfile.id,
|
||||||
@@ -57,6 +65,10 @@ module.exports = async (connection, data = {}) => {
|
|||||||
options: [{ id: "test-option", title: "Default value" }],
|
options: [{ id: "test-option", title: "Default value" }],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
p.images = [image];
|
||||||
|
|
||||||
|
await manager.save(p);
|
||||||
|
|
||||||
await manager.insert(ProductVariant, {
|
await manager.insert(ProductVariant, {
|
||||||
id: "test-variant",
|
id: "test-variant",
|
||||||
inventory_quantity: 10,
|
inventory_quantity: 10,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class Product {
|
|||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
is_giftcard: boolean
|
is_giftcard: boolean
|
||||||
|
|
||||||
@ManyToMany(() => Image)
|
@ManyToMany(() => Image, { cascade: ["insert"] })
|
||||||
@JoinTable({
|
@JoinTable({
|
||||||
name: "product_images",
|
name: "product_images",
|
||||||
joinColumn: {
|
joinColumn: {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { EntityRepository, Repository } from "typeorm"
|
||||||
|
import { Image } from "../models/image"
|
||||||
|
|
||||||
|
@EntityRepository(Image)
|
||||||
|
export class ImageRepository extends Repository<Image> {}
|
||||||
@@ -23,6 +23,7 @@ class ProductService extends BaseService {
|
|||||||
productCollectionService,
|
productCollectionService,
|
||||||
productTypeRepository,
|
productTypeRepository,
|
||||||
productTagRepository,
|
productTagRepository,
|
||||||
|
imageRepository,
|
||||||
}) {
|
}) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@@ -52,6 +53,9 @@ class ProductService extends BaseService {
|
|||||||
|
|
||||||
/** @private @const {ProductCollectionService} */
|
/** @private @const {ProductCollectionService} */
|
||||||
this.productTagRepository_ = productTagRepository
|
this.productTagRepository_ = productTagRepository
|
||||||
|
|
||||||
|
/** @private @const {ImageRepository} */
|
||||||
|
this.imageRepository_ = imageRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
withTransaction(transactionManager) {
|
withTransaction(transactionManager) {
|
||||||
@@ -69,6 +73,7 @@ class ProductService extends BaseService {
|
|||||||
productCollectionService: this.productCollectionService_,
|
productCollectionService: this.productCollectionService_,
|
||||||
productTagRepository: this.productTagRepository_,
|
productTagRepository: this.productTagRepository_,
|
||||||
productTypeRepository: this.productTypeRepository_,
|
productTypeRepository: this.productTypeRepository_,
|
||||||
|
imageRepository: this.imageRepository_,
|
||||||
})
|
})
|
||||||
|
|
||||||
cloned.transactionManager_ = transactionManager
|
cloned.transactionManager_ = transactionManager
|
||||||
@@ -233,7 +238,7 @@ class ProductService extends BaseService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return existing
|
return existing.id
|
||||||
}
|
}
|
||||||
|
|
||||||
const created = productTypeRepository.create(type)
|
const created = productTypeRepository.create(type)
|
||||||
@@ -277,10 +282,18 @@ class ProductService extends BaseService {
|
|||||||
this.productOptionRepository_
|
this.productOptionRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
const { options, tags, type, ...rest } = productObject
|
const { options, tags, type, images, ...rest } = productObject
|
||||||
|
|
||||||
|
if (!rest.thumbnail && images && images.length) {
|
||||||
|
rest.thumbnail = images[0]
|
||||||
|
}
|
||||||
|
|
||||||
let product = productRepo.create(rest)
|
let product = productRepo.create(rest)
|
||||||
|
|
||||||
|
if (images && images.length) {
|
||||||
|
product.images = await this.upsertImages_(images)
|
||||||
|
}
|
||||||
|
|
||||||
if (tags) {
|
if (tags) {
|
||||||
product.tags = await this.upsertProductTags_(tags)
|
product.tags = await this.upsertProductTags_(tags)
|
||||||
}
|
}
|
||||||
@@ -310,6 +323,28 @@ class ProductService extends BaseService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async upsertImages_(images) {
|
||||||
|
const imageRepository = this.manager_.getCustomRepository(
|
||||||
|
this.imageRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
let productImages = []
|
||||||
|
for (const img of images) {
|
||||||
|
const existing = await imageRepository.findOne({
|
||||||
|
where: { url: img },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
productImages.push(existing)
|
||||||
|
} else {
|
||||||
|
const created = imageRepository.create({ url: img })
|
||||||
|
productImages.push(created)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return productImages
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a product. Product variant updates should use dedicated methods,
|
* Updates a product. Product variant updates should use dedicated methods,
|
||||||
* e.g. `addVariant`, etc. The function will throw errors if metadata or
|
* e.g. `addVariant`, etc. The function will throw errors if metadata or
|
||||||
@@ -327,7 +362,7 @@ class ProductService extends BaseService {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const product = await this.retrieve(productId, {
|
const product = await this.retrieve(productId, {
|
||||||
relations: ["variants", "tags"],
|
relations: ["variants", "tags", "images"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -344,6 +379,10 @@ class ProductService extends BaseService {
|
|||||||
product.thumbnail = images[0]
|
product.thumbnail = images[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (images && images.length) {
|
||||||
|
product.images = await this.upsertImages_(images)
|
||||||
|
}
|
||||||
|
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
product.metadata = this.setMetadata_(product, metadata)
|
product.metadata = this.setMetadata_(product, metadata)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user