Skip to content

Commit

Permalink
fix acceptProgramInviteAction + backfillLinkData
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey committed Nov 15, 2024
1 parent 2a63789 commit b637959
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 117 deletions.
2 changes: 1 addition & 1 deletion apps/web/app/api/partners/[partnerId]/dots-user/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const GET = withPartner(async ({ partner }) => {
return NextResponse.json({});
}

const dotsUser = await retrieveDotsUser({ dotsUserId, partner });
const dotsUser = await retrieveDotsUser(partner);

return NextResponse.json(dotsUser);
});
119 changes: 20 additions & 99 deletions apps/web/lib/actions/partners/accept-program-invite.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
"use server";

import { getEvents } from "@/lib/analytics/get-events";
import { createSaleData } from "@/lib/api/sales/sale";
import { createDotsUser } from "@/lib/dots/create-dots-user";
import { retrieveDotsUser } from "@/lib/dots/retrieve-dots-user";
import { createId } from "@/lib/api/utils";
import { prisma } from "@/lib/prisma";
import { recordLink } from "@/lib/tinybird/record-link";
import { saleEventResponseSchema } from "@/lib/zod/schemas/sales";
import { z } from "zod";
import { authPartnerActionClient } from "../safe-action";
import { backfillLinkData } from "./backfill-link-data";
import { enrollDotsUserApp } from "./enroll-dots-user-app";

export const acceptProgramInviteAction = authPartnerActionClient
.schema(
Expand All @@ -27,123 +24,47 @@ export const acceptProgramInviteAction = authPartnerActionClient

const programInvite = await prisma.programInvite.findUniqueOrThrow({
where: { id: programInviteId },
include: {
program: {
select: {
workspace: {
select: {
id: true,
dotsAppId: true,
},
},
},
},
},
});

// enroll partner in program and delete the invite
const [programEnrollment, dotsUser, _] = await Promise.all([
const [programEnrollment, _] = await Promise.all([
prisma.programEnrollment.create({
data: {
id: createId({ prefix: "pge_" }),
programId: programInvite.programId,
linkId: programInvite.linkId,
partnerId: partner.id,
status: "approved",
},
include: {
program: true,
link: {
program: {
include: {
tags: true,
workspace: true,
},
},
},
}),

retrieveDotsUser({
dotsUserId: partner.dotsUserId,
partner,
}),

prisma.programInvite.delete({
where: { id: programInvite.id },
}),
]);

const workspace = programInvite.program.workspace;
const workspace = programEnrollment.program.workspace;

if (workspace.dotsAppId) {
const newDotsUser = await createDotsUser({
dotsAppId: workspace.dotsAppId, // we need to create a new Dots user under the Program's Dots App
userInfo: {
firstName: dotsUser.first_name,
lastName: dotsUser.last_name,
email: dotsUser.email,
countryCode: dotsUser.phone_number.country_code,
phoneNumber: dotsUser.phone_number.phone_number,
},
});

await prisma.programEnrollment.update({
where: {
id: programEnrollment.id,
},
data: { dotsUserId: newDotsUser.id },
});
if (!workspace.dotsAppId) {
throw new Error("Workspace does not have a Dots app ID");
}

const { link, program } = programEnrollment;

// Backfill sales for the partner's link
const saleEvents = await getEvents({
workspaceId: workspace.id,
linkId: programInvite.linkId,
event: "sales",
interval: "all",
page: 1,
limit: 5000,
order: "desc",
sortBy: "timestamp",
});

const data = saleEvents.map(
(e: z.infer<typeof saleEventResponseSchema>) => ({
...createSaleData({
customerId: e.customer.id,
linkId: e.link.id,
clickId: e.click.id,
invoiceId: e.invoice_id,
eventId: e.eventId,
paymentProcessor: e.payment_processor,
amount: e.sale.amount,
currency: "usd",
partnerId: partner.id,
program,
metadata: e.click,
}),
createdAt: new Date(e.timestamp),
const res = await Promise.all([
enrollDotsUserApp({
partner,
dotsAppId: workspace.dotsAppId,
programEnrollmentId: programEnrollment.id,
}),
);

if (data.length > 0) {
await prisma.sale.createMany({
data,
skipDuplicates: true,
});
}
backfillLinkData(programEnrollment.id),
]);

// Backfill programId for the partner's link on TB
if (link) {
await recordLink({
domain: link.domain,
key: link.key,
link_id: link.id,
created_at: link.createdAt,
url: link.url,
tag_ids: link.tags.map((t) => t.id) || [],
program_id: program.id,
workspace_id: workspace.id,
deleted: false,
});
}
return {
id: programEnrollment.id,
};
});
86 changes: 86 additions & 0 deletions apps/web/lib/actions/partners/backfill-link-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { getEvents } from "@/lib/analytics/get-events";
import { createSaleData } from "@/lib/api/sales/sale";
import { prisma } from "@/lib/prisma";
import { recordLink } from "@/lib/tinybird";
import z from "@/lib/zod";
import { saleEventResponseSchema } from "@/lib/zod/schemas/sales";

export const backfillLinkData = async (programEnrollmentId: string) => {
const programEnrollment = await prisma.programEnrollment.findUniqueOrThrow({
where: {
id: programEnrollmentId,
},
include: {
program: {
include: {
workspace: true,
},
},
link: {
include: {
tags: true,
},
},
partner: true,
},
});

const { program, link, partner } = programEnrollment;
const workspace = program.workspace;

if (!link) {
console.warn(
`No link found for program enrollment ${programEnrollment.id}`,
);
return;
}

const saleEvents = await getEvents({
workspaceId: workspace.id,
linkId: link.id,
event: "sales",
interval: "all",
page: 1,
limit: 5000,
order: "desc",
sortBy: "timestamp",
});

const data = saleEvents.map((e: z.infer<typeof saleEventResponseSchema>) => ({
...createSaleData({
customerId: e.customer.id,
linkId: e.link.id,
clickId: e.click.id,
invoiceId: e.invoice_id,
eventId: e.eventId,
paymentProcessor: e.payment_processor,
amount: e.sale.amount,
currency: "usd",
partnerId: partner.id,
program,
metadata: e.click,
}),
createdAt: new Date(e.timestamp),
}));

return await Promise.all([
// Backfill sales for the partner's link
data.length > 0 &&
prisma.sale.createMany({
data,
skipDuplicates: true,
}),
// Add link's programId to dub_links_metadata in Tinybird
recordLink({
domain: link.domain,
key: link.key,
link_id: link.id,
created_at: link.createdAt,
url: link.url,
tag_ids: link.tags.map((t) => t.id) || [],
program_id: program.id,
workspace_id: workspace.id,
deleted: false,
}),
]);
};
5 changes: 1 addition & 4 deletions apps/web/lib/actions/partners/create-dots-withdrawal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ export const createDotsWithdrawalAction = authPartnerActionClient
throw new Error("Partner does not have a Dots user ID");
}

const dotsUser = await retrieveDotsUser({
dotsUserId: partner.dotsUserId,
partner,
});
const dotsUser = await retrieveDotsUser(partner);

const amountToWithdraw = dotsUser.wallet.withdrawable_amount;

Expand Down
39 changes: 39 additions & 0 deletions apps/web/lib/actions/partners/enroll-dots-user-app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createDotsUser } from "@/lib/dots/create-dots-user";
import { retrieveDotsUser } from "@/lib/dots/retrieve-dots-user";
import { prisma } from "@/lib/prisma";
import { PartnerProps } from "@/lib/types";

export const enrollDotsUserApp = async ({
partner,
dotsAppId,
programEnrollmentId,
}: {
partner: PartnerProps;
dotsAppId: string;
programEnrollmentId: string;
}) => {
if (!partner.dotsUserId) {
console.warn(`Partner ${partner.id} has no Dots user ID`);
return;
}

const dotsUser = await retrieveDotsUser(partner);

const newDotsUser = await createDotsUser({
dotsAppId, // we need to create a new Dots user under the Program's Dots App
userInfo: {
firstName: dotsUser.first_name,
lastName: dotsUser.last_name,
email: dotsUser.email,
countryCode: dotsUser.phone_number.country_code,
phoneNumber: dotsUser.phone_number.phone_number,
},
});

return await prisma.programEnrollment.update({
where: {
id: programEnrollmentId,
},
data: { dotsUserId: newDotsUser.id },
});
};
12 changes: 3 additions & 9 deletions apps/web/lib/dots/retrieve-dots-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,13 @@ import { PartnerProps } from "../types";
import { dotsFetch } from "./fetch";
import { dotsUserSchema } from "./schemas";

export const retrieveDotsUser = async ({
dotsUserId,
partner,
}: {
dotsUserId: string;
partner: PartnerProps;
}) => {
export const retrieveDotsUser = async (partner: PartnerProps) => {
const [dotsUser, payoutMethods] = await Promise.all([
dotsFetch(`/users/${dotsUserId}`, {
dotsFetch(`/users/${partner.dotsUserId}`, {
method: "GET",
dotsAppId: "default",
}),
dotsFetch(`/users/${dotsUserId}/payout-methods`, {
dotsFetch(`/users/${partner.dotsUserId}/payout-methods`, {
method: "GET",
dotsAppId: "default",
}),
Expand Down
5 changes: 1 addition & 4 deletions apps/web/scripts/backfill-dots-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ async function main() {
return;
}

const dotsUser = await retrieveDotsUser({
dotsUserId: partner.dotsUserId,
partner,
});
const dotsUser = await retrieveDotsUser(partner);

const userInfo = {
firstName: dotsUser.first_name,
Expand Down

0 comments on commit b637959

Please sign in to comment.