feat(fulfillment): Shipping options, rules CRUD + rules based context filtering (#6455)

**What**
- Update shipping options with its rules and type
- create/update rules independently
- context based validation fundation
- 🔴 list shipping options with context rules fitlering will come in a separate pr to keep this one smaller

FIXES CORE-1743
FIXES CORE-1764
This commit is contained in:
Adrien de Peretti
2024-02-23 11:14:33 +00:00
committed by GitHub
parent 78b6d46584
commit 788c4a1e36
19 changed files with 1767 additions and 55 deletions
@@ -812,7 +812,7 @@ moduleIntegrationTestRunner({
{
attribute: "test-attribute",
operator: "in",
value: "test-value",
value: ["test-value"],
},
],
}
@@ -887,7 +887,7 @@ moduleIntegrationTestRunner({
rules: [
{
attribute: "test-attribute",
operator: "in",
operator: "eq",
value: "test-value",
},
],
@@ -909,7 +909,7 @@ moduleIntegrationTestRunner({
rules: [
{
attribute: "test-attribute",
operator: "in",
operator: "eq",
value: "test-value",
},
],
@@ -953,6 +953,145 @@ moduleIntegrationTestRunner({
++i
}
})
it("should fail to create a new shipping option with invalid rules", async function () {
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
// TODO: change that for a real provider instead of fake data manual inserted data
const [{ id: providerId }] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const createData: CreateShippingOptionDTO = {
name: "test-option",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: providerId,
type: {
code: "test-type",
description: "test-description",
label: "test-label",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test-attribute",
operator: "invalid",
value: "test-value",
},
],
}
const err = await service
.createShippingOptions(createData)
.catch((e) => e)
expect(err).toBeDefined()
expect(err.message).toBe(
"Rule operator invalid is not supported. Must be one of in, eq, ne, gt, gte, lt, lte, nin"
)
})
})
describe("on create shipping option rules", () => {
it("should create a new rule", async () => {
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
// service provider
const [{ id: providerId }] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOption = await service.createShippingOptions({
name: "test-option",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: providerId,
type: {
code: "test-type",
description: "test-description",
label: "test-label",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test-attribute",
operator: "eq",
value: "test-value",
},
],
})
const ruleData = {
attribute: "test-attribute",
operator: "eq",
value: "test-value",
shipping_option_id: shippingOption.id,
}
const rule = await service.createShippingOptionRules(ruleData)
expect(rule).toEqual(
expect.objectContaining({
id: expect.any(String),
attribute: ruleData.attribute,
operator: ruleData.operator,
value: ruleData.value,
shipping_option_id: ruleData.shipping_option_id,
})
)
const rules = await service.listShippingOptionRules()
expect(rules).toHaveLength(2)
expect(rules).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: rule.id,
attribute: ruleData.attribute,
operator: ruleData.operator,
value: ruleData.value,
shipping_option_id: shippingOption.id,
}),
expect.objectContaining({
id: shippingOption.rules[0].id,
attribute: shippingOption.rules[0].attribute,
operator: shippingOption.rules[0].operator,
value: shippingOption.rules[0].value,
shipping_option_id: shippingOption.id,
}),
])
)
})
})
describe("on update", () => {
@@ -1680,6 +1819,698 @@ moduleIntegrationTestRunner({
}
})
})
describe("on update shipping options", () => {
it("should update a shipping option", async () => {
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const [serviceProvider] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOptionData = {
name: "test",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
}
const shippingOption = await service.createShippingOptions(
shippingOptionData
)
const updateData = {
id: shippingOption.id,
name: "updated-test",
price_type: "calculated",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "updated-test",
description: "updated-test",
label: "updated-test",
},
data: {
amount: 2000,
},
rules: [
{
attribute: "new-test",
operator: "eq",
value: "new-test",
},
],
}
const updatedShippingOption = await service.updateShippingOptions(
updateData
)
expect(updatedShippingOption).toEqual(
expect.objectContaining({
id: updateData.id,
name: updateData.name,
price_type: updateData.price_type,
service_zone_id: updateData.service_zone_id,
shipping_profile_id: updateData.shipping_profile_id,
service_provider_id: updateData.service_provider_id,
shipping_option_type_id: expect.any(String),
type: expect.objectContaining({
id: expect.any(String),
code: updateData.type.code,
description: updateData.type.description,
label: updateData.type.label,
}),
data: updateData.data,
rules: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
attribute: updateData.rules[0].attribute,
operator: updateData.rules[0].operator,
value: updateData.rules[0].value,
}),
]),
})
)
const rules = await service.listShippingOptionRules()
expect(rules).toHaveLength(1)
expect(rules[0]).toEqual(
expect.objectContaining({
id: updatedShippingOption.rules[0].id,
})
)
const types = await service.listShippingOptionTypes()
expect(types).toHaveLength(1)
expect(types[0]).toEqual(
expect.objectContaining({
code: updateData.type.code,
description: updateData.type.description,
label: updateData.type.label,
})
)
})
it("should update a shipping option without updating the rules or the type", async () => {
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const [serviceProvider] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOptionData = {
name: "test",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
}
const shippingOption = await service.createShippingOptions(
shippingOptionData
)
const updateData = {
id: shippingOption.id,
name: "updated-test",
price_type: "calculated",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
data: {
amount: 2000,
},
}
await service.updateShippingOptions(updateData)
const updatedShippingOption = await service.retrieveShippingOption(
shippingOption.id,
{
relations: ["rules", "type"],
}
)
expect(updatedShippingOption).toEqual(
expect.objectContaining({
id: updateData.id,
name: updateData.name,
price_type: updateData.price_type,
service_zone_id: updateData.service_zone_id,
shipping_profile_id: updateData.shipping_profile_id,
service_provider_id: updateData.service_provider_id,
shipping_option_type_id: expect.any(String),
type: expect.objectContaining({
id: expect.any(String),
code: shippingOptionData.type.code,
description: shippingOptionData.type.description,
label: shippingOptionData.type.label,
}),
data: updateData.data,
rules: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
attribute: shippingOptionData.rules[0].attribute,
operator: shippingOptionData.rules[0].operator,
value: shippingOptionData.rules[0].value,
}),
]),
})
)
const rules = await service.listShippingOptionRules()
expect(rules).toHaveLength(1)
expect(rules[0]).toEqual(
expect.objectContaining({
id: updatedShippingOption.rules[0].id,
})
)
const types = await service.listShippingOptionTypes()
expect(types).toHaveLength(1)
expect(types[0]).toEqual(
expect.objectContaining({
code: shippingOptionData.type.code,
description: shippingOptionData.type.description,
label: shippingOptionData.type.label,
})
)
})
it("should update a collection of shipping options", async () => {
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const [serviceProvider] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOptionData = [
{
name: "test",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
},
{
name: "test2",
price_type: "calculated",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
},
]
const shippingOptions = await service.createShippingOptions(
shippingOptionData
)
const updateData = [
{
id: shippingOptions[0].id,
name: "updated-test",
price_type: "calculated",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "updated-test",
description: "updated-test",
label: "updated-test",
},
data: {
amount: 2000,
},
rules: [
{
attribute: "new-test",
operator: "eq",
value: "new-test",
},
],
},
{
id: shippingOptions[1].id,
name: "updated-test",
price_type: "calculated",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "updated-test",
description: "updated-test",
label: "updated-test",
},
data: {
amount: 2000,
},
rules: [
{
attribute: "new-test",
operator: "eq",
value: "new-test",
},
],
},
]
const updatedShippingOption = await service.updateShippingOptions(
updateData
)
for (const data_ of updateData) {
const expectedShippingOption = updatedShippingOption.find(
(shippingOption) => shippingOption.id === data_.id
)
expect(expectedShippingOption).toEqual(
expect.objectContaining({
id: data_.id,
name: data_.name,
price_type: data_.price_type,
service_zone_id: data_.service_zone_id,
shipping_profile_id: data_.shipping_profile_id,
service_provider_id: data_.service_provider_id,
shipping_option_type_id: expect.any(String),
type: expect.objectContaining({
id: expect.any(String),
code: data_.type.code,
description: data_.type.description,
label: data_.type.label,
}),
data: data_.data,
rules: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
attribute: data_.rules[0].attribute,
operator: data_.rules[0].operator,
value: data_.rules[0].value,
}),
]),
})
)
}
const rules = await service.listShippingOptionRules()
expect(rules).toHaveLength(2)
expect(rules).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: updatedShippingOption[0].rules[0].id,
}),
expect.objectContaining({
id: updatedShippingOption[1].rules[0].id,
}),
])
)
const types = await service.listShippingOptionTypes()
expect(types).toHaveLength(2)
expect(types).toEqual(
expect.arrayContaining([
expect.objectContaining({
code: updateData[0].type.code,
description: updateData[0].type.description,
label: updateData[0].type.label,
}),
expect.objectContaining({
code: updateData[1].type.code,
description: updateData[1].type.description,
label: updateData[1].type.label,
}),
])
)
})
it("should fail to update a non-existent shipping option", async () => {
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const [serviceProvider] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOptionData = {
id: "sp_jdafwfleiwuonl",
name: "test",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
}
const err = await service
.updateShippingOptions(shippingOptionData)
.catch((e) => e)
expect(err).toBeDefined()
expect(err.message).toBe(
`The following shipping options do not exist: ${shippingOptionData.id}`
)
})
it("should fail to update a shipping option when adding non existing rules", async () => {
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const [serviceProvider] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOptionData = {
name: "test",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
}
const shippingOption = await service.createShippingOptions(
shippingOptionData
)
const updateData = [
{
id: shippingOption.id,
rules: [
{
id: "sp_jdafwfleiwuonl",
},
],
},
]
const err = await service
.updateShippingOptions(updateData)
.catch((e) => e)
expect(err).toBeDefined()
expect(err.message).toBe(
`The following rules does not exists: ${updateData[0].rules[0].id} on shipping option ${shippingOption.id}`
)
})
it("should fail to update a shipping option when adding invalid rules", async () => {
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const [serviceProvider] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOptionData = {
name: "test",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
}
const shippingOption = await service.createShippingOptions(
shippingOptionData
)
const updateData = [
{
id: shippingOption.id,
rules: [
{
attribute: "test",
operator: "invalid",
value: "test",
},
],
},
]
const err = await service
.updateShippingOptions(updateData)
.catch((e) => e)
expect(err).toBeDefined()
expect(err.message).toBe(
`Rule operator invalid is not supported. Must be one of in, eq, ne, gt, gte, lt, lte, nin`
)
})
})
describe("on update shipping option rules", () => {
it("should update a shipping option rule", async () => {
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const [serviceProvider] =
await MikroOrmWrapper.forkManager().execute(
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
)
const shippingOption = await service.createShippingOptions({
name: "test",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
service_provider_id: serviceProvider.id,
type: {
code: "test",
description: "test",
label: "test",
},
data: {
amount: 1000,
},
rules: [
{
attribute: "test",
operator: "eq",
value: "test",
},
],
})
const updateData = {
id: shippingOption.rules[0].id,
attribute: "updated-test",
operator: "eq",
value: "updated-test",
}
const updatedRule = await service.updateShippingOptionRules(
updateData
)
expect(updatedRule).toEqual(
expect.objectContaining({
id: updateData.id,
attribute: updateData.attribute,
operator: updateData.operator,
value: updateData.value,
})
)
})
it("should fail to update a non-existent shipping option rule", async () => {
const updateData = {
id: "sp_jdafwfleiwuonl",
attribute: "updated-test",
operator: "eq",
value: "updated-test",
}
const err = await service
.updateShippingOptionRules(updateData)
.catch((e) => e)
expect(err).toBeDefined()
expect(err.message).toBe(
`ShippingOptionRule with id "${updateData.id}" not found`
)
})
})
})
})
},