Skip to content

Instantly share code, notes, and snippets.

@izakfilmalter
Last active November 28, 2023 16:26
Show Gist options
  • Save izakfilmalter/afe3444d81d3b79d42f0eb7a5c5b6e9d to your computer and use it in GitHub Desktop.
Save izakfilmalter/afe3444d81d3b79d42f0eb7a5c5b6e9d to your computer and use it in GitHub Desktop.
import type { IO } from '@trigger.dev/sdk'
import { Option, pipe, ReadonlyArray, String } from 'effect'
import qs from 'qs'
import type { z } from 'zod'
import type { PlanningCenterDonation } from '@steepleinc/shared'
import { asyncNoOp } from '@steepleinc/shared'
import { transformPlanningCenterDonation } from '../../helpers/planningCenterTransformers'
import type { triggerRouter } from '../../types'
import { saveDonationsTask } from '../replicache/saveDonations.task'
import { getPlanningCenterAPITask } from './getPlanningCenterAPI.task'
import { updateSyncStatusTask } from './updateSyncStatus.task'
const importNextPage = (params: {
nextUrl?: string
io: IO
orgId: string
alreadyImportedIds: ReadonlyArray<string>
}) => {
const { nextUrl, io, orgId, alreadyImportedIds } = params
return pipe(
nextUrl,
Option.fromNullable,
Option.match({
onNone: asyncNoOp,
onSome: async (x) => {
const { offset } = qs.parse(
pipe(x, String.split('?'), ReadonlyArray.lastNonEmpty),
) as {
include: string
offset: string
order: string
per_page: string
}
return await importPlanningCenterDonationsTaskChain({
orgId,
url: x,
alreadyImportedIds,
io,
offset,
})
},
}),
)
}
export const importPlanningCenterDonationsTaskChain = async (
params: {
io: IO
offset: string
} & z.infer<typeof triggerRouter.importPlanningCenterDonations.schema>,
) => {
const {
orgId,
url = `https://api.planningcenteronline.com/giving/v2/donations?${qs.stringify(
{ per_page: 100 },
{ arrayFormat: 'comma' },
)}`,
alreadyImportedIds = [],
io,
offset,
} = params
// Toggle syncStatus to syncing.
await io.runTask(
...updateSyncStatusTask({
taskId: `startSyncingDonations-${offset}`,
orgId,
key: 'donations',
status: { tag: 'syncing' },
}),
)
// Get donations.
const donationOpt = await io
.runTask(
...getPlanningCenterAPITask<Array<PlanningCenterDonation>, null>({
url,
orgId,
setSyncStatusOnFailure: {
orgId,
key: 'donations',
status: {
tag: 'error',
error: `It looks like something when wrong trying to sync your Donations. Contact Steeple support to get this resolved.`,
},
},
}),
)
.then((x) =>
pipe(
x,
Option.fromNullable,
Option.map((y) => ({
donations: pipe(
y.data,
ReadonlyArray.map((z) =>
transformPlanningCenterDonation({ donation: z }),
),
),
nextUrl: y.links.next,
})),
),
)
await io.logger.log(
`${offset}: Got donations: ${pipe(
donationOpt,
Option.match({
onNone: () => 'No Donations',
onSome: (x) => x.donations.length,
}),
)}`,
)
await pipe(
donationOpt,
Option.match({
// Failed to get donations
onNone: asyncNoOp,
// Get donations
onSome: async ({ donations, nextUrl }) => {
// Save donations.
await io.runTask(...saveDonationsTask({ orgId, donations, offset }))
// Import next page if we have a next page.
await importNextPage({
nextUrl,
orgId,
io,
alreadyImportedIds: pipe(
alreadyImportedIds,
ReadonlyArray.appendAll(
pipe(
donations,
ReadonlyArray.map((y) => y.id),
),
),
),
})
await pipe(
nextUrl,
Option.fromNullable,
Option.match({
// No more donations. Clean up time.
onNone: async () => {
// Finished syncing
await io.runTask(
...updateSyncStatusTask({
taskId: `finishSyncingDonations-${offset}`,
orgId,
key: 'donations',
status: { tag: 'done' },
}),
)
},
// More donations. NoOp
onSome: asyncNoOp,
}),
)
},
}),
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment