-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Rewardful import coupons #2963
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
Rewardful import coupons #2963
Changes from 11 commits
fa82d53
bb861bf
2b5ada9
32ff7e0
3e3adae
a9a4725
6f1b31c
d1924eb
ca76c57
31e2bc2
8e42b2c
cdd99ba
dbd6ba5
d0512a9
d7314e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { prisma } from "@dub/prisma"; | ||
import { bulkCreateLinks } from "../api/links"; | ||
import { redis } from "../upstash"; | ||
import { RewardfulApi } from "./api"; | ||
import { MAX_BATCHES, rewardfulImporter } from "./importer"; | ||
import { RewardfulImportPayload } from "./types"; | ||
|
||
export async function importAffiliateCoupons(payload: RewardfulImportPayload) { | ||
const { programId, userId, page = 1 } = payload; | ||
|
||
const program = await prisma.program.findUniqueOrThrow({ | ||
where: { | ||
id: programId, | ||
}, | ||
select: { | ||
id: true, | ||
workspaceId: true, | ||
domain: true, | ||
url: true, | ||
defaultFolderId: true, | ||
}, | ||
}); | ||
|
||
const { token } = await rewardfulImporter.getCredentials(program.workspaceId); | ||
|
||
const rewardfulApi = new RewardfulApi({ token }); | ||
|
||
let currentPage = page; | ||
let hasMore = true; | ||
let processedBatches = 0; | ||
|
||
while (hasMore && processedBatches < MAX_BATCHES) { | ||
const affiliateCoupons = await rewardfulApi.listAffiliateCoupons({ | ||
page: currentPage, | ||
}); | ||
|
||
if (affiliateCoupons.length === 0) { | ||
hasMore = false; | ||
break; | ||
} | ||
devkiran marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const affiliateIds = affiliateCoupons.map( | ||
(affiliateCoupon) => affiliateCoupon.affiliate_id, | ||
); | ||
|
||
const results = await redis.hmget<Record<string, string>>( | ||
`rewardful:affiliates:${program.id}`, | ||
...affiliateIds, | ||
); | ||
|
||
const filteredPartners = Object.fromEntries( | ||
Object.entries(results ?? {}).filter( | ||
([_, value]) => value !== null && value !== undefined, | ||
), | ||
); | ||
|
||
const affiliateIdToCouponsMap = affiliateCoupons.reduce( | ||
(acc, coupon) => { | ||
if (!acc[coupon.affiliate_id]) { | ||
acc[coupon.affiliate_id] = []; | ||
} | ||
|
||
acc[coupon.affiliate_id].push(coupon); | ||
return acc; | ||
}, | ||
{} as Record<string, typeof affiliateCoupons>, | ||
); | ||
|
||
if (Object.keys(filteredPartners).length > 0) { | ||
const linksToCreate: any[] = []; | ||
devkiran marked this conversation as resolved.
Show resolved
Hide resolved
devkiran marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for (const [affiliateId, partnerId] of Object.entries(filteredPartners)) { | ||
const coupons = affiliateIdToCouponsMap[affiliateId]; | ||
|
||
if (!coupons) { | ||
continue; | ||
} | ||
|
||
const activeCoupons = affiliateCoupons.filter( | ||
(coupon) => !coupon.archived, | ||
); | ||
|
||
if (activeCoupons.length === 0) { | ||
continue; | ||
} | ||
|
||
linksToCreate.push( | ||
...activeCoupons.map((coupon) => ({ | ||
domain: program.domain, | ||
key: coupon.token, | ||
url: program.url, | ||
trackConversion: true, | ||
programId, | ||
partnerId, | ||
folderId: program.defaultFolderId, | ||
userId, | ||
projectId: program.workspaceId, | ||
})), | ||
); | ||
} | ||
|
||
await bulkCreateLinks({ | ||
links: linksToCreate, | ||
}); | ||
} | ||
|
||
currentPage++; | ||
processedBatches++; | ||
} | ||
|
||
if (!hasMore) { | ||
await redis.del(`rewardful:affiliates:${program.id}`); | ||
} | ||
devkiran marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const action = hasMore ? "import-affiliate-coupons" : "import-customers"; | ||
|
||
await rewardfulImporter.queue({ | ||
...payload, | ||
action, | ||
page: hasMore ? currentPage : undefined, | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -99,21 +99,28 @@ async function createCustomer({ | |||||||||||||||||||||||||||||||||
entity_id: referralId, | ||||||||||||||||||||||||||||||||||
} as const; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if (!referral.link) { | ||||||||||||||||||||||||||||||||||
if (!referral.link && !referral.coupon) { | ||||||||||||||||||||||||||||||||||
await logImportError({ | ||||||||||||||||||||||||||||||||||
...commonImportLogInputs, | ||||||||||||||||||||||||||||||||||
code: "LINK_NOT_FOUND", | ||||||||||||||||||||||||||||||||||
message: `Link not found for referral ${referralId} (could've been a coupon-based referral).`, | ||||||||||||||||||||||||||||||||||
message: `Link or coupon not found for referral ${referralId}.`, | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const shortLinkToken = referral.link?.token || referral.coupon?.token; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if (!shortLinkToken) { | ||||||||||||||||||||||||||||||||||
console.error(`Short link token not found for referral ${referralId}.`); | ||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+112
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use structured error logging for consistency. While the validation logic is sound, the error handling here uses Apply this diff to maintain consistent error logging: const shortLinkToken = referral.link?.token || referral.coupon?.token;
if (!shortLinkToken) {
- console.error(`Short link token not found for referral ${referralId}.`);
+ await logImportError({
+ ...commonImportLogInputs,
+ code: "LINK_TOKEN_NOT_FOUND",
+ message: `Link token not found for referral ${referralId}.`,
+ });
return;
} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const link = await prisma.link.findUnique({ | ||||||||||||||||||||||||||||||||||
where: { | ||||||||||||||||||||||||||||||||||
domain_key: { | ||||||||||||||||||||||||||||||||||
domain: program.domain!, | ||||||||||||||||||||||||||||||||||
key: referral.link.token, | ||||||||||||||||||||||||||||||||||
key: shortLinkToken, | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
|
@@ -122,7 +129,7 @@ async function createCustomer({ | |||||||||||||||||||||||||||||||||
await logImportError({ | ||||||||||||||||||||||||||||||||||
...commonImportLogInputs, | ||||||||||||||||||||||||||||||||||
code: "LINK_NOT_FOUND", | ||||||||||||||||||||||||||||||||||
message: `Link not found for referral ${referralId} (token: ${referral.link.token}).`, | ||||||||||||||||||||||||||||||||||
message: `Link not found for referral ${referralId} (token: ${shortLinkToken}).`, | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,44 +4,22 @@ import { EventType } from "@dub/prisma/client"; | |
import "dotenv-flow/config"; | ||
|
||
async function main() { | ||
// @ts-ignore (old sales table) | ||
const sales = await prisma.sale.findMany({ | ||
select: { | ||
id: true, | ||
programId: true, | ||
partnerId: true, | ||
linkId: true, | ||
payoutId: true, | ||
invoiceId: true, | ||
customerId: true, | ||
eventId: true, | ||
amount: true, | ||
earnings: true, | ||
currency: true, | ||
status: true, | ||
createdAt: true, | ||
updatedAt: true, | ||
const program = await prisma.program.findUniqueOrThrow({ | ||
where: { | ||
id: "prog_1K2J9DRWPPJ2F1RX53N92TSGA", | ||
}, | ||
take: 1000, | ||
skip: 2500, | ||
}); | ||
|
||
if (!sales.length) { | ||
console.log("No sales found."); | ||
return; | ||
} | ||
|
||
await prisma.commission.createMany({ | ||
data: sales.map((sale) => ({ | ||
...sale, | ||
id: createId({ prefix: "cm_" }), | ||
type: EventType.sale, | ||
quantity: 1, | ||
})), | ||
skipDuplicates: true, | ||
const link = await prisma.link.findUnique({ | ||
where: { | ||
domain_key: { | ||
domain: program.domain!, | ||
key: "BESTPIC", | ||
}, | ||
}, | ||
}); | ||
|
||
console.log(`Migrated ${sales.length} sales.`); | ||
console.log(link) | ||
} | ||
Comment on lines
6
to
45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainDebug code should not be committed to the repository. This migration script has been replaced with what appears to be test or debug code that:
This should either be reverted to its original state, removed from the PR, or explained if there's a specific reason for this change. Please clarify the intent of this change. If this was meant for local testing, it should not be included in the PR. I'll investigate the migration script to verify the review comment. Let me gather information about the file's history and context.
Length of output: 0 🏁 Script executed: #!/bin/bash
# Check git log to see recent changes to this file
git log --oneline -n 20 -- apps/web/scripts/migrations/migrate-sales.ts || true Length of output: 106 🏁 Script executed: #!/bin/bash
# Find other migration scripts to compare pattern
fd -t f 'migrate-.*\.ts$' apps/web/scripts/migrations/ || fd -t f '.*migration.*\.ts$' apps/web/scripts/ Length of output: 759 🏁 Script executed: #!/bin/bash
# Show the complete current content of the migration file
cat apps/web/scripts/migrations/migrate-sales.ts || echo "File not found" Length of output: 507 🏁 Script executed: #!/bin/bash
# Check for any test or commented-out code patterns in the file
grep -n "test\|TODO\|FIXME\|debug\|temporary" apps/web/scripts/migrations/migrate-sales.ts -i || echo "No markers found" Length of output: 16 I need to compare with other migration files to understand the expected pattern and clarify the file's status in this PR.
Length of output: 63 🏁 Script executed: #!/bin/bash
# Examine 2 other migration files to understand expected pattern
echo "=== migrate-rewards.ts ===" && head -50 apps/web/scripts/migrations/migrate-rewards.ts && echo -e "\n=== migrate-discounts.ts ===" && head -50 apps/web/scripts/migrations/migrate-discounts.ts Length of output: 2296 🏁 Script executed: #!/bin/bash
# Check if migrate-sales.ts exists in HEAD (to verify if it's new or modified)
git show HEAD:apps/web/scripts/migrations/migrate-sales.ts 2>/dev/null | head -40 || echo "File does not exist in HEAD" Length of output: 507 I need to understand the commit history context better to determine if this is a new issue in this PR or a pre-existing problem.
Length of output: 59 🏁 Script executed: #!/bin/bash
# Check what branch we're on and compare to main
git log --oneline -n 1 && echo "---" && git log --oneline origin/main -n 1 2>/dev/null || echo "Cannot access origin/main" Length of output: 114 🏁 Script executed: #!/bin/bash
# Show commits in current branch that aren't in main
git log --oneline origin/main..HEAD -- apps/web/scripts/migrations/migrate-sales.ts 2>/dev/null || echo "Cannot compare or no changes in this file since main" Length of output: 106 Remove or replace debug code in migration script. This file is part of the current PR branch and contains incomplete code. Unlike other migration scripts in the repository (e.g.,
Replace with actual migration logic or remove the file if no migration is needed. 🤖 Prompt for AI Agents
|
||
|
||
main(); |
Uh oh!
There was an error while loading. Please reload this page.