+
Skip to content

feat/promotions add duplicate promotion endpoint #6652

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -84,7 +84,10 @@ export const defaultShopManagerRoles = [
"reaction:legacy:taxRates/create",
"reaction:legacy:taxRates/delete",
"reaction:legacy:taxRates/read",
"reaction:legacy:taxRates/update"
"reaction:legacy:taxRates/update",
"reaction:legacy:promotions/create",
"reaction:legacy:promotions/read",
"reaction:legacy:promotions/update"
];

export const defaultShopOwnerRoles = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import _ from "lodash";
import SimpleSchema from "simpl-schema";
import { Promotion as PromotionSchema, Promotion, Trigger } from "../simpleSchemas.js";
import createPromotion from "./createPromotion.js";
import { CreateOrderPromotion } from "./fixtures/orderPromotion.js";

const triggerKeys = ["offers"];
const promotionTypes = ["coupon"];
Expand All @@ -27,46 +28,7 @@ const insertResults = {
insertedId: "myId"
};
mockContext.collections.Promotions.insertOne = () => insertResults;
mockContext.mutations.incrementSequence = () => 1;

const now = new Date();

const OrderPromotion = {
_id: "orderPromotion",
shopId: "testShop",
promotionType: "coupon",
label: "5 percent off your entire order when you spend more then $200",
description: "5 percent off your entire order when you spend more then $200",
enabled: true,
triggers: [
{
triggerKey: "offers",
triggerParameters: {
name: "5 percent off your entire order when you spend more then $200",
conditions: {
any: [
{
fact: "cart",
path: "$.merchandiseTotal",
operator: "greaterThanInclusive",
value: 200
}
]
}
}
}
],
actions: [
{
actionKey: "noop",
actionParameters: {}
}
],
startDate: now,
endDate: new Date(now.getTime() + 1000 * 60 * 60 * 24 * 7),
stackAbility: "none"
};

mockContext.mutations.incrementSequence = () => 1000000;
mockContext.simpleSchemas = {
Promotion
};
Expand Down Expand Up @@ -103,7 +65,7 @@ test("will not insert a record if it fails simple-schema validation", async () =
});

test("will not insert a record with no triggers", async () => {
const promotion = _.cloneDeep(OrderPromotion);
const promotion = _.cloneDeep(CreateOrderPromotion);
promotion.triggers = [
{
triggerKey: "offers",
Expand All @@ -120,7 +82,7 @@ test("will not insert a record with no triggers", async () => {
});

test("will not insert a record if trigger parameters are incorrect", async () => {
const promotion = _.cloneDeep(OrderPromotion);
const promotion = _.cloneDeep(CreateOrderPromotion);
promotion.triggers = [];
try {
await createPromotion(mockContext, promotion);
Expand All @@ -131,7 +93,7 @@ test("will not insert a record if trigger parameters are incorrect", async () =>


test("will insert a record if it passes validation", async () => {
const promotionToInsert = OrderPromotion;
const promotionToInsert = CreateOrderPromotion;
try {
const { success } = await createPromotion(mockContext, promotionToInsert);
expect(success).toBeTruthy();
Expand Down
37 changes: 37 additions & 0 deletions packages/api-plugin-promotions/src/mutations/duplicatePromotion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import _ from "lodash";
import Random from "@reactioncommerce/random";
import validateTriggerParams from "./validateTriggerParams.js";

/**
* @summary duplicate an existing promotion to a new one
* @param {Object} context - the per-request application context
* @param {String} promotionId - The ID of the promotion you want to duplicate
* @return {Promise<{success: boolean, promotion: *}|{success: boolean, errors: [{message: string}]}>} - return the newly created promotion or an array of errors
*/
export default async function duplicatePromotion(context, promotionId) {
const { collections: { Promotions }, simpleSchemas: { Promotion: PromotionSchema } } = context;
const now = new Date();
const existingPromotion = await Promotions.findOne({ _id: promotionId });
const newPromotion = _.cloneDeep(existingPromotion);
newPromotion._id = Random.id();
newPromotion.createdAt = now;
newPromotion.updatedAt = now;
newPromotion.name = `Copy of ${existingPromotion.name}`;
newPromotion.referenceId = await context.mutations.incrementSequence(context, newPromotion.shopId, "Promotions");
PromotionSchema.validate(newPromotion);
validateTriggerParams(context, newPromotion);
const results = await Promotions.insertOne(newPromotion);
const { insertedCount } = results;
if (!insertedCount) {
return {
success: false,
errors: [{
message: "The record could not be inserted but no error was thrown"
}]
};
}
return {
success: true,
promotion: newPromotion
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import mockCollection from "@reactioncommerce/api-utils/tests/mockCollection.js";
import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js";
import SimpleSchema from "simpl-schema";
import { Promotion as PromotionSchema, Promotion, Trigger } from "../simpleSchemas.js";
import duplicatePromotion from "./duplicatePromotion.js";
import { ExistingOrderPromotion } from "./fixtures/orderPromotion.js";

const triggerKeys = ["offers"];
const promotionTypes = ["coupon"];

Trigger.extend({
triggerKey: {
allowedValues: [...Trigger.getAllowedValuesForKey("triggerKey"), ...triggerKeys]
}
});

PromotionSchema.extend({
promotionType: {
allowedValues: [...PromotionSchema.getAllowedValuesForKey("promotionType"), ...promotionTypes]
}
});

mockContext.collections.Promotions = mockCollection("Promotions");
const insertResults = {
insertedCount: 1,
insertedId: "myId"
};
mockContext.collections.Promotions.insertOne = () => insertResults;
mockContext.collections.Promotions.findOne = () => ExistingOrderPromotion;
mockContext.mutations.incrementSequence = () => 1000000;

mockContext.simpleSchemas = {
Promotion
};

export const OfferTriggerParameters = new SimpleSchema({
name: String,
conditions: {
type: Object,
blackbox: true
}
});

const offerTrigger = {
key: "offers",
handler: () => {},
paramSchema: OfferTriggerParameters,
type: "implicit"
};


mockContext.promotions = {
triggers: [
offerTrigger
]
};


test("duplicates existing promotions and creates new one", async () => {
try {
const { success, promotion } = await duplicatePromotion(mockContext, ExistingOrderPromotion._id);
expect(success).toBeTruthy();
expect(promotion.name).toEqual("Copy of Order Promotion");
expect(promotion.referenceId).toEqual(1000000);
expect(promotion._id).not.toEqual("orderPromotion");
} catch (error) {
expect(error).toBeUndefined();
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const now = new Date();

export const CreateOrderPromotion = {
shopId: "testShop",
promotionType: "coupon",
name: "Order Promotion",
label: "5 percent off your entire order when you spend more then $200",
description: "5 percent off your entire order when you spend more then $200",
enabled: true,
triggers: [
{
triggerKey: "offers",
triggerParameters: {
name: "5 percent off your entire order when you spend more then $200",
conditions: {
any: [
{
fact: "cart",
path: "$.merchandiseTotal",
operator: "greaterThanInclusive",
value: 200
}
]
}
}
}
],
actions: [
{
actionKey: "noop",
actionParameters: {}
}
],
startDate: now,
endDate: new Date(now.getTime() + 1000 * 60 * 60 * 24 * 7),
stackAbility: "none"
};

export const ExistingOrderPromotion = {
_id: "orderPromotion",
referenceId: 1,
shopId: "testShop",
promotionType: "item-discount",
triggerType: "implicit",
name: "Order Promotion",
label: "5 percent off your entire order when you spend more then $200",
description: "5 percent off your entire order when you spend more then $200",
enabled: true,
triggers: [
{
triggerKey: "offers",
triggerParameters: {
name: "5 percent off your entire order when you spend more then $200",
conditions: {
any: [
{
fact: "cart",
path: "$.merchandiseTotal",
operator: "greaterThanInclusive",
value: 200
}
]
}
}
}
],
actions: [
{
actionKey: "noop",
actionParameters: {}
}
],
startDate: now,
endDate: new Date(now.getTime() + 1000 * 60 * 60 * 24 * 7),
stackAbility: "none"
};

4 changes: 3 additions & 1 deletion packages/api-plugin-promotions/src/mutations/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import applyExplicitPromotionToCart from "./applyExplicitPromotionToCart.js";
import createPromotion from "./createPromotion.js";
import updatePromotion from "./updatePromotion.js";
import duplicatePromotion from "./duplicatePromotion.js";

export default {
applyExplicitPromotionToCart,
createPromotion,
updatePromotion
updatePromotion,
duplicatePromotion
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ PromotionSchema.extend({
}
});


mockContext.collections.Promotions = mockCollection("Promotions");
const insertResults = {
insertedCount: 1,
Expand All @@ -37,6 +36,7 @@ const OrderPromotion = {
referenceId: 123,
shopId: "testShop",
promotionType: "coupon",
name: "Order Promotion",
triggerType: "explicit",
label: "5 percent off your entire order when you spend more then $200",
description: "5 percent off your entire order when you spend more then $200",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @summary duplicate an existing promotion
* @param {undefined} _ - unused
* @param {Object} args - The arguments passed to the mutation
* @param {Object} context - The application context
* @return {Promise<boolean>} - true if success
*/
export default async function duplicatePromotion(_, { input }, context) {
const { promotionId, shopId } = input;
await context.validatePermissions("reaction:legacy:promotions", "create", { shopId });
const duplicatePromotionResults = await context.mutations.duplicatePromotion(context, promotionId);
return duplicatePromotionResults;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import updatePromotion from "./updatePromotion.js";
import createPromotion from "./createPromotion.js";
import duplicatePromotion from "./duplicatePromotion.js";

export default {
updatePromotion,
createPromotion
createPromotion,
duplicatePromotion
};
19 changes: 17 additions & 2 deletions packages/api-plugin-promotions/src/schemas/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,18 @@ input PromotionFilter {

input PromotionCreateInput {

"The id of the shop that this promotion resides"
"The id of the shop that this promotion resides in"
shopId: String!

"What type of promotion this is for stackability purposes"
promotionType: String!

"The short description of the promotion"
"The short description of the promotion visible to the customer"
label: String!

"The short description of the promotion"
name: String!

"A longer detailed description of the promotion"
description: String!

Expand All @@ -160,6 +163,11 @@ input PromotionCreateInput {
stackAbility: Stackability
}

input PromotionDuplicateInput {
"The id of the promotion to duplicate"
promotionId: String!
}

"This is identical to the PromotionCreate except it includes the _id"
input PromotionUpdateInput {
"The unique ID of the promotion"
Expand All @@ -175,6 +183,9 @@ input PromotionUpdateInput {
promotionType: String!

"The short description of the promotion"
name: String!

"The short description of the promotion visible to the customer"
label: String!

"A longer detailed description of the promotion"
Expand Down Expand Up @@ -221,6 +232,10 @@ extend type Mutation {
input: PromotionCreateInput
): PromotionUpdateCreatePayload

duplicatePromotion(
input: PromotionDuplicateInput
): PromotionUpdateCreatePayload

updatePromotion(
input: PromotionUpdateInput
): PromotionUpdateCreatePayload
Expand Down
3 changes: 3 additions & 0 deletions packages/api-plugin-promotions/src/simpleSchemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export const Promotion = new SimpleSchema({
"label": {
type: String
},
"name": {
type: String
},
"description": {
type: String
},
Expand Down
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载