Skip to content

Commit

Permalink
Refactor schedule update logic and add schedule metafield updates
Browse files Browse the repository at this point in the history
 - Implement `updateScheduleLocationsField` to handle schedule location updates
 - Modify `updateUserMetaobject` to deduplicate locations and include schedule IDs
 - Introduce `updateScheduleMetafield` activity in schedule update orchestration
 - Add `UpdateScheduleLocationsFieldMutation` type for GraphQL operations
 - Remove redundant location aggregation logic from `updateScheduleMetafield`
  • Loading branch information
jamalsoueidan committed May 30, 2024
1 parent 6305551 commit fbacd15
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,24 @@ describe("CustomerUpdateOrchestration", () => {
metafieldId: "1",
});

await createSchedule({
const schedule = await createSchedule({
metafieldId: "2",
name: faker.person.lastName(),
customerId,
products: [
getProductObject({
locations: [
getDumbLocationObject({
location: locationOrigin._id,
metafieldId: locationOrigin.metafieldId,
}),
],
}),
],
});

const schedule2 = await createSchedule({
metafieldId: "3",
name: faker.person.lastName(),
customerId,
products: [
Expand Down Expand Up @@ -99,6 +116,13 @@ describe("CustomerUpdateOrchestration", () => {
key: "locations",
value: JSON.stringify(["1"]),
},
{
key: "schedules",
value: JSON.stringify([
schedule.metafieldId,
schedule2.metafieldId,
]),
},
{
key: "active",
value: String(user.active),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ export const updateUserMetaobject = async ({
}) => {
const user = await CustomerServiceGet({ customerId });

const schedule = await UserScheduleServiceLocationsList({
const schedules = await UserScheduleServiceLocationsList({
customerId,
});

const locations = schedule.map((item) => item.locations).flat();
// save unqiue locations across all schedule in user metafield
const locations = schedules
.map((item) => item.locations)
.flat()
.map((p) => p.metafieldId);

const variables = {
id: user.userMetaobjectId || "",
Expand All @@ -41,7 +45,11 @@ export const updateUserMetaobject = async ({
},
{
key: "locations",
value: JSON.stringify(locations.map((p) => p.metafieldId)),
value: JSON.stringify([...new Set(locations)]),
},
{
key: "schedules",
value: JSON.stringify(schedules.map((p) => p.metafieldId)),
},
{
key: "active",
Expand Down
13 changes: 12 additions & 1 deletion src/functions/customer/orchestrations/product/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import {
} from "../customer/update/update-user-metaobject";
import { updatePrice, updatePriceName } from "./update/update-price";
import { updateProduct, updateProductName } from "./update/update-product";
import {
updateScheduleLocationsField,
updateScheduleLocationsFieldName,
} from "./update/update-schedule-locations-field";

df.app.activity(updateProductName, { handler: updateProduct });
df.app.activity(updatePriceName, { handler: updatePrice });
Expand Down Expand Up @@ -35,7 +39,14 @@ const orchestrator: df.OrchestrationHandler = function* (
activityType<typeof updateUserMetaobject>(input)
);

return { productUpdated, priceUpdated, userField };
const scheduleLocationsField: Awaited<
ReturnType<typeof updateScheduleLocationsField>
> = yield context.df.callActivity(
updateScheduleLocationsFieldName,
activityType<typeof updateScheduleLocationsField>(input)
);

return { productUpdated, priceUpdated, userField, scheduleLocationsField };
};

df.app.orchestration("updateProductShopify", orchestrator);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { ensureType } from "~/library/jest/helpers/mock";
import { createSchedule } from "~/library/jest/helpers/schedule";
import { shopifyAdmin } from "~/library/shopify";
import {
UpdateScheduleLocationsFieldMutation,
UpdateScheduleLocationsFieldMutationVariables,
} from "~/types/admin.generated";

import {
createLocation,
getDumbLocationObject,
} from "~/library/jest/helpers/location";
import { getProductObject } from "~/library/jest/helpers/product";
import {
UPDATE_SCHEDULE_LOCATIONS_FIELD,
updateScheduleLocationsField,
} from "./update-schedule-locations-field";

require("~/library/jest/mongoose/mongodb.jest");

jest.mock("~/library/shopify", () => ({
shopifyAdmin: jest.fn().mockReturnValue({
request: jest.fn(),
}),
}));

const mockRequest = shopifyAdmin().request as jest.Mock;

describe("CustomerProductUpdateOrchestration", () => {
beforeAll(async () => {
jest.clearAllMocks();
});

it("updateScheduleLocationsField", async () => {
const location1 = await createLocation({
customerId: 123,
metafieldId: "asd",
});

const location2 = await createLocation({
customerId: 123,
metafieldId: "2",
});

const product1 = getProductObject({
locations: [
getDumbLocationObject({
location: location1._id,
metafieldId: location1.metafieldId,
}),
getDumbLocationObject({
location: location2._id,
metafieldId: location2.metafieldId,
}),
],
});

const product2 = getProductObject({
locations: [
getDumbLocationObject({
location: location1._id,
metafieldId: location1.metafieldId,
}),
],
});

const newSchedule = await createSchedule({
metafieldId: "1",
name: "ANOTHER CUSTOMER",
customerId: 7,
products: [product1, product2],
});

mockRequest.mockResolvedValueOnce({
data: ensureType<UpdateScheduleLocationsFieldMutation>({
metaobjectUpdate: {
metaobject: {
fields: [
{
value: JSON.stringify([
location1.metafieldId,
location2.metafieldId,
]),
key: "locations",
},
],
},
},
}),
});

await updateScheduleLocationsField({
productId: product1.productId,
customerId: newSchedule.customerId,
});

expect(mockRequest).toHaveBeenCalledTimes(1);

expect(mockRequest).toHaveBeenNthCalledWith(
1,
UPDATE_SCHEDULE_LOCATIONS_FIELD,
{
variables: ensureType<UpdateScheduleLocationsFieldMutationVariables>({
id: newSchedule.metafieldId || "",
fields: [
{
value: JSON.stringify([
location1.metafieldId,
location2.metafieldId,
]),
key: "locations",
},
],
}),
}
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { ScheduleModel } from "~/functions/schedule";
import { shopifyAdmin } from "~/library/shopify";

export const updateScheduleLocationsFieldName = "updateScheduleLocationsField";
export const updateScheduleLocationsField = async ({
productId,
customerId,
}: {
productId: number;
customerId: number;
}) => {
const schedule = await ScheduleModel.findOne({
customerId,
products: {
$elemMatch: {
productId,
},
},
});

if (!schedule?.metafieldId) {
throw new Error(
`Failed to update schedule locations field for productId ${productId}`
);
}

// save unique locations for this schedule metafield that are found in the current schedule model.
const locations = schedule.products.reduce((locations, product) => {
product.locations.forEach((location) => {
if (location.metafieldId && !locations.includes(location.metafieldId)) {
locations.push(location.metafieldId);
}
});
return locations;
}, [] as string[]);

const { data } = await shopifyAdmin().request(
UPDATE_SCHEDULE_LOCATIONS_FIELD,
{
variables: {
id: schedule.metafieldId,
fields: [
{
key: "locations",
value: JSON.stringify(locations),
},
],
},
}
);

if (!data?.metaobjectUpdate?.metaobject) {
throw new Error(
`Failed to update schedule locations field for schedule ${schedule._id}`
);
}

return data?.metaobjectUpdate.metaobject;
};

export const UPDATE_SCHEDULE_LOCATIONS_FIELD = `#graphql
mutation UpdateScheduleLocationsField($id: ID!, $fields: [MetaobjectFieldInput!]!) {
metaobjectUpdate(id: $id, metaobject: {fields: $fields}) {
metaobject {
fields {
value
key
}
}
}
}
` as const;
16 changes: 8 additions & 8 deletions src/functions/customer/orchestrations/schedule/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import { OrchestrationContext } from "durable-functions";
import { activityType } from "~/library/orchestration";
import { StringOrObjectIdType } from "~/library/zod";
import {
createScheduleMetafield,
createScheduleMetafieldName,
} from "./create/create-schedule-metafield";
updateScheduleMetafield,
updateScheduleMetafieldName,
} from "./update/update-schedule-metafield";

df.app.activity(createScheduleMetafieldName, {
handler: createScheduleMetafield,
df.app.activity(updateScheduleMetafieldName, {
handler: updateScheduleMetafield,
});

const orchestrator: df.OrchestrationHandler = function* (
context: OrchestrationContext
) {
const input = context.df.getInput() as Input;

const metafield: Awaited<ReturnType<typeof createScheduleMetafield>> =
const metafield: Awaited<ReturnType<typeof updateScheduleMetafield>> =
yield context.df.callActivity(
createScheduleMetafieldName,
activityType<typeof createScheduleMetafield>(input)
updateScheduleMetafieldName,
activityType<typeof updateScheduleMetafield>(input)
);

return { metafield };
Expand Down
Loading

0 comments on commit fbacd15

Please sign in to comment.