Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(product, types, workflows): Update product variant workflow #5668

Merged
merged 32 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c200a01
add product option value service + repo
pKorsholm Nov 21, 2023
b9a1956
upsert options where possible
pKorsholm Nov 21, 2023
72cf434
remove method not related to pr
pKorsholm Nov 21, 2023
9cff152
update integration tests
pKorsholm Nov 21, 2023
fbde7b6
rename variable
pKorsholm Nov 21, 2023
aec91a0
add injectTransactionManager
pKorsholm Nov 21, 2023
3c4ab61
flavor update
pKorsholm Nov 21, 2023
88a61de
rm tsdocs for internal interface
pKorsholm Nov 21, 2023
7f6316f
cleanup
pKorsholm Nov 21, 2023
5ad5f46
refactor product-module service method
pKorsholm Nov 21, 2023
abef992
remove promiseAll
pKorsholm Nov 21, 2023
ba70cd2
add changeset
pKorsholm Nov 21, 2023
cc2336f
add integration test for multiple updates
pKorsholm Nov 21, 2023
49493e8
rename test
pKorsholm Nov 21, 2023
b08466e
Merge branch 'develop' into fix/update-product-variant-workflow
pKorsholm Nov 21, 2023
481ff3c
cleanup cast
pKorsholm Nov 21, 2023
4ab10f1
Update packages/product/src/repositories/product-option-value.ts
pKorsholm Nov 21, 2023
ac9fe09
update option.id
pKorsholm Nov 21, 2023
26acb31
rename result variable
pKorsholm Nov 21, 2023
03629bd
Update packages/product/src/services/product-module-service.ts
pKorsholm Nov 21, 2023
edc309e
Update integration-tests/plugins/__tests__/product/admin/update-produ…
pKorsholm Nov 21, 2023
826dc42
redo test check
pKorsholm Nov 21, 2023
9e95e84
use serialize
pKorsholm Nov 21, 2023
657b27d
fix pr feedback
pKorsholm Nov 21, 2023
776b805
use util for arrayDifference
pKorsholm Nov 21, 2023
28d4d7e
Merge branch 'develop' into fix/update-product-variant-workflow
pKorsholm Nov 21, 2023
cc67b58
add metadata test
pKorsholm Nov 21, 2023
2a5f6ce
fix pipeline?
pKorsholm Nov 22, 2023
6a0570c
chore: update changeset
riqwan Nov 22, 2023
f667f84
Merge branch 'develop' into fix/update-product-variant-workflow
pKorsholm Nov 22, 2023
34eb346
pr feedback
pKorsholm Nov 23, 2023
d81acff
Merge branch 'develop' into fix/update-product-variant-workflow
riqwan Nov 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", ()
{
options: [{ option_id: "test-product-option-1", value: "test" }],
},
{
options: [{ option_id: "test-product-option-1", value: "test 2" }],
},
],
options: [
{
Expand Down Expand Up @@ -250,4 +253,121 @@ describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", ()
})
)
})

it("Should update variant option value", async () => {
pKorsholm marked this conversation as resolved.
Show resolved Hide resolved
const api = useApi()! as AxiosInstance

const data = {
options: [
{
option_id: "test-product-option-1",
value: "updated",
},
],
}

await api.post(
`/admin/products/${product.id}/variants/${variant.id}`,
data,
adminHeaders
)

const response = await api.get(
`/admin/products/${product.id}`,
adminHeaders
)

expect(response.status).toEqual(200)
pKorsholm marked this conversation as resolved.
Show resolved Hide resolved
expect(response.data.product).toEqual(
expect.objectContaining({
id: expect.any(String),
variants: expect.arrayContaining([
expect.objectContaining({
id: variant.id,
options: [
expect.objectContaining({
option_id: "test-product-option-1",
value: "updated",
}),
],
}),
expect.objectContaining({
id: product.variants[1].id,
options: [
expect.objectContaining({
option_id: "test-product-option-1",
value: "test 2",
}),
],
}),
]),
})
)
})

it("Should remove options not present in update", async () => {
const api = useApi()! as AxiosInstance

product = await simpleProductFactory(dbConnection, {
id: "test-product-with-multiple-options",
variants: [
{
options: [
{ option_id: "test-product-multi-option-1", value: "test" },
{ option_id: "test-product-multi-option-2", value: "test value" },
],
},
],
options: [
{
id: "test-product-multi-option-1",
title: "Test option 1",
},
{
id: "test-product-multi-option-2",
title: "Test option 2",
},
],
})

variant = product.variants[0]

const data = {
options: [
{
option_id: "test-product-multi-option-1",
value: "updated",
},
],
}

await api.post(
`/admin/products/${product.id}/variants/${variant.id}`,
data,
adminHeaders
)

const response = await api.get(
`/admin/products/${product.id}`,
adminHeaders
)

expect(response.status).toEqual(200)
expect(response.data.product).toEqual(
expect.objectContaining({
id: expect.any(String),
variants: [
expect.objectContaining({
id: variant.id,
options: [
expect.objectContaining({
option_id: "test-product-multi-option-1",
value: "updated",
}),
],
}),
],
})
)
})
})
6 changes: 6 additions & 0 deletions packages/product/src/loaders/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ProductCollectionRepository,
ProductImageRepository,
ProductOptionRepository,
ProductOptionValueRepository,
ProductRepository,
ProductTagRepository,
ProductTypeRepository,
Expand All @@ -17,6 +18,7 @@ import {
ProductImageService,
ProductModuleService,
ProductOptionService,
ProductOptionValueService,
ProductService,
ProductTagService,
ProductTypeService,
Expand Down Expand Up @@ -48,6 +50,7 @@ export default async ({
productImageService: asClass(ProductImageService).singleton(),
productTypeService: asClass(ProductTypeService).singleton(),
productOptionService: asClass(ProductOptionService).singleton(),
productOptionValueService: asClass(ProductOptionValueService).singleton(),
})

if (customRepositories) {
Expand All @@ -69,6 +72,9 @@ function loadDefaultRepositories({ container }) {
productTagRepository: asClass(ProductTagRepository).singleton(),
productTypeRepository: asClass(ProductTypeRepository).singleton(),
productOptionRepository: asClass(ProductOptionRepository).singleton(),
productOptionValueRepository: asClass(
ProductOptionValueRepository
).singleton(),
productVariantRepository: asClass(ProductVariantRepository).singleton(),
})
}
Expand Down
1 change: 1 addition & 0 deletions packages/product/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export { default as ProductTag } from "./product-tag"
export { default as ProductType } from "./product-type"
export { default as ProductVariant } from "./product-variant"
export { default as ProductOption } from "./product-option"
export { default as ProductOptionValue } from "./product-option-value"
export { default as Image } from "./product-image"
1 change: 1 addition & 0 deletions packages/product/src/repositories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { ProductCategoryRepository } from "./product-category"
export { ProductImageRepository } from "./product-image"
export { ProductTypeRepository } from "./product-type"
export { ProductOptionRepository } from "./product-option"
export { ProductOptionValueRepository } from "./product-option-value"
109 changes: 109 additions & 0 deletions packages/product/src/repositories/product-option-value.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { ProductOptionValue } from "@models"
import { Context, DAL } from "@medusajs/types"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { DALUtils } from "@medusajs/utils"
import {
CreateProductOptionValueDTO,
UpdateProductOptionValueDTO,
} from "../types/services/product-option-value"
import { LoadStrategy } from "@mikro-orm/core"
import { FilterQuery as MikroFilterQuery } from "@mikro-orm/core/typings"
import { FindOptions as MikroOptions } from "@mikro-orm/core/drivers/IDatabaseDriver"

export class ProductOptionValueRepository extends DALUtils.MikroOrmBaseRepository {
protected readonly manager_: SqlEntityManager

constructor({ manager }: { manager: SqlEntityManager }) {
// @ts-ignore
// eslint-disable-next-line prefer-rest-params
super(...arguments)
this.manager_ = manager
}

async find(
findOptions: DAL.FindOptions<ProductOptionValue> = { where: {} },
context: Context = {}
): Promise<ProductOptionValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }

findOptions_.options ??= {}

pKorsholm marked this conversation as resolved.
Show resolved Hide resolved
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
pKorsholm marked this conversation as resolved.
Show resolved Hide resolved

return await manager.find(
ProductOptionValue,
findOptions_.where as MikroFilterQuery<ProductOptionValue>,
findOptions_.options as MikroOptions<ProductOptionValue>
)
}

async upsert(
optionValues: (UpdateProductOptionValueDTO | CreateProductOptionValueDTO)[],
context: Context = {}
): Promise<ProductOptionValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)

const optionValueIds = optionValues
.filter((option) => !!option.id)
pKorsholm marked this conversation as resolved.
Show resolved Hide resolved
.map(({ id }) => id!)
pKorsholm marked this conversation as resolved.
Show resolved Hide resolved

const existingOptionValues = await this.find(
{
where: {
id: {
$in: optionValueIds,
},
},
},
context
)

const existingOptionValuesMap = new Map(
existingOptionValues.map<[string, ProductOptionValue]>((optionValue) => [
optionValue.id,
optionValue,
])
)

const upsertedOptionValues: ProductOptionValue[] = []
const optionValuesToCreate: ProductOptionValue[] = []
const optionValuesToUpdate: ProductOptionValue[] = []

optionValues.forEach((optionValue) => {
if (optionValue.id && existingOptionValuesMap.has(optionValue.id)) {
const existingOptionValue = existingOptionValuesMap.get(optionValue.id)!
const updatedOptionValue = manager.assign(
existingOptionValue,
optionValue as UpdateProductOptionValueDTO
)
optionValuesToUpdate.push(updatedOptionValue)
} else {
const newOptionValue = manager.create(
ProductOptionValue,
optionValue as CreateProductOptionValueDTO
)
optionValuesToCreate.push(newOptionValue)
}
})

if (optionValuesToCreate.length) {
manager.persist(optionValuesToCreate)
upsertedOptionValues.push(...optionValuesToCreate)
}

if (optionValuesToUpdate.length) {
manager.persist(optionValuesToUpdate)
upsertedOptionValues.push(...optionValuesToUpdate)
}

return upsertedOptionValues
}

async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(ProductOptionValue, { id: { $in: ids } }, {})
}
}
1 change: 1 addition & 0 deletions packages/product/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { default as ProductVariantService } from "./product-variant"
export { default as ProductTypeService } from "./product-type"
export { default as ProductOptionService } from "./product-option"
export { default as ProductImageService } from "./product-image"
export { default as ProductOptionValueService } from "./product-option-value"
Loading
Loading