Skip to content

Commit

Permalink
Transactions sync
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Mar 8, 2024
1 parent e18b5c9 commit 34491b9
Show file tree
Hide file tree
Showing 9 changed files with 1,225 additions and 1,155 deletions.
1 change: 0 additions & 1 deletion apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
"teller-connect-react": "^0.1.0",
"tus-js-client": "^4.0.1",
"use-long-press": "^3.2.0",
"usehooks-ts": "2.16.0",
"zod": "^3.22.4",
"zustand": "^4.5.2"
},
Expand Down
53 changes: 44 additions & 9 deletions apps/dashboard/src/app/api/migrate/route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,55 @@
import { scheduler } from "@midday/jobs/src/transactions/scheduler";
import { Provider } from "@midday/providers";
// import { scheduler } from "@midday/jobs/src/transactions/scheduler";
import { createClient } from "@midday/supabase/server";
import { NextResponse } from "next/server";

export async function GET(req) {
const requestUrl = new URL(req.url);
const teamId = requestUrl.searchParams.get("teamId");
const supabase = createClient();

if (teamId) {
const event = await scheduler.register(teamId, {
type: "interval",
options: {
seconds: 3600, // every 1h
},
const { data: accountsData, error: accountsError } = await supabase
.from("bank_accounts")
.select(
"id, team_id, account_id, bank_connection:bank_connection_id(provider, access_token, enrollment_id)"
)
.eq("team_id", teamId)
.eq("enabled", true);

const promises = accountsData?.map(async (account) => {
const provider = new Provider({
provider: account.bank_connection.provider,
});

return provider.getTransactions({
teamId: account.team_id,
accountId: account.account_id,
accessToken: account.bank_connection?.access_token,
bankAccountId: account.id,
latest: true,
});
});

return NextResponse.json(event);
try {
if (promises) {
const transactions = (await Promise.all(promises)).flat();

return NextResponse.json(transactions);
}
} catch (error) {
console.log(error);
}

return NextResponse.json({ error: "No team ID provided" });
// if (teamId) {
// const event = await scheduler.register(teamId, {
// type: "interval",
// options: {
// seconds: 3600, // every 1h
// },
// });

// return NextResponse.json(event);
// }

// return NextResponse.json({ error: "No team ID provided" });
}
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/email/locales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ type Options = {
locale: string;
};

function translations(locale: string, params: any) {
function translations(locale: string, params?: any) {
switch (locale) {
case "en":
return {
Expand Down Expand Up @@ -107,6 +107,6 @@ function translations(locale: string, params: any) {

export function getI18n({ locale }: Options) {
return {
t: (key: string, params: any) => translations(locale, params)[key],
t: (key: string, params?: any) => translations(locale, params)[key],
};
}
141 changes: 70 additions & 71 deletions packages/jobs/src/transactions/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ client.defineJob({
trigger: eventTrigger({
name: Events.TRANSACTIONS_NOTIFICATION,
schema: z.object({
teamId: z.string(),
transactions: z.array(
z.object({
id: z.string(),
Expand All @@ -27,7 +28,6 @@ client.defineJob({
currency: z.string(),
})
),
teamId: z.string(),
}),
}),
integrations: { supabase },
Expand All @@ -36,85 +36,84 @@ client.defineJob({

const { data: usersData } = await io.supabase.client
.from("users_on_team")
.select("team_id, user:user_id(id, full_name, avatar_url, email, locale)")
.select(
"team_id, user:users_on_team(id, full_name, avatar_url, email, locale)"
)
.eq("team_id", teamId);

const notificationEvents = await Promise.all(
usersData?.map(async ({ user, team_id }) => {
const { t } = getI18n({ locale: user.locale });
const notificationPromises = usersData?.map(async ({ user, team_id }) => {
const { t } = getI18n({ locale: user.locale });
return transactions.map((transaction) => ({
name: TriggerEvents.TransactionNewInApp,
payload: {
recordId: transaction.id,
type: NotificationTypes.Transaction,
description: t("notifications.transaction", {
amount: Intl.NumberFormat(user.locale, {
style: "currency",
currency: transaction.currency,
}).format(transaction.amount),
from: transaction.name,
}),
},
user: {
subscriberId: user.id,
teamId: team_id,
email: user.email,
fullName: user.full_name,
avatarUrl: user.avatar_url,
},
}));
});

return transactions.map((transaction) => ({
name: TriggerEvents.TransactionNewInApp,
payload: {
recordId: transaction.id,
type: NotificationTypes.Transaction,
description: t("notifications.transaction", {
amount: Intl.NumberFormat(user.locale, {
style: "currency",
currency: transaction.currency,
}).format(transaction.amount),
from: transaction.name,
}),
},
user: {
subscriberId: user.id,
teamId: team_id,
email: user.email,
fullName: user.full_name,
avatarUrl: user.avatar_url,
},
}));
})
);
if (notificationPromises) {
const notificationEvents = await Promise.all(notificationPromises);

if (notificationEvents?.length) {
triggerBulk(notificationEvents.flat());
await io.logger.log(
`Sending notifications: ${notificationEvents.length}`
);
// if (notificationEvents?.length) {
// triggerBulk(notificationEvents.flat());
// await io.logger.log(
// `Sending notifications: ${notificationEvents.length}`
// );
// }
}

const emailEvents = await Promise.all(
usersData?.map(async ({ user, team_id }) => {
const { t } = getI18n({ locale: user.locale });
const emailPromises = usersData?.map(async ({ user, team_id }) => {
const { t } = getI18n({ locale: user.locale });

const html = await renderAsync(
TransactionsEmail({
fullName: user.full_name,
transactions: transactions.map((transaction) => ({
id: transaction.id,
date: transaction.date,
amount: transaction.amount,
name: transaction.name,
currency: transaction.currency,
})),
locale: user.locale,
})
);
const html = await renderAsync(
TransactionsEmail({
fullName: user.full_name,
transactions,
locale: user.locale,
})
);

return {
name: TriggerEvents.TransactionNewEmail,
payload: {
subject: t("transactions.subject"),
html,
},
user: {
subscriberId: user.id,
teamId: team_id,
email: user.email,
fullName: user.full_name,
avatarUrl: user.avatar_url,
},
};
});

return {
name: TriggerEvents.TransactionNewEmail,
payload: {
subject: t("transactions.subject"),
html,
},
user: {
subscriberId: user.id,
teamId: team_id,
email: user.email,
fullName: user.full_name,
avatarUrl: user.avatar_url,
},
};
})
);
if (emailPromises) {
const emailEvents = await Promise.all(emailPromises);

if (emailEvents?.length) {
try {
triggerBulk(emailEvents.flat());
} catch (error) {
await io.logger.debug(error);
}
// if (emailEvents?.length) {
// try {
// triggerBulk(emailEvents.flat());
// } catch (error) {
// await io.logger.debug(error);
// }
// }
}
},
});
49 changes: 35 additions & 14 deletions packages/jobs/src/transactions/sync.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Provider } from "@midday/providers";
import { revalidateTag } from "next/cache";
import { client, supabase } from "../client";
import { Jobs } from "../constants";
import { Events, Jobs } from "../constants";
import { scheduler } from "./scheduler";

client.defineJob({
Expand All @@ -14,7 +15,7 @@ client.defineJob({

const teamId = ctx.source?.id as string;

const { data: accountsData, error } = await supabase
const { data: accountsData, error: accountsError } = await supabase
.from("bank_accounts")
.select(
"id, team_id, account_id, bank_connection:bank_connection_id(provider, access_token, enrollment_id)"
Expand All @@ -28,10 +29,8 @@ client.defineJob({
await io.logger.debug("Unregister", event);
}

await io.logger.debug("Accounts", JSON.stringify(accountsData, null, 2));

if (error) {
await io.logger.error("Accounts Error", error);
if (accountsError) {
await io.logger.error("Accounts Error", accountsError);
}

const promises = accountsData?.map(async (account) => {
Expand All @@ -44,25 +43,47 @@ client.defineJob({
accountId: account.account_id,
accessToken: account.bank_connection?.access_token,
bankAccountId: account.id,
latest: true,
});
});

try {
if (promises) {
const transactions = await Promise.all(promises);
const result = await Promise.all(promises);
const transactions = result?.flat();

await io.logger.debug("Transactions", transactions);

if (!transactions?.length) {
return null;
}

// const { error, data: transactionsData } = await supabase
// .from("decrypted_transactions")
// .upsert(transactions, {
// onConflict: "internal_id",
// ignoreDuplicates: true,
// })
// .select("*, name:decrypted_name");
const { error: transactionsError, data: transactionsData } =
await supabase
.from("decrypted_transactions")
.upsert(transactions, {
onConflict: "internal_id",
ignoreDuplicates: true,
})
.select("*, name:decrypted_name");

if (transactionsError) {
await io.logger.error("Transactions error", transactionsError);
}

if (transactionsData && transactionsData?.length > 0) {
await io.sendEvent("🔔 Send notifications", {
name: Events.TRANSACTIONS_NOTIFICATION,
payload: {
teamId,
transactions: transactionsData,
},
});

revalidateTag(`transactions_${teamId}`);
revalidateTag(`spending_${teamId}`);
revalidateTag(`metrics_${teamId}`);
}
}
} catch (error) {
await io.logger.error(JSON.stringify(error, null, 2));
Expand Down
2 changes: 1 addition & 1 deletion packages/providers/src/gocardless/gocardless-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export class GoCardLessApi {
const response = await this.#get<GetTransactionsResponse>(
`/api/v2/accounts/${accountId}/transactions/`,
latest && {
date_to: formatISO(subMonths(new Date(), 1), {
date_from: formatISO(subMonths(new Date(), 1), {
representation: "date",
}),
}
Expand Down
2 changes: 1 addition & 1 deletion packages/supabase/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"dependencies": {
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.39.7",
"@supabase/supabase-js": "2.39.7",
"supabase": "^1.148.6"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 34491b9

Please sign in to comment.