feat: region payment providers management workflows/api (#6864)
This commit is contained in:
committed by
GitHub
parent
12fcb655cd
commit
e944a627f0
@@ -0,0 +1,216 @@
|
||||
import { ModuleRegistrationName, RemoteLink } from "@medusajs/modules-sdk"
|
||||
import { IPaymentModuleService, RemoteQueryFunction } from "@medusajs/types"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
arrayDifference,
|
||||
ContainerRegistrationKeys,
|
||||
LINKS,
|
||||
MedusaError,
|
||||
Modules,
|
||||
promiseAll,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export interface SetRegionsPaymentProvidersStepInput {
|
||||
input: {
|
||||
id: string
|
||||
payment_providers?: string[]
|
||||
}[]
|
||||
}
|
||||
|
||||
interface FilteredSetRegionsPaymentProvidersStepInput {
|
||||
id: string
|
||||
payment_providers: string[]
|
||||
}
|
||||
|
||||
type LinkItems = {
|
||||
[Modules.REGION]: { region_id: string }
|
||||
[Modules.PAYMENT]: { payment_provider_id: string }
|
||||
}[]
|
||||
|
||||
async function validatePaymentProvidersExists(
|
||||
paymentService: IPaymentModuleService,
|
||||
paymentProviderIds: string[]
|
||||
) {
|
||||
const paymentProviders = await paymentService.listPaymentProviders({
|
||||
id: { $in: paymentProviderIds },
|
||||
is_enabled: true,
|
||||
})
|
||||
|
||||
const retrievedPaymentProviderIds = paymentProviders.map((p) => p.id)
|
||||
|
||||
const missingProviders = arrayDifference(
|
||||
paymentProviderIds,
|
||||
retrievedPaymentProviderIds
|
||||
)
|
||||
|
||||
if (missingProviders.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Payment providers with ids ${missingProviders.join(
|
||||
", "
|
||||
)} not found or not enabled`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function getCurrentRegionPaymentProvidersLinks(
|
||||
regionIds: string[],
|
||||
{ remoteQuery }: { remoteQuery: RemoteQueryFunction }
|
||||
): Promise<
|
||||
{
|
||||
[Modules.REGION]: { region_id: string }
|
||||
[Modules.PAYMENT]: { payment_provider_id: string }
|
||||
}[]
|
||||
> {
|
||||
const query = remoteQueryObjectFromString({
|
||||
service: LINKS.RegionPaymentProvider,
|
||||
variables: {
|
||||
filters: { region_id: regionIds },
|
||||
take: null,
|
||||
},
|
||||
fields: ["region_id", "payment_provider_id"],
|
||||
})
|
||||
|
||||
const regionProviderLinks = (await remoteQuery(query)) as {
|
||||
region_id: string
|
||||
payment_provider_id: string
|
||||
}[]
|
||||
|
||||
return regionProviderLinks.map((region) => {
|
||||
return {
|
||||
[Modules.REGION]: {
|
||||
region_id: region.region_id,
|
||||
},
|
||||
[Modules.PAYMENT]: {
|
||||
payment_provider_id: region.payment_provider_id,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const setRegionsPaymentProvidersStepId =
|
||||
"add-region-payment-providers-step"
|
||||
export const setRegionsPaymentProvidersStep = createStep(
|
||||
setRegionsPaymentProvidersStepId,
|
||||
async (data: SetRegionsPaymentProvidersStepInput, { container }) => {
|
||||
const dataInputToProcess = data.input.filter((inputData) => {
|
||||
return inputData.payment_providers?.length
|
||||
}) as FilteredSetRegionsPaymentProvidersStepInput[]
|
||||
|
||||
if (!dataInputToProcess.length) {
|
||||
return new StepResponse(void 0)
|
||||
}
|
||||
|
||||
const paymentService = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
const remoteLink = container.resolve<RemoteLink>(
|
||||
ContainerRegistrationKeys.REMOTE_LINK
|
||||
)
|
||||
const remoteQuery = container.resolve<RemoteQueryFunction>(
|
||||
ContainerRegistrationKeys.REMOTE_QUERY
|
||||
)
|
||||
|
||||
const allPaymentProviderIds = dataInputToProcess
|
||||
.map((inputData) => {
|
||||
return inputData.payment_providers!
|
||||
})
|
||||
.flat()
|
||||
const uniquePaymentProviderIds = Array.from(
|
||||
new Set<string>(allPaymentProviderIds)
|
||||
)
|
||||
|
||||
await validatePaymentProvidersExists(
|
||||
paymentService,
|
||||
uniquePaymentProviderIds
|
||||
)
|
||||
|
||||
const regionIds = dataInputToProcess.map((inputData) => inputData.id)
|
||||
const currentExistingLinks = await getCurrentRegionPaymentProvidersLinks(
|
||||
regionIds,
|
||||
{ remoteQuery }
|
||||
)
|
||||
|
||||
const linksToRemove: LinkItems = currentExistingLinks
|
||||
.filter((existingLink) => {
|
||||
return !dataInputToProcess.some((input) => {
|
||||
return (
|
||||
input.id === existingLink[Modules.REGION].region_id &&
|
||||
input.payment_providers.includes(
|
||||
existingLink[Modules.PAYMENT].payment_provider_id
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
.map((link) => {
|
||||
return {
|
||||
[Modules.REGION]: { region_id: link[Modules.REGION].region_id },
|
||||
[Modules.PAYMENT]: {
|
||||
payment_provider_id: link[Modules.PAYMENT].payment_provider_id,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const linksToCreate = dataInputToProcess
|
||||
.map((inputData) => {
|
||||
return inputData.payment_providers.map((provider) => {
|
||||
const alreadyExists = currentExistingLinks.some((link) => {
|
||||
return (
|
||||
link[Modules.REGION].region_id === inputData.id &&
|
||||
link[Modules.PAYMENT].payment_provider_id === provider
|
||||
)
|
||||
})
|
||||
|
||||
if (alreadyExists) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
[Modules.REGION]: { region_id: inputData.id },
|
||||
[Modules.PAYMENT]: { payment_provider_id: provider },
|
||||
}
|
||||
})
|
||||
})
|
||||
.flat()
|
||||
.filter((d): d is LinkItems[0] => !!d)
|
||||
|
||||
const promises: Promise<unknown[]>[] = []
|
||||
|
||||
if (linksToRemove.length) {
|
||||
promises.push(remoteLink.dismiss(linksToRemove))
|
||||
}
|
||||
|
||||
if (linksToCreate.length) {
|
||||
promises.push(remoteLink.create(linksToCreate))
|
||||
}
|
||||
|
||||
await promiseAll(promises)
|
||||
|
||||
return new StepResponse(void 0, {
|
||||
linksToCreate: linksToRemove,
|
||||
linksToRemove: linksToCreate,
|
||||
})
|
||||
},
|
||||
async (rollbackData, { container }) => {
|
||||
if (!rollbackData) {
|
||||
return
|
||||
}
|
||||
|
||||
const remoteLink = container.resolve<RemoteLink>(
|
||||
ContainerRegistrationKeys.REMOTE_LINK
|
||||
)
|
||||
|
||||
const promises: Promise<unknown[]>[] = []
|
||||
|
||||
if (rollbackData.linksToRemove.length) {
|
||||
promises.push(remoteLink.dismiss(rollbackData.linksToRemove))
|
||||
}
|
||||
|
||||
if (rollbackData.linksToCreate.length) {
|
||||
promises.push(remoteLink.create(rollbackData.linksToCreate))
|
||||
}
|
||||
|
||||
await promiseAll(promises)
|
||||
}
|
||||
)
|
||||
@@ -29,6 +29,10 @@ export const updateRegionsStep = createStep(
|
||||
relations,
|
||||
})
|
||||
|
||||
if (Object.keys(data.update).length === 0) {
|
||||
return new StepResponse(prevData, [])
|
||||
}
|
||||
|
||||
const regions = await service.update(data.selector, data.update)
|
||||
|
||||
return new StepResponse(regions, prevData)
|
||||
@@ -48,7 +52,7 @@ export const updateRegionsStep = createStep(
|
||||
name: r.name,
|
||||
currency_code: r.currency_code,
|
||||
metadata: r.metadata,
|
||||
countries: r.countries.map((c) => c.iso_2),
|
||||
countries: r.countries?.map((c) => c.iso_2),
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,57 @@
|
||||
import { CreateRegionDTO, RegionDTO } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
WorkflowData,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { createRegionsStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { regionsData: CreateRegionDTO[] }
|
||||
import { setRegionsPaymentProvidersStep } from "../steps/set-regions-payment-providers"
|
||||
|
||||
export const createRegionsWorkflowId = "create-regions"
|
||||
export const createRegionsWorkflow = createWorkflow(
|
||||
createRegionsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<RegionDTO[]> => {
|
||||
return createRegionsStep(input.regionsData)
|
||||
(
|
||||
input: WorkflowData<WorkflowTypes.RegionWorkflow.CreateRegionsWorkflowInput>
|
||||
): WorkflowData<WorkflowTypes.RegionWorkflow.CreateRegionsWorkflowOutput> => {
|
||||
const data = transform(input, (data) => {
|
||||
const regionIndexToPaymentProviders = data.regions.map(
|
||||
(region, index) => {
|
||||
return {
|
||||
region_index: index,
|
||||
payment_providers: region.payment_providers,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
regions: data.regions,
|
||||
regionIndexToPaymentProviders,
|
||||
}
|
||||
})
|
||||
|
||||
const regions = createRegionsStep(data.regions)
|
||||
|
||||
const normalizedRegionProviderData = transform(
|
||||
{
|
||||
regionIndexToPaymentProviders: data.regionIndexToPaymentProviders,
|
||||
regions,
|
||||
},
|
||||
(data) => {
|
||||
return data.regionIndexToPaymentProviders.map(
|
||||
({ region_index, payment_providers }) => {
|
||||
return {
|
||||
id: data.regions[region_index].id,
|
||||
payment_providers,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
setRegionsPaymentProvidersStep({
|
||||
input: normalizedRegionProviderData,
|
||||
})
|
||||
|
||||
return regions
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,22 +1,46 @@
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import {
|
||||
FilterableRegionProps,
|
||||
RegionDTO,
|
||||
UpdateRegionDTO,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
createWorkflow,
|
||||
transform,
|
||||
WorkflowData,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { updateRegionsStep } from "../steps"
|
||||
|
||||
type UpdateRegionsStepInput = {
|
||||
selector: FilterableRegionProps
|
||||
update: UpdateRegionDTO
|
||||
}
|
||||
|
||||
type WorkflowInput = UpdateRegionsStepInput
|
||||
import { setRegionsPaymentProvidersStep } from "../steps/set-regions-payment-providers"
|
||||
|
||||
export const updateRegionsWorkflowId = "update-regions"
|
||||
export const updateRegionsWorkflow = createWorkflow(
|
||||
updateRegionsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<RegionDTO[]> => {
|
||||
return updateRegionsStep(input)
|
||||
(
|
||||
input: WorkflowData<WorkflowTypes.RegionWorkflow.UpdateRegionsWorkflowInput>
|
||||
): WorkflowData<WorkflowTypes.RegionWorkflow.UpdateRegionsWorkflowOutput> => {
|
||||
const data = transform(input, (data) => {
|
||||
const { selector, update } = data
|
||||
const { payment_providers = [], ...rest } = update
|
||||
return {
|
||||
selector,
|
||||
update: rest,
|
||||
payment_providers,
|
||||
}
|
||||
})
|
||||
|
||||
const regions = updateRegionsStep(data)
|
||||
|
||||
const upsertProvidersNormalizedInput = transform(
|
||||
{ data, regions },
|
||||
(data) => {
|
||||
return data.regions.map((region) => {
|
||||
return {
|
||||
id: region.id,
|
||||
payment_providers: data.data.payment_providers,
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
setRegionsPaymentProvidersStep({
|
||||
input: upsertProvidersNormalizedInput,
|
||||
})
|
||||
|
||||
return regions
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user