From 99b6a9414601b31dd736a90eaa9f9ca9eccd25b3 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Thu, 2 Sep 2021 09:51:53 +0200 Subject: [PATCH 01/21] add rank field to product variant --- packages/medusa/src/models/product-variant.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/medusa/src/models/product-variant.ts b/packages/medusa/src/models/product-variant.ts index a746605e27..7cecfea41f 100644 --- a/packages/medusa/src/models/product-variant.ts +++ b/packages/medusa/src/models/product-variant.ts @@ -63,6 +63,9 @@ export class ProductVariant { @Index({ unique: true, where: "deleted_at IS NOT NULL" }) upc: string + @Column() + rank: number + @Column({ type: "int" }) inventory_quantity: number From 7681e6a641c70d589273f8701c81e756382c86a3 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Thu, 2 Sep 2021 09:53:07 +0200 Subject: [PATCH 02/21] add rank updates to productservice.update --- packages/medusa/src/services/product.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index 7cacd571f0..0fe74e42d2 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -410,7 +410,9 @@ class ProductService extends BaseService { } const newVariants = [] - for (const newVariant of variants) { + for (const [i, newVariant] of variants.entries()) { + newVariant.rank = i + if (newVariant.id) { const variant = product.variants.find(v => v.id === newVariant.id) From 1bf2b9c2ac0f35ac85a185cdf4ce29d756bcd353 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Thu, 2 Sep 2021 09:53:58 +0200 Subject: [PATCH 03/21] Test if productservice.update updates rankings of variants --- .../medusa/src/services/__tests__/product.js | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/medusa/src/services/__tests__/product.js b/packages/medusa/src/services/__tests__/product.js index 422bdb42e5..194216be2b 100644 --- a/packages/medusa/src/services/__tests__/product.js +++ b/packages/medusa/src/services/__tests__/product.js @@ -148,6 +148,15 @@ describe("ProductService", () => { if (query.where.id === "123") { return undefined } + if (query.where.id === "ranking test") { + return Promise.resolve({ + id: "ranking test", + variants: [ + { id: "test_321", title: "Greener", rank: 1 }, + { id: "test_123", title: "Blueer", rank: 0 }, + ], + }) + } return Promise.resolve({ id: IdMap.getId("ironman") }) }, }) @@ -165,7 +174,12 @@ describe("ProductService", () => { withTransaction: function() { return this }, - update: () => Promise.resolve(), + update: (variant, update) => { + if (variant.id) { + return update + } + return Promise.resolve() + }, } const productTagRepository = MockRepository({ @@ -248,6 +262,30 @@ describe("ProductService", () => { }) }) + it("successfully updates variant ranking", async () => { + await productService.update("ranking test", { + variants: [ + { id: "test_321", title: "Greener", rank: 1 }, + { id: "test_123", title: "Blueer", rank: 0 }, + ], + }) + + expect(eventBusService.emit).toHaveBeenCalledTimes(1) + expect(eventBusService.emit).toHaveBeenCalledWith( + "product.updated", + expect.any(Object) + ) + + expect(productRepository.save).toHaveBeenCalledTimes(1) + expect(productRepository.save).toHaveBeenCalledWith({ + id: "ranking test", + variants: [ + { id: "test_321", title: "Greener", rank: 0 }, + { id: "test_123", title: "Blueer", rank: 1 }, + ], + }) + }) + it("successfully updates tags", async () => { await productService.update(IdMap.getId("ironman"), { tags: [ From f73841c3847f97f4a4bc7daf4925b7a0ef3546e8 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Thu, 2 Sep 2021 11:42:02 +0200 Subject: [PATCH 04/21] add variant ranking on creation of a product --- .../medusa/src/services/__tests__/product.js | 58 ++++++++++++++++++- packages/medusa/src/services/product.js | 8 +++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/packages/medusa/src/services/__tests__/product.js b/packages/medusa/src/services/__tests__/product.js index 194216be2b..4f94b92494 100644 --- a/packages/medusa/src/services/__tests__/product.js +++ b/packages/medusa/src/services/__tests__/product.js @@ -11,9 +11,24 @@ const eventBusService = { describe("ProductService", () => { describe("retrieve", () => { const productRepo = MockRepository({ - findOneWithRelations: () => - Promise.resolve({ id: IdMap.getId("ironman") }), + findOneWithRelations: (rels, query) => { + if (query.where.id === "test id with variants") + return { + id: "test id with variants", + variants: [ + { id: "test_321", title: "Green", rank: 1 }, + { id: "test_123", title: "Blue", rank: 0 }, + ], + } + if (query.where.id === "test id one variant") + return { + id: "test id one variant", + variants: [{ id: "test_123", title: "Blue", rank: 0 }], + } + return Promise.resolve({ id: IdMap.getId("ironman") }) + }, }) + const productService = new ProductService({ manager: MockManager, productRepository: productRepo, @@ -23,6 +38,30 @@ describe("ProductService", () => { jest.clearAllMocks() }) + it("Orders variants according to rank when retrieving a product", async () => { + const result = await productService.retrieve("test id with variants", { + relations: ["Variants"], + }) + + expect(productRepo.findOneWithRelations).toHaveBeenCalledTimes(1) + expect(productRepo.findOneWithRelations).toHaveBeenCalledWith( + ["Variants"], + { + where: { id: "test id with variants" }, + } + ) + + const expected = { + id: "test id with variants", + variants: [ + { id: "test_123", title: "Blue", rank: 0 }, + { id: "test_321", title: "Green", rank: 1 }, + ], + } + + expect(result).toEqual(expected) + }) + it("successfully retrieves a product", async () => { const result = await productService.retrieve(IdMap.getId("ironman")) @@ -37,11 +76,12 @@ describe("ProductService", () => { describe("create", () => { const productRepository = MockRepository({ - create: () => ({ + create: product => ({ id: IdMap.getId("ironman"), title: "Suit", options: [], collection: { id: IdMap.getId("cat"), title: "Suits" }, + variants: product.variants, }), findOneWithRelations: () => ({ id: IdMap.getId("ironman"), @@ -97,6 +137,10 @@ describe("ProductService", () => { options: [], tags: [{ value: "title" }, { value: "title2" }], type: "type-1", + variants: [ + { id: "test1", title: "green", rank: 0 }, + { id: "test2", title: "blue", rank: 0 }, + ], }) expect(eventBusService.emit).toHaveBeenCalledTimes(1) @@ -108,6 +152,10 @@ describe("ProductService", () => { expect(productRepository.create).toHaveBeenCalledTimes(1) expect(productRepository.create).toHaveBeenCalledWith({ title: "Suit", + variants: [ + { id: "test1", title: "green", rank: 0 }, + { id: "test2", title: "blue", rank: 1 }, + ], }) expect(productTagRepository.findOne).toHaveBeenCalledTimes(2) @@ -132,6 +180,10 @@ describe("ProductService", () => { id: IdMap.getId("cat"), title: "Suits", }, + variants: [ + { id: "test1", title: "green", rank: 0 }, + { id: "test2", title: "blue", rank: 1 }, + ], }) }) }) diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index 0fe74e42d2..779ef8804a 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -185,6 +185,11 @@ class ProductService extends BaseService { ) } + if (product.variants) + product.variants.sort( + (variant1, variant2) => variant1.rank - variant2.rank + ) + return product } @@ -293,6 +298,9 @@ class ProductService extends BaseService { rest.discountable = false } + if (rest.variants) + for (const [i, variant] of rest.variants.entries()) variant.rank = i + let product = productRepo.create(rest) if (images && images.length) { From 9e62746e43a9de24a43dba6ffebb0f3e61986246 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Fri, 3 Sep 2021 17:15:33 +0200 Subject: [PATCH 05/21] integration tests --- .../api/__tests__/admin/product.js | 359 ++++++++++++------ .../api/helpers/product-seeder.js | 127 ++++++- 2 files changed, 352 insertions(+), 134 deletions(-) diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index 7b0696e02c..e179af156d 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -1,49 +1,49 @@ -const path = require("path"); +const path = require("path") -const setupServer = require("../../../helpers/setup-server"); -const { useApi } = require("../../../helpers/use-api"); -const { initDb, useDb } = require("../../../helpers/use-db"); +const setupServer = require("../../../helpers/setup-server") +const { useApi } = require("../../../helpers/use-api") +const { initDb, useDb } = require("../../../helpers/use-db") -const adminSeeder = require("../../helpers/admin-seeder"); -const productSeeder = require("../../helpers/product-seeder"); +const adminSeeder = require("../../helpers/admin-seeder") +const productSeeder = require("../../helpers/product-seeder") -jest.setTimeout(30000); +jest.setTimeout(30000) describe("/admin/products", () => { - let medusaProcess; - let dbConnection; + let medusaProcess + let dbConnection beforeAll(async () => { - const cwd = path.resolve(path.join(__dirname, "..", "..")); - dbConnection = await initDb({ cwd }); - medusaProcess = await setupServer({ cwd }); - }); + const cwd = path.resolve(path.join(__dirname, "..", "..")) + dbConnection = await initDb({ cwd }) + medusaProcess = await setupServer({ cwd }) + }) afterAll(async () => { - const db = useDb(); - await db.shutdown(); + const db = useDb() + await db.shutdown() - medusaProcess.kill(); - }); + medusaProcess.kill() + }) describe("POST /admin/products", () => { beforeEach(async () => { try { - await productSeeder(dbConnection); - await adminSeeder(dbConnection); + await productSeeder(dbConnection) + await adminSeeder(dbConnection) } catch (err) { - console.log(err); - throw err; + console.log(err) + throw err } - }); + }) afterEach(async () => { - const db = useDb(); - await db.teardown(); - }); + const db = useDb() + await db.teardown() + }) it("creates a product", async () => { - const api = useApi(); + const api = useApi() const payload = { title: "Test product", @@ -61,7 +61,7 @@ describe("/admin/products", () => { options: [{ value: "large" }, { value: "green" }], }, ], - }; + } const response = await api .post("/admin/products", payload, { @@ -70,10 +70,10 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ @@ -133,11 +133,67 @@ describe("/admin/products", () => { }), ], }) - ); - }); + ) + }) + + it("Sets variant ranks when creating a product", async () => { + const api = useApi() + + const payload = { + title: "Test product - 1", + description: "test-product-description 1", + type: { value: "test-type 1" }, + images: ["test-image.png", "test-image-2.png"], + collection_id: "test-collection", + tags: [{ value: "123" }, { value: "456" }], + options: [{ title: "size" }, { title: "color" }], + variants: [ + { + title: "Test variant 1", + inventory_quantity: 10, + prices: [{ currency_code: "usd", amount: 100 }], + options: [{ value: "large" }, { value: "green" }], + }, + { + title: "Test variant 2", + inventory_quantity: 10, + prices: [{ currency_code: "usd", amount: 100 }], + options: [{ value: "large" }, { value: "green" }], + }, + ], + } + + const response = await api + .post("/admin/products", payload, { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + expect(response.status).toEqual(200) + + expect(response.data.product).toEqual( + expect.objectContaining({ + title: "Test product - 1", + variants: [ + expect.objectContaining({ + title: "Test variant 1", + rank: 0, + }), + expect.objectContaining({ + title: "Test variant 2", + rank: 1, + }), + ], + }) + ) + }) it("creates a giftcard", async () => { - const api = useApi(); + const api = useApi() const payload = { title: "Test Giftcard", @@ -151,7 +207,7 @@ describe("/admin/products", () => { options: [{ value: "100" }], }, ], - }; + } const response = await api .post("/admin/products", payload, { @@ -160,21 +216,21 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ title: "Test Giftcard", discountable: false, }) - ); - }); + ) + }) it("updates a product (update prices, tags, delete collection, delete type, replaces images)", async () => { - const api = useApi(); + const api = useApi() const payload = { collection_id: null, @@ -182,13 +238,19 @@ describe("/admin/products", () => { variants: [ { id: "test-variant", - prices: [{ currency_code: "usd", amount: 100, sale_amount: 75 }], + prices: [ + { + currency_code: "usd", + amount: 100, + sale_amount: 75, + }, + ], }, ], tags: [{ value: "123" }], images: ["test-image-2.png"], type: { value: "test-type-2" }, - }; + } const response = await api .post("/admin/products/test-product", payload, { @@ -197,10 +259,10 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ @@ -231,15 +293,72 @@ describe("/admin/products", () => { value: "test-type-2", }), }) - ); - }); + ) + }) + + it("updates a product (variant ordering)", async () => { + const api = useApi() + + const payload = { + collection_id: null, + type: null, + variants: [ + { + id: "test-variant", + }, + { + id: "test-variant_1", + }, + { + id: "test-variant_2", + }, + ], + } + + const response = await api + .post("/admin/products/test-product", payload, { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + expect(response.status).toEqual(200) + + expect(response.data.product).toEqual( + expect.objectContaining({ + title: "Test product", + variants: [ + expect.objectContaining({ + id: "test-variant", + rank: 0, + title: "Test variant", + }), + expect.objectContaining({ + id: "test-variant_1", + rank: 1, + title: "Test variant rank (1)", + }), + expect.objectContaining({ + id: "test-variant_2", + rank: 2, + title: "Test variant rank (2)", + }), + ], + type: null, + collection: null, + }) + ) + }) it("add option", async () => { - const api = useApi(); + const api = useApi() const payload = { title: "should_add", - }; + } const response = await api .post("/admin/products/test-product/options", payload, { @@ -248,41 +367,41 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ - options: [ + options: expect.arrayContaining([ expect.objectContaining({ title: "should_add", product_id: "test-product", }), - ], + ]), }) - ); - }); - }); + ) + }) + }) describe("testing for soft-deletion + uniqueness on handles, collection and variant properties", () => { beforeEach(async () => { try { - await productSeeder(dbConnection); - await adminSeeder(dbConnection); + await productSeeder(dbConnection) + await adminSeeder(dbConnection) } catch (err) { - console.log(err); - throw err; + console.log(err) + throw err } - }); + }) afterEach(async () => { - const db = useDb(); - await db.teardown(); - }); + const db = useDb() + await db.teardown() + }) it("successfully deletes a product", async () => { - const api = useApi(); + const api = useApi() const response = await api .delete("/admin/products/test-product", { @@ -291,21 +410,21 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data).toEqual( expect.objectContaining({ id: "test-product", deleted: true, }) - ); - }); + ) + }) it("successfully creates product with soft-deleted product handle", async () => { - const api = useApi(); + const api = useApi() // First we soft-delete the product const response = await api @@ -315,11 +434,11 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.id).toEqual("test-product"); + expect(response.status).toEqual(200) + expect(response.data.id).toEqual("test-product") // Lets try to create a product with same handle as deleted one const payload = { @@ -339,20 +458,20 @@ describe("/admin/products", () => { options: [{ value: "large" }, { value: "green" }], }, ], - }; + } const res = await api.post("/admin/products", payload, { headers: { Authorization: "Bearer test_token", }, - }); + }) - expect(res.status).toEqual(200); - expect(res.data.product.handle).toEqual("test-product"); - }); + expect(res.status).toEqual(200) + expect(res.data.product.handle).toEqual("test-product") + }) it("successfully deletes product collection", async () => { - const api = useApi(); + const api = useApi() // First we soft-delete the product collection const response = await api @@ -362,15 +481,15 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.id).toEqual("test-collection"); - }); + expect(response.status).toEqual(200) + expect(response.data.id).toEqual("test-collection") + }) it("successfully creates soft-deleted product collection", async () => { - const api = useApi(); + const api = useApi() const response = await api .delete("/admin/collections/test-collection", { @@ -379,30 +498,43 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.id).toEqual("test-collection"); + expect(response.status).toEqual(200) + expect(response.data.id).toEqual("test-collection") // Lets try to create a product collection with same handle as deleted one const payload = { title: "Another test collection", handle: "test-collection", - }; + } const res = await api.post("/admin/collections", payload, { headers: { Authorization: "Bearer test_token", }, - }); + }) - expect(res.status).toEqual(200); - expect(res.data.collection.handle).toEqual("test-collection"); - }); + expect(res.status).toEqual(200) + expect(res.data.collection.handle).toEqual("test-collection") + }) it("successfully creates soft-deleted product variant", async () => { - const api = useApi(); + const api = useApi() + + console.log("test") + + const product = await api + .get("/admin/products/test-product", { + headers: { + Authorization: "bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + console.log(product.data.product) const response = await api .delete("/admin/products/test-product/variants/test-variant", { @@ -411,11 +543,11 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.variant_id).toEqual("test-variant"); + expect(response.status).toEqual(200) + expect(response.data.variant_id).toEqual("test-variant") // Lets try to create a product collection with same handle as deleted one const payload = { @@ -430,19 +562,18 @@ describe("/admin/products", () => { amount: 100, }, ], - }; + options: [{ option_id: "test-option", value: "inserted value" }], + } - const res = await api.post( - "/admin/products/test-product/variants", - payload, - { + const res = await api + .post("/admin/products/test-product/variants", payload, { headers: { Authorization: "Bearer test_token", }, - } - ); + }) + .catch((err) => console.log(err)) - expect(res.status).toEqual(200); + expect(res.status).toEqual(200) expect(res.data.product.variants).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -453,7 +584,7 @@ describe("/admin/products", () => { barcode: "test-barcode", }), ]) - ); - }); - }); -}); + ) + }) + }) +}) diff --git a/integration-tests/api/helpers/product-seeder.js b/integration-tests/api/helpers/product-seeder.js index 77fd31a034..ae7109b4e6 100644 --- a/integration-tests/api/helpers/product-seeder.js +++ b/integration-tests/api/helpers/product-seeder.js @@ -2,55 +2,58 @@ const { ProductCollection, ProductTag, ProductType, + ProductOption, + ProductOptionValue, Region, Product, ShippingProfile, ProductVariant, Image, -} = require("@medusajs/medusa"); + MoneyAmount, +} = require("@medusajs/medusa") module.exports = async (connection, data = {}) => { - const manager = connection.manager; + const manager = connection.manager const defaultProfile = await manager.findOne(ShippingProfile, { type: "default", - }); + }) const coll = manager.create(ProductCollection, { id: "test-collection", handle: "test-collection", title: "Test collection", - }); + }) - await manager.save(coll); + await manager.save(coll) const tag = manager.create(ProductTag, { id: "tag1", value: "123", - }); + }) - await manager.save(tag); + await manager.save(tag) const type = manager.create(ProductType, { id: "test-type", value: "test-type", - }); + }) - 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.save(image) await manager.insert(Region, { id: "test-region", name: "Test Region", currency_code: "usd", tax_rate: 0, - }); + }) const p = manager.create(Product, { id: "test-product", @@ -64,23 +67,107 @@ module.exports = async (connection, data = {}) => { { id: "tag1", value: "123" }, { tag: "tag2", value: "456" }, ], - options: [{ id: "test-option", title: "Default value" }], - }); + }) - p.images = [image]; + const productOption = manager.create(ProductOption, { + id: "test-option", + title: "test-option", + }) - await manager.save(p); + await manager.save(productOption) - await manager.insert(ProductVariant, { + p.options = [productOption] + + p.images = [image] + + await manager.save(p) + + const variant1 = await manager.create(ProductVariant, { id: "test-variant", inventory_quantity: 10, title: "Test variant", + rank: 0, sku: "test-sku", ean: "test-ean", upc: "test-upc", barcode: "test-barcode", product_id: "test-product", prices: [{ id: "test-price", currency_code: "usd", amount: 100 }], - options: [{ id: "test-variant-option", value: "Default variant" }], - }); -}; + // options: [{ id: "test-variant-option", value: "Default variant" }], + }) + + const variantOption_1 = manager.create(ProductOptionValue, { + id: "test-option-variant1", + option_id: "test-option", + value: "test-option1", + variant_id: "test-variant", + // product_id: "test-product", + }) + + // variant1.options = [variantOption_1] + + await manager.save(variant1) + await manager.save(variantOption_1) + + const variant2 = await manager.create(ProductVariant, { + id: "test-variant_1", + inventory_quantity: 10, + title: "Test variant rank (1)", + rank: 2, + sku: "test-sku1", + ean: "test-ean1", + upc: "test-upc1", + barcode: "test-barcode 1", + product_id: "test-product", + prices: [{ id: "test-price1", currency_code: "usd", amount: 100 }], + // options: [{ id: "test-variant-option-1", value: "Default variant 1" }], + }) + + const variantOption_2 = manager.create(ProductOptionValue, { + id: "test-option-variant2", + option_id: "test-option", + value: "test-option2", + variant_id: "test-variant_1", + // product_id: "test-product", + }) + await manager.save(variant2) + + await manager.save(variantOption_2) + + // variant2.options = [variantOption_2] + + const variant3 = await manager.create(ProductVariant, { + id: "test-variant_2", + inventory_quantity: 10, + title: "Test variant rank (2)", + rank: 1, + sku: "test-sku2", + ean: "test-ean2", + upc: "test-upc2", + product_id: "test-product", + prices: [{ id: "test-price2", currency_code: "usd", amount: 100 }], + // options: [{ id: "test-variant-option-2", value: "Default variant 2" }], + }) + + const variantOption_3 = manager.create(ProductOptionValue, { + id: "test-option-variant3", + option_id: "test-option", + value: "test-option3", + variant_id: "test-variant_2", + // product_id: "test-product", + }) + + await manager.save(variant3) + await manager.save(variantOption_3) + + // variant3.options = [variantOption_3] + + const moneyAmount = await manager.create(MoneyAmount, { + id: "money_amount", + amount: 100, + currency_code: "usd", + variant_id: "test-variant", + }) + + await manager.save(moneyAmount) +} From 32880dbed35d8dcc1dabdfb707b0cc28b37139b6 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Fri, 3 Sep 2021 17:17:43 +0200 Subject: [PATCH 06/21] add ranking of variants to product creation --- .../products/__tests__/create-product.js | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js b/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js index c3aa01cddc..eb6c8d3655 100644 --- a/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js +++ b/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js @@ -1,10 +1,77 @@ import { IdMap } from "medusa-test-utils" import { request } from "../../../../../helpers/test-request" import { ProductServiceMock } from "../../../../../services/__mocks__/product" +import { ProductVariantServiceMock } from "../../../../../services/__mocks__/product-variant" import { ShippingProfileServiceMock } from "../../../../../services/__mocks__/shipping-profile" describe("POST /admin/products", () => { - describe("successful creation", () => { + describe("successful creation with variants", () => { + let subject + + beforeAll(async () => { + subject = await request("POST", "/admin/products", { + payload: { + title: "Test Product with variants", + description: "Test Description", + tags: [{ id: "test", value: "test" }], + handle: "test-product", + options: [{ title: "Test" }], + variants: [ + { + title: "Test", + prices: [ + { + currency_code: "USD", + amount: 100, + }, + ], + options: [ + { + value: "100", + }, + ], + }, + ], + }, + adminSession: { + jwt: { + userId: IdMap.getId("admin_user"), + }, + }, + }) + }) + + it("returns 200", () => { + expect(subject.status).toEqual(200) + }) + + it("assigns invokes productVariantService with ranked variants", () => { + expect(ProductVariantServiceMock.create).toHaveBeenCalledTimes(1) + expect(ProductVariantServiceMock.create).toHaveBeenCalledWith( + IdMap.getId("productWithOptions"), + { + title: "Test", + rank: 0, + prices: [ + { + currency_code: "USD", + amount: 100, + }, + ], + options: [ + { + option_id: IdMap.getId("option1"), + value: "100", + }, + ], + inventory_quantity: 0, + } + ) + expect(true).toEqual(true) + }) + }) + + describe("successful creation test", () => { let subject beforeAll(async () => { @@ -14,6 +81,7 @@ describe("POST /admin/products", () => { description: "Test Description", tags: [{ id: "test", value: "test" }], handle: "test-product", + options: [{ title: "Denominations" }], }, adminSession: { jwt: { @@ -28,6 +96,7 @@ describe("POST /admin/products", () => { }) it("returns created product draft", () => { + console.log(subject.body) expect(subject.body.product.id).toEqual(IdMap.getId("product1")) }) @@ -40,10 +109,24 @@ describe("POST /admin/products", () => { tags: [{ id: "test", value: "test" }], handle: "test-product", is_giftcard: false, + options: [{ title: "Denominations" }], profile_id: IdMap.getId("default_shipping_profile"), }) }) + // it("calls productvariantservice create", () => { + // expect(ProductServiceMock.create).toHaveBeenCalledTimes(1) + // expect(ProductServiceMock.create).toHaveBeenCalledWith({ + // title: "Test Product", + // discountable: true, + // description: "Test Description", + // tags: [{ id: "test", value: "test" }], + // handle: "test-product", + // is_giftcard: false, + // profile_id: IdMap.getId("default_shipping_profile"), + // }) + // }) + it("calls shipping profile default", () => { expect(ShippingProfileServiceMock.retrieveDefault).toHaveBeenCalledTimes( 1 From 3ae6bdb7fcb3b5bf9c2f6ba3504c9a103fdff692 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Fri, 3 Sep 2021 17:18:20 +0200 Subject: [PATCH 07/21] add ranking of variants to product creation implementation --- .../src/api/routes/admin/products/create-product.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/medusa/src/api/routes/admin/products/create-product.js b/packages/medusa/src/api/routes/admin/products/create-product.js index 0d9b928691..779e800109 100644 --- a/packages/medusa/src/api/routes/admin/products/create-product.js +++ b/packages/medusa/src/api/routes/admin/products/create-product.js @@ -327,7 +327,13 @@ export default async (req, res) => { .withTransaction(manager) .create({ ...value, profile_id: shippingProfile.id }) + // console.log(variants) + // console.log(value) + // console.log(newProduct) + if (variants) { + for (const [i, variant] of variants.entries()) variant.rank = i + const optionIds = value.options.map( o => newProduct.options.find(newO => newO.title === o.title).id ) @@ -341,6 +347,7 @@ export default async (req, res) => { option_id: optionIds[index], })), } + await productVariantService .withTransaction(manager) .create(newProduct.id, variant) @@ -349,13 +356,18 @@ export default async (req, res) => { } }) + // console.log("got to after variants") + const product = await productService.retrieve(newProduct.id, { select: defaultFields, relations: defaultRelations, }) + // console.log(product) + res.json({ product }) } catch (err) { + // console.log(err) throw err } } From 73a4746500b2b2c8fa8fa026d20e1e70b54b6456 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Fri, 3 Sep 2021 17:20:11 +0200 Subject: [PATCH 08/21] add cascading deletes to product variant --- packages/medusa/src/models/product-variant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/medusa/src/models/product-variant.ts b/packages/medusa/src/models/product-variant.ts index 7cecfea41f..d3f70ae6ac 100644 --- a/packages/medusa/src/models/product-variant.ts +++ b/packages/medusa/src/models/product-variant.ts @@ -43,7 +43,7 @@ export class ProductVariant { @OneToMany( () => MoneyAmount, ma => ma.variant, - { cascade: true } + { cascade: true, onDelete: "CASCADE" } ) prices: MoneyAmount[] From 13d539b88223769d97cea2e25d0afa63278e7bd5 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Fri, 3 Sep 2021 17:24:06 +0200 Subject: [PATCH 09/21] add rank enforcement at product variant level --- packages/medusa/src/services/product-variant.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/medusa/src/services/product-variant.js b/packages/medusa/src/services/product-variant.js index 8e8a9e8b3c..adb4af2135 100644 --- a/packages/medusa/src/services/product-variant.js +++ b/packages/medusa/src/services/product-variant.js @@ -174,6 +174,8 @@ class ProductVariantService extends BaseService { ) } + if (!rest.rank) rest.rank = 0 + const toCreate = { ...rest, product_id: product.id, From 8fdfc87049f865be5ae2a6db46932e42d84d8a2b Mon Sep 17 00:00:00 2001 From: olivermrbl Date: Sun, 5 Sep 2021 21:31:38 +0200 Subject: [PATCH 10/21] integration tests passing --- .../api/__tests__/admin/product.js | 4 - .../api/helpers/product-seeder.js | 101 ++++++++++-------- .../products/__tests__/create-product.js | 1 - .../routes/admin/products/create-product.js | 9 -- .../1630868939283-product_variant_rank.ts | 14 +++ ...70102483-product_variant_option_cascade.ts | 17 +++ .../medusa/src/models/product-option-value.ts | 3 +- packages/medusa/src/models/product-variant.ts | 2 +- packages/medusa/src/services/product.js | 3 +- 9 files changed, 94 insertions(+), 60 deletions(-) create mode 100644 packages/medusa/src/migrations/1630868939283-product_variant_rank.ts create mode 100644 packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index e179af156d..681fb7a010 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -74,7 +74,6 @@ describe("/admin/products", () => { }) expect(response.status).toEqual(200) - expect(response.data.product).toEqual( expect.objectContaining({ title: "Test product", @@ -523,8 +522,6 @@ describe("/admin/products", () => { it("successfully creates soft-deleted product variant", async () => { const api = useApi() - console.log("test") - const product = await api .get("/admin/products/test-product", { headers: { @@ -534,7 +531,6 @@ describe("/admin/products", () => { .catch((err) => { console.log(err) }) - console.log(product.data.product) const response = await api .delete("/admin/products/test-product/variants/test-variant", { diff --git a/integration-tests/api/helpers/product-seeder.js b/integration-tests/api/helpers/product-seeder.js index ae7109b4e6..ea3de2e4b7 100644 --- a/integration-tests/api/helpers/product-seeder.js +++ b/integration-tests/api/helpers/product-seeder.js @@ -69,19 +69,16 @@ module.exports = async (connection, data = {}) => { ], }) - const productOption = manager.create(ProductOption, { - id: "test-option", - title: "test-option", - }) - - await manager.save(productOption) - - p.options = [productOption] - p.images = [image] await manager.save(p) + await manager.save(ProductOption, { + id: "test-option", + title: "test-option", + product_id: "test-product", + }) + const variant1 = await manager.create(ProductVariant, { id: "test-variant", inventory_quantity: 10, @@ -93,21 +90,27 @@ module.exports = async (connection, data = {}) => { barcode: "test-barcode", product_id: "test-product", prices: [{ id: "test-price", currency_code: "usd", amount: 100 }], - // options: [{ id: "test-variant-option", value: "Default variant" }], + options: [ + { + id: "test-variant-option", + value: "Default variant", + option_id: "test-option", + }, + ], }) - const variantOption_1 = manager.create(ProductOptionValue, { - id: "test-option-variant1", - option_id: "test-option", - value: "test-option1", - variant_id: "test-variant", - // product_id: "test-product", - }) + // const variantOption_1 = manager.create(ProductOptionValue, { + // id: "test-option-variant1", + // option_id: "test-option", + // value: "test-option1", + // variant_id: "test-variant", + // // product_id: "test-product", + // }) // variant1.options = [variantOption_1] await manager.save(variant1) - await manager.save(variantOption_1) + // await manager.save(variantOption_1) const variant2 = await manager.create(ProductVariant, { id: "test-variant_1", @@ -120,19 +123,25 @@ module.exports = async (connection, data = {}) => { barcode: "test-barcode 1", product_id: "test-product", prices: [{ id: "test-price1", currency_code: "usd", amount: 100 }], - // options: [{ id: "test-variant-option-1", value: "Default variant 1" }], + options: [ + { + id: "test-variant-option-1", + value: "Default variant 1", + option_id: "test-option", + }, + ], }) - const variantOption_2 = manager.create(ProductOptionValue, { - id: "test-option-variant2", - option_id: "test-option", - value: "test-option2", - variant_id: "test-variant_1", - // product_id: "test-product", - }) + // const variantOption_2 = manager.create(ProductOptionValue, { + // id: "test-option-variant2", + // option_id: "test-option", + // value: "test-option2", + // variant_id: "test-variant_1", + // // product_id: "test-product", + // }) await manager.save(variant2) - await manager.save(variantOption_2) + // await manager.save(variantOption_2) // variant2.options = [variantOption_2] @@ -146,28 +155,34 @@ module.exports = async (connection, data = {}) => { upc: "test-upc2", product_id: "test-product", prices: [{ id: "test-price2", currency_code: "usd", amount: 100 }], - // options: [{ id: "test-variant-option-2", value: "Default variant 2" }], + options: [ + { + id: "test-variant-option-2", + value: "Default variant 2", + option_id: "test-option", + }, + ], }) - const variantOption_3 = manager.create(ProductOptionValue, { - id: "test-option-variant3", - option_id: "test-option", - value: "test-option3", - variant_id: "test-variant_2", - // product_id: "test-product", - }) + // const variantOption_3 = manager.create(ProductOptionValue, { + // id: "test-option-variant3", + // option_id: "test-option", + // value: "test-option3", + // variant_id: "test-variant_2", + // // product_id: "test-product", + // }) await manager.save(variant3) - await manager.save(variantOption_3) + // await manager.save(variantOption_3) // variant3.options = [variantOption_3] - const moneyAmount = await manager.create(MoneyAmount, { - id: "money_amount", - amount: 100, - currency_code: "usd", - variant_id: "test-variant", - }) + // const moneyAmount = await manager.create(MoneyAmount, { + // id: "money_amount", + // amount: 100, + // currency_code: "usd", + // variant_id: "test-variant", + // }) - await manager.save(moneyAmount) + // await manager.save(moneyAmount) } diff --git a/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js b/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js index eb6c8d3655..f076ffb4e6 100644 --- a/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js +++ b/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js @@ -96,7 +96,6 @@ describe("POST /admin/products", () => { }) it("returns created product draft", () => { - console.log(subject.body) expect(subject.body.product.id).toEqual(IdMap.getId("product1")) }) diff --git a/packages/medusa/src/api/routes/admin/products/create-product.js b/packages/medusa/src/api/routes/admin/products/create-product.js index 779e800109..0f90634ac3 100644 --- a/packages/medusa/src/api/routes/admin/products/create-product.js +++ b/packages/medusa/src/api/routes/admin/products/create-product.js @@ -327,10 +327,6 @@ export default async (req, res) => { .withTransaction(manager) .create({ ...value, profile_id: shippingProfile.id }) - // console.log(variants) - // console.log(value) - // console.log(newProduct) - if (variants) { for (const [i, variant] of variants.entries()) variant.rank = i @@ -356,18 +352,13 @@ export default async (req, res) => { } }) - // console.log("got to after variants") - const product = await productService.retrieve(newProduct.id, { select: defaultFields, relations: defaultRelations, }) - // console.log(product) - res.json({ product }) } catch (err) { - // console.log(err) throw err } } diff --git a/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts b/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts new file mode 100644 index 0000000000..9f76a6596e --- /dev/null +++ b/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts @@ -0,0 +1,14 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class productVariantRank1630868939283 implements MigrationInterface { + name = 'productVariantRank1630868939283' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "product_variant" ADD "rank" integer NOT NULL`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "product_variant" DROP COLUMN "rank"`); + } + +} diff --git a/packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts b/packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts new file mode 100644 index 0000000000..6ab95c0060 --- /dev/null +++ b/packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts @@ -0,0 +1,17 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class productVariantOptionCascade1630870102483 implements MigrationInterface { + name = 'productVariantOptionCascade1630870102483' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`); + await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`); + + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`); + await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + +} diff --git a/packages/medusa/src/models/product-option-value.ts b/packages/medusa/src/models/product-option-value.ts index 64a390bafe..379ad87591 100644 --- a/packages/medusa/src/models/product-option-value.ts +++ b/packages/medusa/src/models/product-option-value.ts @@ -41,7 +41,8 @@ export class ProductOptionValue { @ManyToOne( () => ProductVariant, - variant => variant.options + variant => variant.options, + { onDelete: "cascade" } ) @JoinColumn({ name: "variant_id" }) variant: ProductVariant diff --git a/packages/medusa/src/models/product-variant.ts b/packages/medusa/src/models/product-variant.ts index d3f70ae6ac..a1fd09968a 100644 --- a/packages/medusa/src/models/product-variant.ts +++ b/packages/medusa/src/models/product-variant.ts @@ -43,7 +43,7 @@ export class ProductVariant { @OneToMany( () => MoneyAmount, ma => ma.variant, - { cascade: true, onDelete: "CASCADE" } + { cascade: ["remove"] } ) prices: MoneyAmount[] diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index 779ef8804a..b439fd12b2 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -185,10 +185,11 @@ class ProductService extends BaseService { ) } - if (product.variants) + if (product.variants) { product.variants.sort( (variant1, variant2) => variant1.rank - variant2.rank ) + } return product } From 1a87a463b4cd7071e221a918949388bd96fa8a13 Mon Sep 17 00:00:00 2001 From: olivermrbl Date: Sun, 5 Sep 2021 21:39:00 +0200 Subject: [PATCH 11/21] adds money amount migration --- ...82705-money_amount_product_variant_cascade.ts | 16 ++++++++++++++++ packages/medusa/src/models/money-amount.ts | 6 +++++- packages/medusa/src/models/product-variant.ts | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts diff --git a/packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts b/packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts new file mode 100644 index 0000000000..bcdfd4eceb --- /dev/null +++ b/packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts @@ -0,0 +1,16 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class moneyAmountProductVariantCascade1630870682705 implements MigrationInterface { + name = 'moneyAmountProductVariantCascade1630870682705' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`); + await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`); + await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + +} diff --git a/packages/medusa/src/models/money-amount.ts b/packages/medusa/src/models/money-amount.ts index 29440b5083..b4e87b716d 100644 --- a/packages/medusa/src/models/money-amount.ts +++ b/packages/medusa/src/models/money-amount.ts @@ -44,7 +44,11 @@ export class MoneyAmount { @Column({ nullable: true }) variant_id: string - @ManyToOne(() => ProductVariant) + @ManyToOne( + () => ProductVariant, + variant => variant.prices, + { onDelete: "cascade" } + ) @JoinColumn({ name: "variant_id" }) variant: ProductVariant diff --git a/packages/medusa/src/models/product-variant.ts b/packages/medusa/src/models/product-variant.ts index a1fd09968a..d3f70ae6ac 100644 --- a/packages/medusa/src/models/product-variant.ts +++ b/packages/medusa/src/models/product-variant.ts @@ -43,7 +43,7 @@ export class ProductVariant { @OneToMany( () => MoneyAmount, ma => ma.variant, - { cascade: ["remove"] } + { cascade: true, onDelete: "CASCADE" } ) prices: MoneyAmount[] From 0c47c78c08999cb1e844113e386616429328541e Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Mon, 6 Sep 2021 13:29:24 +0200 Subject: [PATCH 12/21] change variant column 'rank' to 'variant_rank' --- .../api/__tests__/admin/product.js | 10 +-- .../api/helpers/product-seeder.js | 48 +------------- .../products/__tests__/create-product.js | 20 ++---- .../routes/admin/products/create-product.js | 2 +- .../1630868939283-product_variant_rank.ts | 4 +- packages/medusa/src/models/product-variant.ts | 2 +- .../medusa/src/services/__mocks__/product.js | 2 + .../src/services/__tests__/product-variant.js | 1 + .../medusa/src/services/__tests__/product.js | 66 +++++++++++++------ .../medusa/src/services/product-variant.js | 2 +- packages/medusa/src/services/product.js | 7 +- 11 files changed, 72 insertions(+), 92 deletions(-) diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index 681fb7a010..6d49e86fed 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -180,11 +180,11 @@ describe("/admin/products", () => { variants: [ expect.objectContaining({ title: "Test variant 1", - rank: 0, + variant_rank: 0, }), expect.objectContaining({ title: "Test variant 2", - rank: 1, + variant_rank: 1, }), ], }) @@ -332,17 +332,17 @@ describe("/admin/products", () => { variants: [ expect.objectContaining({ id: "test-variant", - rank: 0, + variant_rank: 0, title: "Test variant", }), expect.objectContaining({ id: "test-variant_1", - rank: 1, + variant_rank: 1, title: "Test variant rank (1)", }), expect.objectContaining({ id: "test-variant_2", - rank: 2, + variant_rank: 2, title: "Test variant rank (2)", }), ], diff --git a/integration-tests/api/helpers/product-seeder.js b/integration-tests/api/helpers/product-seeder.js index ea3de2e4b7..c92137cfe6 100644 --- a/integration-tests/api/helpers/product-seeder.js +++ b/integration-tests/api/helpers/product-seeder.js @@ -83,7 +83,7 @@ module.exports = async (connection, data = {}) => { id: "test-variant", inventory_quantity: 10, title: "Test variant", - rank: 0, + variant_rank: 0, sku: "test-sku", ean: "test-ean", upc: "test-upc", @@ -99,24 +99,13 @@ module.exports = async (connection, data = {}) => { ], }) - // const variantOption_1 = manager.create(ProductOptionValue, { - // id: "test-option-variant1", - // option_id: "test-option", - // value: "test-option1", - // variant_id: "test-variant", - // // product_id: "test-product", - // }) - - // variant1.options = [variantOption_1] - await manager.save(variant1) - // await manager.save(variantOption_1) const variant2 = await manager.create(ProductVariant, { id: "test-variant_1", inventory_quantity: 10, title: "Test variant rank (1)", - rank: 2, + variant_rank: 2, sku: "test-sku1", ean: "test-ean1", upc: "test-upc1", @@ -132,24 +121,13 @@ module.exports = async (connection, data = {}) => { ], }) - // const variantOption_2 = manager.create(ProductOptionValue, { - // id: "test-option-variant2", - // option_id: "test-option", - // value: "test-option2", - // variant_id: "test-variant_1", - // // product_id: "test-product", - // }) await manager.save(variant2) - // await manager.save(variantOption_2) - - // variant2.options = [variantOption_2] - const variant3 = await manager.create(ProductVariant, { id: "test-variant_2", inventory_quantity: 10, title: "Test variant rank (2)", - rank: 1, + variant_rank: 1, sku: "test-sku2", ean: "test-ean2", upc: "test-upc2", @@ -164,25 +142,5 @@ module.exports = async (connection, data = {}) => { ], }) - // const variantOption_3 = manager.create(ProductOptionValue, { - // id: "test-option-variant3", - // option_id: "test-option", - // value: "test-option3", - // variant_id: "test-variant_2", - // // product_id: "test-product", - // }) - await manager.save(variant3) - // await manager.save(variantOption_3) - - // variant3.options = [variantOption_3] - - // const moneyAmount = await manager.create(MoneyAmount, { - // id: "money_amount", - // amount: 100, - // currency_code: "usd", - // variant_id: "test-variant", - // }) - - // await manager.save(moneyAmount) } diff --git a/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js b/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js index f076ffb4e6..25a9012395 100644 --- a/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js +++ b/packages/medusa/src/api/routes/admin/products/__tests__/create-product.js @@ -41,6 +41,10 @@ describe("POST /admin/products", () => { }) }) + afterAll(async () => { + jest.clearAllMocks() + }) + it("returns 200", () => { expect(subject.status).toEqual(200) }) @@ -51,7 +55,7 @@ describe("POST /admin/products", () => { IdMap.getId("productWithOptions"), { title: "Test", - rank: 0, + variant_rank: 0, prices: [ { currency_code: "USD", @@ -67,7 +71,6 @@ describe("POST /admin/products", () => { inventory_quantity: 0, } ) - expect(true).toEqual(true) }) }) @@ -113,19 +116,6 @@ describe("POST /admin/products", () => { }) }) - // it("calls productvariantservice create", () => { - // expect(ProductServiceMock.create).toHaveBeenCalledTimes(1) - // expect(ProductServiceMock.create).toHaveBeenCalledWith({ - // title: "Test Product", - // discountable: true, - // description: "Test Description", - // tags: [{ id: "test", value: "test" }], - // handle: "test-product", - // is_giftcard: false, - // profile_id: IdMap.getId("default_shipping_profile"), - // }) - // }) - it("calls shipping profile default", () => { expect(ShippingProfileServiceMock.retrieveDefault).toHaveBeenCalledTimes( 1 diff --git a/packages/medusa/src/api/routes/admin/products/create-product.js b/packages/medusa/src/api/routes/admin/products/create-product.js index 0f90634ac3..95c5f4b3b7 100644 --- a/packages/medusa/src/api/routes/admin/products/create-product.js +++ b/packages/medusa/src/api/routes/admin/products/create-product.js @@ -328,7 +328,7 @@ export default async (req, res) => { .create({ ...value, profile_id: shippingProfile.id }) if (variants) { - for (const [i, variant] of variants.entries()) variant.rank = i + for (const [i, variant] of variants.entries()) variant.variant_rank = i const optionIds = value.options.map( o => newProduct.options.find(newO => newO.title === o.title).id diff --git a/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts b/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts index 9f76a6596e..0461e33c01 100644 --- a/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts +++ b/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts @@ -4,11 +4,11 @@ export class productVariantRank1630868939283 implements MigrationInterface { name = 'productVariantRank1630868939283' public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "product_variant" ADD "rank" integer NOT NULL`); + await queryRunner.query(`ALTER TABLE "product_variant" ADD "variant_rank" integer NOT NULL`); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "product_variant" DROP COLUMN "rank"`); + await queryRunner.query(`ALTER TABLE "product_variant" DROP COLUMN "variant_rank"`); } } diff --git a/packages/medusa/src/models/product-variant.ts b/packages/medusa/src/models/product-variant.ts index d3f70ae6ac..363ef074a8 100644 --- a/packages/medusa/src/models/product-variant.ts +++ b/packages/medusa/src/models/product-variant.ts @@ -64,7 +64,7 @@ export class ProductVariant { upc: string @Column() - rank: number + variant_rank: number @Column({ type: "int" }) inventory_quantity: number diff --git a/packages/medusa/src/services/__mocks__/product.js b/packages/medusa/src/services/__mocks__/product.js index 82fcff22ae..9880d920f2 100644 --- a/packages/medusa/src/services/__mocks__/product.js +++ b/packages/medusa/src/services/__mocks__/product.js @@ -36,6 +36,8 @@ export const ProductServiceMock = { if (data.title === "Test Product") { return Promise.resolve(products.product1) } + if (data.title === "Test Product with variants") + return Promise.resolve(products.productWithOptions) return Promise.resolve({ ...data }) }), diff --git a/packages/medusa/src/services/__tests__/product-variant.js b/packages/medusa/src/services/__tests__/product-variant.js index 08950022fc..ca6d00614a 100644 --- a/packages/medusa/src/services/__tests__/product-variant.js +++ b/packages/medusa/src/services/__tests__/product-variant.js @@ -148,6 +148,7 @@ describe("ProductVariantService", () => { expect(productVariantRepository.create).toHaveBeenCalledWith({ id: IdMap.getId("v2"), product_id: IdMap.getId("ironman"), + variant_rank: 0, options: [ { id: IdMap.getId("test"), diff --git a/packages/medusa/src/services/__tests__/product.js b/packages/medusa/src/services/__tests__/product.js index 4f94b92494..2cc3a90918 100644 --- a/packages/medusa/src/services/__tests__/product.js +++ b/packages/medusa/src/services/__tests__/product.js @@ -16,14 +16,14 @@ describe("ProductService", () => { return { id: "test id with variants", variants: [ - { id: "test_321", title: "Green", rank: 1 }, - { id: "test_123", title: "Blue", rank: 0 }, + { id: "test_321", title: "Green", variant_rank: 1 }, + { id: "test_123", title: "Blue", variant_rank: 0 }, ], } if (query.where.id === "test id one variant") return { id: "test id one variant", - variants: [{ id: "test_123", title: "Blue", rank: 0 }], + variants: [{ id: "test_123", title: "Blue", variant_rank: 0 }], } return Promise.resolve({ id: IdMap.getId("ironman") }) }, @@ -54,8 +54,8 @@ describe("ProductService", () => { const expected = { id: "test id with variants", variants: [ - { id: "test_123", title: "Blue", rank: 0 }, - { id: "test_321", title: "Green", rank: 1 }, + { id: "test_123", title: "Blue", variant_rank: 0 }, + { id: "test_321", title: "Green", variant_rank: 1 }, ], } @@ -138,8 +138,14 @@ describe("ProductService", () => { tags: [{ value: "title" }, { value: "title2" }], type: "type-1", variants: [ - { id: "test1", title: "green", rank: 0 }, - { id: "test2", title: "blue", rank: 0 }, + { + id: "test1", + title: "green", + }, + { + id: "test2", + title: "blue", + }, ], }) @@ -153,8 +159,16 @@ describe("ProductService", () => { expect(productRepository.create).toHaveBeenCalledWith({ title: "Suit", variants: [ - { id: "test1", title: "green", rank: 0 }, - { id: "test2", title: "blue", rank: 1 }, + { + id: "test1", + title: "green", + variant_rank: 0, + }, + { + id: "test2", + title: "blue", + variant_rank: 1, + }, ], }) @@ -172,8 +186,14 @@ describe("ProductService", () => { title: "Suit", options: [], tags: [ - { id: "tag-1", value: "title" }, - { id: "tag-2", value: "title2" }, + { + id: "tag-1", + value: "title", + }, + { + id: "tag-2", + value: "title2", + }, ], type_id: "type", collection: { @@ -181,8 +201,16 @@ describe("ProductService", () => { title: "Suits", }, variants: [ - { id: "test1", title: "green", rank: 0 }, - { id: "test2", title: "blue", rank: 1 }, + { + id: "test1", + title: "green", + variant_rank: 0, + }, + { + id: "test2", + title: "blue", + variant_rank: 1, + }, ], }) }) @@ -204,8 +232,8 @@ describe("ProductService", () => { return Promise.resolve({ id: "ranking test", variants: [ - { id: "test_321", title: "Greener", rank: 1 }, - { id: "test_123", title: "Blueer", rank: 0 }, + { id: "test_321", title: "Greener", variant_rank: 1 }, + { id: "test_123", title: "Blueer", variant_rank: 0 }, ], }) } @@ -317,8 +345,8 @@ describe("ProductService", () => { it("successfully updates variant ranking", async () => { await productService.update("ranking test", { variants: [ - { id: "test_321", title: "Greener", rank: 1 }, - { id: "test_123", title: "Blueer", rank: 0 }, + { id: "test_321", title: "Greener", variant_rank: 1 }, + { id: "test_123", title: "Blueer", variant_rank: 0 }, ], }) @@ -332,8 +360,8 @@ describe("ProductService", () => { expect(productRepository.save).toHaveBeenCalledWith({ id: "ranking test", variants: [ - { id: "test_321", title: "Greener", rank: 0 }, - { id: "test_123", title: "Blueer", rank: 1 }, + { id: "test_321", title: "Greener", variant_rank: 0 }, + { id: "test_123", title: "Blueer", variant_rank: 1 }, ], }) }) diff --git a/packages/medusa/src/services/product-variant.js b/packages/medusa/src/services/product-variant.js index adb4af2135..db7373ecc9 100644 --- a/packages/medusa/src/services/product-variant.js +++ b/packages/medusa/src/services/product-variant.js @@ -174,7 +174,7 @@ class ProductVariantService extends BaseService { ) } - if (!rest.rank) rest.rank = 0 + if (!rest.variant_rank) rest.variant_rank = 0 const toCreate = { ...rest, diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index b439fd12b2..dc020b45c7 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -187,7 +187,7 @@ class ProductService extends BaseService { if (product.variants) { product.variants.sort( - (variant1, variant2) => variant1.rank - variant2.rank + (variant1, variant2) => variant1.variant_rank - variant2.variant_rank ) } @@ -300,7 +300,8 @@ class ProductService extends BaseService { } if (rest.variants) - for (const [i, variant] of rest.variants.entries()) variant.rank = i + for (const [i, variant] of rest.variants.entries()) + variant.variant_rank = i let product = productRepo.create(rest) @@ -420,7 +421,7 @@ class ProductService extends BaseService { const newVariants = [] for (const [i, newVariant] of variants.entries()) { - newVariant.rank = i + newVariant.variant_rank = i if (newVariant.id) { const variant = product.variants.find(v => v.id === newVariant.id) From ddffd45071504ad25373cbf7e164aeff4e9b479c Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Mon, 6 Sep 2021 13:38:22 +0200 Subject: [PATCH 13/21] removed unused imports --- integration-tests/api/helpers/product-seeder.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration-tests/api/helpers/product-seeder.js b/integration-tests/api/helpers/product-seeder.js index c92137cfe6..60626ce68b 100644 --- a/integration-tests/api/helpers/product-seeder.js +++ b/integration-tests/api/helpers/product-seeder.js @@ -3,13 +3,11 @@ const { ProductTag, ProductType, ProductOption, - ProductOptionValue, Region, Product, ShippingProfile, ProductVariant, Image, - MoneyAmount, } = require("@medusajs/medusa") module.exports = async (connection, data = {}) => { From 5d9d34edf469bb2858996d2d9df44383bce9d822 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Mon, 6 Sep 2021 14:20:00 +0200 Subject: [PATCH 14/21] Create variant appends variant in rankings --- packages/medusa/src/services/__tests__/product-variant.js | 2 +- packages/medusa/src/services/product-variant.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/medusa/src/services/__tests__/product-variant.js b/packages/medusa/src/services/__tests__/product-variant.js index ca6d00614a..dbd0054eed 100644 --- a/packages/medusa/src/services/__tests__/product-variant.js +++ b/packages/medusa/src/services/__tests__/product-variant.js @@ -148,7 +148,7 @@ describe("ProductVariantService", () => { expect(productVariantRepository.create).toHaveBeenCalledWith({ id: IdMap.getId("v2"), product_id: IdMap.getId("ironman"), - variant_rank: 0, + variant_rank: 1, options: [ { id: IdMap.getId("test"), diff --git a/packages/medusa/src/services/product-variant.js b/packages/medusa/src/services/product-variant.js index db7373ecc9..922d607a37 100644 --- a/packages/medusa/src/services/product-variant.js +++ b/packages/medusa/src/services/product-variant.js @@ -174,7 +174,7 @@ class ProductVariantService extends BaseService { ) } - if (!rest.variant_rank) rest.variant_rank = 0 + if (!rest.variant_rank) rest.variant_rank = product.variants.length const toCreate = { ...rest, From ecad9db924e325dd9b23e51029e801be4156c72c Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Wed, 8 Sep 2021 14:12:27 +0200 Subject: [PATCH 15/21] product-list snapshot test --- .../admin/__snapshots__/product.js.snap | 387 ++++++++++++++++++ .../api/__tests__/admin/product.js | 233 ++++++++++- .../api/helpers/product-seeder.js | 58 +++ 3 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 integration-tests/api/__tests__/admin/__snapshots__/product.js.snap diff --git a/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap b/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap new file mode 100644 index 0000000000..830f17fc33 --- /dev/null +++ b/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap @@ -0,0 +1,387 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`/admin/products GET /admin/products returns a list of products with child entities 1`] = ` +Array [ + Object { + "collection": Object { + "created_at": Any, + "deleted_at": null, + "handle": "test-collection", + "id": StringMatching /\\^test-\\*/, + "metadata": null, + "title": "Test collection", + "updated_at": Any, + }, + "collection_id": "test-collection", + "created_at": Any, + "deleted_at": null, + "description": "test-product-description", + "discountable": true, + "handle": "test-product", + "height": null, + "hs_code": null, + "id": StringMatching /\\^test-\\*/, + "images": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-\\*/, + "metadata": null, + "updated_at": Any, + "url": "test-image.png", + }, + ], + "is_giftcard": false, + "length": null, + "material": null, + "metadata": null, + "mid_code": null, + "options": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-\\*/, + "metadata": null, + "product_id": StringMatching /\\^test-\\*/, + "title": "test-option", + "updated_at": Any, + }, + ], + "origin_country": null, + "profile_id": StringMatching /\\^sp_\\*/, + "subtitle": null, + "tags": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^tag\\*/, + "metadata": null, + "updated_at": Any, + "value": "123", + }, + ], + "thumbnail": null, + "title": "Test product", + "type": Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-\\*/, + "metadata": null, + "updated_at": Any, + "value": "test-type", + }, + "type_id": "test-type", + "updated_at": Any, + "variants": Array [ + Object { + "allow_backorder": false, + "barcode": "test-barcode", + "created_at": Any, + "deleted_at": null, + "ean": "test-ean", + "height": null, + "hs_code": null, + "id": StringMatching /\\^test-variant\\*/, + "inventory_quantity": 10, + "length": null, + "manage_inventory": true, + "material": null, + "metadata": null, + "mid_code": null, + "options": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-variant-option\\*/, + "metadata": null, + "option_id": StringMatching /\\^test-opt\\*/, + "updated_at": Any, + "value": "Default variant", + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "origin_country": null, + "prices": Array [ + Object { + "amount": 100, + "created_at": Any, + "currency_code": "usd", + "deleted_at": null, + "id": StringMatching /\\^test-price\\*/, + "region_id": null, + "sale_amount": null, + "updated_at": Any, + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "product_id": StringMatching /\\^test-\\*/, + "sku": "test-sku", + "title": "Test variant", + "upc": "test-upc", + "updated_at": Any, + "variant_rank": 0, + "weight": null, + "width": null, + }, + Object { + "allow_backorder": false, + "barcode": null, + "created_at": Any, + "deleted_at": null, + "ean": "test-ean2", + "height": null, + "hs_code": null, + "id": StringMatching /\\^test-variant\\*/, + "inventory_quantity": 10, + "length": null, + "manage_inventory": true, + "material": null, + "metadata": null, + "mid_code": null, + "options": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-variant-option\\*/, + "metadata": null, + "option_id": StringMatching /\\^test-opt\\*/, + "updated_at": Any, + "value": "Default variant 2", + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "origin_country": null, + "prices": Array [ + Object { + "amount": 100, + "created_at": Any, + "currency_code": "usd", + "deleted_at": null, + "id": StringMatching /\\^test-price\\*/, + "region_id": null, + "sale_amount": null, + "updated_at": Any, + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "product_id": StringMatching /\\^test-\\*/, + "sku": "test-sku2", + "title": "Test variant rank (2)", + "upc": "test-upc2", + "updated_at": Any, + "variant_rank": 1, + "weight": null, + "width": null, + }, + Object { + "allow_backorder": false, + "barcode": "test-barcode 1", + "created_at": Any, + "deleted_at": null, + "ean": "test-ean1", + "height": null, + "hs_code": null, + "id": StringMatching /\\^test-variant\\*/, + "inventory_quantity": 10, + "length": null, + "manage_inventory": true, + "material": null, + "metadata": null, + "mid_code": null, + "options": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-variant-option\\*/, + "metadata": null, + "option_id": StringMatching /\\^test-opt\\*/, + "updated_at": Any, + "value": "Default variant 1", + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "origin_country": null, + "prices": Array [ + Object { + "amount": 100, + "created_at": Any, + "currency_code": "usd", + "deleted_at": null, + "id": StringMatching /\\^test-price\\*/, + "region_id": null, + "sale_amount": null, + "updated_at": Any, + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "product_id": StringMatching /\\^test-\\*/, + "sku": "test-sku1", + "title": "Test variant rank (1)", + "upc": "test-upc1", + "updated_at": Any, + "variant_rank": 2, + "weight": null, + "width": null, + }, + ], + "weight": null, + "width": null, + }, + Object { + "collection": Object { + "created_at": Any, + "deleted_at": null, + "handle": "test-collection", + "id": StringMatching /\\^test-\\*/, + "metadata": null, + "title": "Test collection", + "updated_at": Any, + }, + "collection_id": "test-collection", + "created_at": Any, + "deleted_at": null, + "description": "test-product-description1", + "discountable": true, + "handle": "test-product1", + "height": null, + "hs_code": null, + "id": StringMatching /\\^test-\\*/, + "images": Array [], + "is_giftcard": false, + "length": null, + "material": null, + "metadata": null, + "mid_code": null, + "options": Array [], + "origin_country": null, + "profile_id": StringMatching /\\^sp_\\*/, + "subtitle": null, + "tags": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^tag\\*/, + "metadata": null, + "updated_at": Any, + "value": "123", + }, + ], + "thumbnail": null, + "title": "Test product1", + "type": Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-\\*/, + "metadata": null, + "updated_at": Any, + "value": "test-type", + }, + "type_id": "test-type", + "updated_at": Any, + "variants": Array [ + Object { + "allow_backorder": false, + "barcode": null, + "created_at": Any, + "deleted_at": null, + "ean": "test-ean4", + "height": null, + "hs_code": null, + "id": StringMatching /\\^test-variant\\*/, + "inventory_quantity": 10, + "length": null, + "manage_inventory": true, + "material": null, + "metadata": null, + "mid_code": null, + "options": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-variant-option\\*/, + "metadata": null, + "option_id": StringMatching /\\^test-opt\\*/, + "updated_at": Any, + "value": "Default variant 3", + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "origin_country": null, + "prices": Array [ + Object { + "amount": 100, + "created_at": Any, + "currency_code": "usd", + "deleted_at": null, + "id": StringMatching /\\^test-price\\*/, + "region_id": null, + "sale_amount": null, + "updated_at": Any, + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "product_id": StringMatching /\\^test-\\*/, + "sku": "test-sku4", + "title": "Test variant rank (2)", + "upc": "test-upc4", + "updated_at": Any, + "variant_rank": 0, + "weight": null, + "width": null, + }, + Object { + "allow_backorder": false, + "barcode": null, + "created_at": Any, + "deleted_at": null, + "ean": "test-ean3", + "height": null, + "hs_code": null, + "id": StringMatching /\\^test-variant\\*/, + "inventory_quantity": 10, + "length": null, + "manage_inventory": true, + "material": null, + "metadata": null, + "mid_code": null, + "options": Array [ + Object { + "created_at": Any, + "deleted_at": null, + "id": StringMatching /\\^test-variant-option\\*/, + "metadata": null, + "option_id": StringMatching /\\^test-opt\\*/, + "updated_at": Any, + "value": "Default variant 3", + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "origin_country": null, + "prices": Array [ + Object { + "amount": 100, + "created_at": Any, + "currency_code": "usd", + "deleted_at": null, + "id": StringMatching /\\^test-price\\*/, + "region_id": null, + "sale_amount": null, + "updated_at": Any, + "variant_id": StringMatching /\\^test-variant\\*/, + }, + ], + "product_id": StringMatching /\\^test-\\*/, + "sku": "test-sku3", + "title": "Test variant rank (2)", + "upc": "test-upc3", + "updated_at": Any, + "variant_rank": 1, + "weight": null, + "width": null, + }, + ], + "weight": null, + "width": null, + }, +] +`; diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index 6d49e86fed..a63f821bd2 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -16,7 +16,7 @@ describe("/admin/products", () => { beforeAll(async () => { const cwd = path.resolve(path.join(__dirname, "..", "..")) dbConnection = await initDb({ cwd }) - medusaProcess = await setupServer({ cwd }) + medusaProcess = await setupServer({ cwd, verbose: true }) }) afterAll(async () => { @@ -26,6 +26,237 @@ describe("/admin/products", () => { medusaProcess.kill() }) + describe("GET /admin/products", () => { + beforeEach(async () => { + try { + await productSeeder(dbConnection) + await adminSeeder(dbConnection) + } catch (err) { + console.log(err) + throw err + } + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it.only("returns a list of products with child entities", async () => { + const api = useApi() + + const response = await api + .get("/admin/products", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + const testProduct = response.data.products[0] + + console.log(JSON.stringify(testProduct, null, 2)) + + const testProduct1 = response.data.products[1] + + console.log(JSON.stringify(testProduct1, null, 2)) + // console.log(testProduct.variants) + + expect(response.data.products).toMatchSnapshot([ + { + id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + options: [ + { + id: expect.stringMatching(/^test-*/), + product_id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + images: [ + { + id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + variants: [ + { + id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + variant_rank: 0, + product_id: expect.stringMatching(/^test-*/), + prices: [ + { + id: expect.stringMatching(/^test-price*/), + variant_id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + options: [ + { + id: expect.stringMatching(/^test-variant-option*/), + variant_id: expect.stringMatching(/^test-variant*/), + option_id: expect.stringMatching(/^test-opt*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + }, + { + id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + variant_rank: 1, + product_id: expect.stringMatching(/^test-*/), + prices: [ + { + id: expect.stringMatching(/^test-price*/), + variant_id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + options: [ + { + id: expect.stringMatching(/^test-variant-option*/), + variant_id: expect.stringMatching(/^test-variant*/), + option_id: expect.stringMatching(/^test-opt*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + }, + { + id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + variant_rank: 2, + product_id: expect.stringMatching(/^test-*/), + prices: [ + { + id: expect.stringMatching(/^test-price*/), + variant_id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + options: [ + { + id: expect.stringMatching(/^test-variant-option*/), + variant_id: expect.stringMatching(/^test-variant*/), + option_id: expect.stringMatching(/^test-opt*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + }, + ], + tags: [ + { + id: expect.stringMatching(/^tag*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + type: { + id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + collection: { + id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + profile_id: expect.stringMatching(/^sp_*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + { + id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + options: [], + variants: [ + { + id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + variant_rank: 0, + product_id: expect.stringMatching(/^test-*/), + prices: [ + { + id: expect.stringMatching(/^test-price*/), + variant_id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + options: [ + { + id: expect.stringMatching(/^test-variant-option*/), + variant_id: expect.stringMatching(/^test-variant*/), + option_id: expect.stringMatching(/^test-opt*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + }, + { + id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + variant_rank: 1, + product_id: expect.stringMatching(/^test-*/), + prices: [ + { + id: expect.stringMatching(/^test-price*/), + variant_id: expect.stringMatching(/^test-variant*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + options: [ + { + id: expect.stringMatching(/^test-variant-option*/), + variant_id: expect.stringMatching(/^test-variant*/), + option_id: expect.stringMatching(/^test-opt*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + }, + ], + tags: [ + { + id: expect.stringMatching(/^tag*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + type: { + id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + collection: { + id: expect.stringMatching(/^test-*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + profile_id: expect.stringMatching(/^sp_*/), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ]) + }) + }) + describe("POST /admin/products", () => { beforeEach(async () => { try { diff --git a/integration-tests/api/helpers/product-seeder.js b/integration-tests/api/helpers/product-seeder.js index 60626ce68b..cc69255038 100644 --- a/integration-tests/api/helpers/product-seeder.js +++ b/integration-tests/api/helpers/product-seeder.js @@ -141,4 +141,62 @@ module.exports = async (connection, data = {}) => { }) await manager.save(variant3) + + const p1 = manager.create(Product, { + id: "test-product1", + handle: "test-product1", + title: "Test product1", + profile_id: defaultProfile.id, + description: "test-product-description1", + collection_id: "test-collection", + type: { id: "test-type", value: "test-type" }, + tags: [ + { id: "tag1", value: "123" }, + { tag: "tag2", value: "456" }, + ], + }) + + await manager.save(p1) + + const variant4 = await manager.create(ProductVariant, { + id: "test-variant_3", + inventory_quantity: 10, + title: "Test variant rank (2)", + variant_rank: 1, + sku: "test-sku3", + ean: "test-ean3", + upc: "test-upc3", + product_id: "test-product1", + prices: [{ id: "test-price3", currency_code: "usd", amount: 100 }], + options: [ + { + id: "test-variant-option-3", + value: "Default variant 3", + option_id: "test-option", + }, + ], + }) + + await manager.save(variant4) + + const variant5 = await manager.create(ProductVariant, { + id: "test-variant_4", + inventory_quantity: 10, + title: "Test variant rank (2)", + variant_rank: 0, + sku: "test-sku4", + ean: "test-ean4", + upc: "test-upc4", + product_id: "test-product1", + prices: [{ id: "test-price4", currency_code: "usd", amount: 100 }], + options: [ + { + id: "test-variant-option-4", + value: "Default variant 3", + option_id: "test-option", + }, + ], + }) + + await manager.save(variant5) } From 6bd0d4458cdf12fabc00187afb1e0504b883758c Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Wed, 8 Sep 2021 14:14:28 +0200 Subject: [PATCH 16/21] moved variant sorting from service to the repository --- packages/medusa/src/repositories/product.ts | 32 +++++++++++++++++---- packages/medusa/src/services/product.js | 10 ------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/packages/medusa/src/repositories/product.ts b/packages/medusa/src/repositories/product.ts index dd1dc83c8f..f565c86751 100644 --- a/packages/medusa/src/repositories/product.ts +++ b/packages/medusa/src/repositories/product.ts @@ -19,7 +19,7 @@ export class ProductRepository extends Repository { } const entitiesIds = entities.map(({ id }) => id) - const groupedRelations = {} + const groupedRelations : { [toplevel: string]: string[]} = {} for (const rel of relations) { const [topLevel] = rel.split(".") if (groupedRelations[topLevel]) { @@ -30,13 +30,33 @@ export class ProductRepository extends Repository { } const entitiesIdsWithRelations = await Promise.all( - Object.entries(groupedRelations).map(([_, rels]) => { - return this.findByIds(entitiesIds, { - select: ["id"], - relations: rels as string[], - }) + Object.entries(groupedRelations).map(([toplevel, rels]) => { + let querybuilder = this.createQueryBuilder("products") + + if (toplevel === "variants") { + querybuilder = querybuilder.leftJoinAndSelect(`products.${toplevel}`, toplevel, "variants.deleted_at IS NULL") + .orderBy({ + "variants.variant_rank": "ASC", + }) + } else { + querybuilder = querybuilder.leftJoinAndSelect(`products.${toplevel}`, toplevel) + } + + for(const rel of rels) { + const [_, rest] = rel.split(".") + if (!rest) { + continue + } + // Regex matches all '.' except the rightmost + querybuilder = querybuilder.leftJoinAndSelect(rel.replace(/\.(?=[^.]*\.)/g,"__"), rel.replace(".", "__")) + } + + return querybuilder + .where("products.deleted_at IS NULL AND products.id IN (:...entitiesIds)", { entitiesIds }) + .getMany(); }) ).then(flatten) + const entitiesAndRelations = entitiesIdsWithRelations.concat(entities) const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id") diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index dc020b45c7..45b7375af4 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -185,12 +185,6 @@ class ProductService extends BaseService { ) } - if (product.variants) { - product.variants.sort( - (variant1, variant2) => variant1.variant_rank - variant2.variant_rank - ) - } - return product } @@ -299,10 +293,6 @@ class ProductService extends BaseService { rest.discountable = false } - if (rest.variants) - for (const [i, variant] of rest.variants.entries()) - variant.variant_rank = i - let product = productRepo.create(rest) if (images && images.length) { From f5ee8708547b866502d26a77b06a09de7303657b Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Wed, 8 Sep 2021 15:04:03 +0200 Subject: [PATCH 17/21] product variant test adjustsments after variant was removed from the api --- .../admin/__snapshots__/product.js.snap | 15 ++---- .../api/__tests__/admin/product.js | 47 ++++++++----------- 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap b/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap index 830f17fc33..ea00dae66d 100644 --- a/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap +++ b/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap @@ -81,7 +81,7 @@ Array [ "ean": "test-ean", "height": null, "hs_code": null, - "id": StringMatching /\\^test-variant\\*/, + "id": "test-variant", "inventory_quantity": 10, "length": null, "manage_inventory": true, @@ -119,7 +119,6 @@ Array [ "title": "Test variant", "upc": "test-upc", "updated_at": Any, - "variant_rank": 0, "weight": null, "width": null, }, @@ -131,7 +130,7 @@ Array [ "ean": "test-ean2", "height": null, "hs_code": null, - "id": StringMatching /\\^test-variant\\*/, + "id": "test-variant_2", "inventory_quantity": 10, "length": null, "manage_inventory": true, @@ -169,7 +168,6 @@ Array [ "title": "Test variant rank (2)", "upc": "test-upc2", "updated_at": Any, - "variant_rank": 1, "weight": null, "width": null, }, @@ -181,7 +179,7 @@ Array [ "ean": "test-ean1", "height": null, "hs_code": null, - "id": StringMatching /\\^test-variant\\*/, + "id": "test-variant_1", "inventory_quantity": 10, "length": null, "manage_inventory": true, @@ -219,7 +217,6 @@ Array [ "title": "Test variant rank (1)", "upc": "test-upc1", "updated_at": Any, - "variant_rank": 2, "weight": null, "width": null, }, @@ -287,7 +284,7 @@ Array [ "ean": "test-ean4", "height": null, "hs_code": null, - "id": StringMatching /\\^test-variant\\*/, + "id": "test-variant_4", "inventory_quantity": 10, "length": null, "manage_inventory": true, @@ -325,7 +322,6 @@ Array [ "title": "Test variant rank (2)", "upc": "test-upc4", "updated_at": Any, - "variant_rank": 0, "weight": null, "width": null, }, @@ -337,7 +333,7 @@ Array [ "ean": "test-ean3", "height": null, "hs_code": null, - "id": StringMatching /\\^test-variant\\*/, + "id": "test-variant_3", "inventory_quantity": 10, "length": null, "manage_inventory": true, @@ -375,7 +371,6 @@ Array [ "title": "Test variant rank (2)", "upc": "test-upc3", "updated_at": Any, - "variant_rank": 1, "weight": null, "width": null, }, diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index a63f821bd2..c57ff82ffd 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -42,7 +42,7 @@ describe("/admin/products", () => { await db.teardown() }) - it.only("returns a list of products with child entities", async () => { + it("returns a list of products with child entities", async () => { const api = useApi() const response = await api @@ -55,15 +55,6 @@ describe("/admin/products", () => { console.log(err) }) - const testProduct = response.data.products[0] - - console.log(JSON.stringify(testProduct, null, 2)) - - const testProduct1 = response.data.products[1] - - console.log(JSON.stringify(testProduct1, null, 2)) - // console.log(testProduct.variants) - expect(response.data.products).toMatchSnapshot([ { id: expect.stringMatching(/^test-*/), @@ -85,10 +76,9 @@ describe("/admin/products", () => { ], variants: [ { - id: expect.stringMatching(/^test-variant*/), + id: "test-variant", //expect.stringMatching(/^test-variant*/), created_at: expect.any(String), updated_at: expect.any(String), - variant_rank: 0, product_id: expect.stringMatching(/^test-*/), prices: [ { @@ -109,10 +99,9 @@ describe("/admin/products", () => { ], }, { - id: expect.stringMatching(/^test-variant*/), + id: "test-variant_2", //expect.stringMatching(/^test-variant*/), created_at: expect.any(String), updated_at: expect.any(String), - variant_rank: 1, product_id: expect.stringMatching(/^test-*/), prices: [ { @@ -133,10 +122,9 @@ describe("/admin/products", () => { ], }, { - id: expect.stringMatching(/^test-variant*/), + id: "test-variant_1", // expect.stringMatching(/^test-variant*/), created_at: expect.any(String), updated_at: expect.any(String), - variant_rank: 2, product_id: expect.stringMatching(/^test-*/), prices: [ { @@ -184,10 +172,9 @@ describe("/admin/products", () => { options: [], variants: [ { - id: expect.stringMatching(/^test-variant*/), + id: "test-variant_4", //expect.stringMatching(/^test-variant*/), created_at: expect.any(String), updated_at: expect.any(String), - variant_rank: 0, product_id: expect.stringMatching(/^test-*/), prices: [ { @@ -208,10 +195,9 @@ describe("/admin/products", () => { ], }, { - id: expect.stringMatching(/^test-variant*/), + id: "test-variant_3", //expect.stringMatching(/^test-variant*/), created_at: expect.any(String), updated_at: expect.any(String), - variant_rank: 1, product_id: expect.stringMatching(/^test-*/), prices: [ { @@ -393,7 +379,7 @@ describe("/admin/products", () => { ], } - const response = await api + const creationResponse = await api .post("/admin/products", payload, { headers: { Authorization: "Bearer test_token", @@ -403,7 +389,19 @@ describe("/admin/products", () => { console.log(err) }) - expect(response.status).toEqual(200) + expect(creationResponse.status).toEqual(200) + + const productId = creationResponse.data.product.id + + const response = await api + .get(`/admin/products/${productId}`, { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) expect(response.data.product).toEqual( expect.objectContaining({ @@ -411,11 +409,9 @@ describe("/admin/products", () => { variants: [ expect.objectContaining({ title: "Test variant 1", - variant_rank: 0, }), expect.objectContaining({ title: "Test variant 2", - variant_rank: 1, }), ], }) @@ -563,17 +559,14 @@ describe("/admin/products", () => { variants: [ expect.objectContaining({ id: "test-variant", - variant_rank: 0, title: "Test variant", }), expect.objectContaining({ id: "test-variant_1", - variant_rank: 1, title: "Test variant rank (1)", }), expect.objectContaining({ id: "test-variant_2", - variant_rank: 2, title: "Test variant rank (2)", }), ], From 8e6bd6c97ec106d0e699d32c9ea12dda8f05d41c Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Wed, 8 Sep 2021 15:07:22 +0200 Subject: [PATCH 18/21] nullable variant_rank with defaulvalue of 0 excluded from select queries in database --- .../1630868939283-product_variant_rank.ts | 14 ----------- ...70102483-product_variant_option_cascade.ts | 17 -------------- ...05-money_amount_product_variant_cascade.ts | 16 ------------- ...631104895519-RankColumnWithDefaultValue.ts | 23 +++++++++++++++++++ packages/medusa/src/models/product-variant.ts | 2 +- 5 files changed, 24 insertions(+), 48 deletions(-) delete mode 100644 packages/medusa/src/migrations/1630868939283-product_variant_rank.ts delete mode 100644 packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts delete mode 100644 packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts create mode 100644 packages/medusa/src/migrations/1631104895519-RankColumnWithDefaultValue.ts diff --git a/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts b/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts deleted file mode 100644 index 0461e33c01..0000000000 --- a/packages/medusa/src/migrations/1630868939283-product_variant_rank.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {MigrationInterface, QueryRunner} from "typeorm"; - -export class productVariantRank1630868939283 implements MigrationInterface { - name = 'productVariantRank1630868939283' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "product_variant" ADD "variant_rank" integer NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "product_variant" DROP COLUMN "variant_rank"`); - } - -} diff --git a/packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts b/packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts deleted file mode 100644 index 6ab95c0060..0000000000 --- a/packages/medusa/src/migrations/1630870102483-product_variant_option_cascade.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {MigrationInterface, QueryRunner} from "typeorm"; - -export class productVariantOptionCascade1630870102483 implements MigrationInterface { - name = 'productVariantOptionCascade1630870102483' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`); - await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`); - - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`); - await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts b/packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts deleted file mode 100644 index bcdfd4eceb..0000000000 --- a/packages/medusa/src/migrations/1630870682705-money_amount_product_variant_cascade.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {MigrationInterface, QueryRunner} from "typeorm"; - -export class moneyAmountProductVariantCascade1630870682705 implements MigrationInterface { - name = 'moneyAmountProductVariantCascade1630870682705' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`); - await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`); - await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/packages/medusa/src/migrations/1631104895519-RankColumnWithDefaultValue.ts b/packages/medusa/src/migrations/1631104895519-RankColumnWithDefaultValue.ts new file mode 100644 index 0000000000..75b501cc38 --- /dev/null +++ b/packages/medusa/src/migrations/1631104895519-RankColumnWithDefaultValue.ts @@ -0,0 +1,23 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class RankColumnWithDefaultValue1631104895519 implements MigrationInterface { + name = 'RankColumnWithDefaultValue1631104895519' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "product_variant" ADD "variant_rank" integer DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`); + await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`); + await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "product_variant" DROP COLUMN "variant_rank"`); + await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`); + await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`); + await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + + } + +} diff --git a/packages/medusa/src/models/product-variant.ts b/packages/medusa/src/models/product-variant.ts index 363ef074a8..94c10fa104 100644 --- a/packages/medusa/src/models/product-variant.ts +++ b/packages/medusa/src/models/product-variant.ts @@ -63,7 +63,7 @@ export class ProductVariant { @Index({ unique: true, where: "deleted_at IS NOT NULL" }) upc: string - @Column() + @Column({ nullable: true, default: 0, select:false }) variant_rank: number @Column({ type: "int" }) From 976b57f944ecdabd22ab7b93618322ee7585ccd9 Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Wed, 8 Sep 2021 15:19:46 +0200 Subject: [PATCH 19/21] adjusted product tests after removing sorting and public variant ranks --- .../medusa/src/services/__tests__/product.js | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/packages/medusa/src/services/__tests__/product.js b/packages/medusa/src/services/__tests__/product.js index 2cc3a90918..4f4033d7a3 100644 --- a/packages/medusa/src/services/__tests__/product.js +++ b/packages/medusa/src/services/__tests__/product.js @@ -16,14 +16,14 @@ describe("ProductService", () => { return { id: "test id with variants", variants: [ - { id: "test_321", title: "Green", variant_rank: 1 }, - { id: "test_123", title: "Blue", variant_rank: 0 }, + { id: "test_321", title: "Green" }, + { id: "test_123", title: "Blue" }, ], } if (query.where.id === "test id one variant") return { id: "test id one variant", - variants: [{ id: "test_123", title: "Blue", variant_rank: 0 }], + variants: [{ id: "test_123", title: "Blue" }], } return Promise.resolve({ id: IdMap.getId("ironman") }) }, @@ -38,30 +38,6 @@ describe("ProductService", () => { jest.clearAllMocks() }) - it("Orders variants according to rank when retrieving a product", async () => { - const result = await productService.retrieve("test id with variants", { - relations: ["Variants"], - }) - - expect(productRepo.findOneWithRelations).toHaveBeenCalledTimes(1) - expect(productRepo.findOneWithRelations).toHaveBeenCalledWith( - ["Variants"], - { - where: { id: "test id with variants" }, - } - ) - - const expected = { - id: "test id with variants", - variants: [ - { id: "test_123", title: "Blue", variant_rank: 0 }, - { id: "test_321", title: "Green", variant_rank: 1 }, - ], - } - - expect(result).toEqual(expected) - }) - it("successfully retrieves a product", async () => { const result = await productService.retrieve(IdMap.getId("ironman")) @@ -162,12 +138,10 @@ describe("ProductService", () => { { id: "test1", title: "green", - variant_rank: 0, }, { id: "test2", title: "blue", - variant_rank: 1, }, ], }) @@ -204,12 +178,10 @@ describe("ProductService", () => { { id: "test1", title: "green", - variant_rank: 0, }, { id: "test2", title: "blue", - variant_rank: 1, }, ], }) From 1331a7d4ce57a5f09f5286915639446f8373598b Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Thu, 9 Sep 2021 09:23:38 +0200 Subject: [PATCH 20/21] rename migraiton --- ...ltValue.ts => 1631104895519-rank_column_with_default_value.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/medusa/src/migrations/{1631104895519-RankColumnWithDefaultValue.ts => 1631104895519-rank_column_with_default_value.ts} (100%) diff --git a/packages/medusa/src/migrations/1631104895519-RankColumnWithDefaultValue.ts b/packages/medusa/src/migrations/1631104895519-rank_column_with_default_value.ts similarity index 100% rename from packages/medusa/src/migrations/1631104895519-RankColumnWithDefaultValue.ts rename to packages/medusa/src/migrations/1631104895519-rank_column_with_default_value.ts From 84e208195523ee7b8b167dced68dfe6abfbd7a1a Mon Sep 17 00:00:00 2001 From: pKorsholm Date: Thu, 9 Sep 2021 09:26:49 +0200 Subject: [PATCH 21/21] added missing brackets to control structures --- packages/medusa/src/services/__mocks__/product.js | 4 ++-- packages/medusa/src/services/__tests__/product.js | 6 ++++-- packages/medusa/src/services/product-variant.js | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/medusa/src/services/__mocks__/product.js b/packages/medusa/src/services/__mocks__/product.js index 9880d920f2..c86cec482e 100644 --- a/packages/medusa/src/services/__mocks__/product.js +++ b/packages/medusa/src/services/__mocks__/product.js @@ -36,9 +36,9 @@ export const ProductServiceMock = { if (data.title === "Test Product") { return Promise.resolve(products.product1) } - if (data.title === "Test Product with variants") + if (data.title === "Test Product with variants") { return Promise.resolve(products.productWithOptions) - + } return Promise.resolve({ ...data }) }), count: jest.fn().mockReturnValue(4), diff --git a/packages/medusa/src/services/__tests__/product.js b/packages/medusa/src/services/__tests__/product.js index 4f4033d7a3..a8fe0463d4 100644 --- a/packages/medusa/src/services/__tests__/product.js +++ b/packages/medusa/src/services/__tests__/product.js @@ -12,7 +12,7 @@ describe("ProductService", () => { describe("retrieve", () => { const productRepo = MockRepository({ findOneWithRelations: (rels, query) => { - if (query.where.id === "test id with variants") + if (query.where.id === "test id with variants") { return { id: "test id with variants", variants: [ @@ -20,11 +20,13 @@ describe("ProductService", () => { { id: "test_123", title: "Blue" }, ], } - if (query.where.id === "test id one variant") + } + if (query.where.id === "test id one variant") { return { id: "test id one variant", variants: [{ id: "test_123", title: "Blue" }], } + } return Promise.resolve({ id: IdMap.getId("ironman") }) }, }) diff --git a/packages/medusa/src/services/product-variant.js b/packages/medusa/src/services/product-variant.js index 922d607a37..388d8fad24 100644 --- a/packages/medusa/src/services/product-variant.js +++ b/packages/medusa/src/services/product-variant.js @@ -174,7 +174,9 @@ class ProductVariantService extends BaseService { ) } - if (!rest.variant_rank) rest.variant_rank = product.variants.length + if (!rest.variant_rank) { + rest.variant_rank = product.variants.length + } const toCreate = { ...rest,