@@ -0,0 +1,151 @@
|
||||
// Generated SDK adapter. Production dispatch requires a server-prepared request binding.
|
||||
|
||||
import {
|
||||
businessAppError,
|
||||
type Client,
|
||||
canonicalActionJson,
|
||||
requestBusinessBinding,
|
||||
requiresAppActionTransport,
|
||||
toBusinessError,
|
||||
} from "@conusai-kit/business"
|
||||
import { type Container, forbidden, type ProcedureKind } from "@conusai-kit/core"
|
||||
import {
|
||||
type ContactPoint,
|
||||
type ContactValue,
|
||||
crmAddContactPoint,
|
||||
crmCreateParty,
|
||||
crmGetParty,
|
||||
crmListContactPoints,
|
||||
crmListParties,
|
||||
type Party,
|
||||
} from "@conusai-sdk/crm"
|
||||
import { createClient } from "@conusai-sdk/crm/client"
|
||||
import { type ContactView, type PartyView, SUBJECT_SCHEME } from "../contract/index.js"
|
||||
import { contactRequestBody, partyRequestBody } from "../core/action-plan.js"
|
||||
import { CRM_SERVICE, type CrmReads, type CrmService, crmReadsOf } from "../core/ports.js"
|
||||
|
||||
const partyView = (party: Party): PartyView => ({
|
||||
partyId: party.id,
|
||||
kind: party.kind,
|
||||
displayName: party.display_name,
|
||||
legalName: party.legal_name ?? null,
|
||||
country: party.country,
|
||||
status: party.status,
|
||||
linked: (party.identifiers ?? []).some((identifier) => identifier.scheme === SUBJECT_SCHEME),
|
||||
})
|
||||
|
||||
/** A contact value as a person reads it. */
|
||||
function valueText(value: ContactValue): string {
|
||||
switch (value.kind) {
|
||||
case "postal":
|
||||
return [value.value.street, value.value.postal_code, value.value.city, value.value.country]
|
||||
.filter((part): part is string => typeof part === "string" && part !== "")
|
||||
.join(", ")
|
||||
case "peppol_endpoint":
|
||||
return `${value.value.eas}:${value.value.id}`
|
||||
default:
|
||||
return value.value
|
||||
}
|
||||
}
|
||||
|
||||
const contactView = (contact: ContactPoint): ContactView =>
|
||||
contact.fields
|
||||
? {
|
||||
contactId: contact.id,
|
||||
kind: contact.fields.kind,
|
||||
value: valueText(contact.fields),
|
||||
purposes: contact.fields.purposes,
|
||||
isPrimary: contact.fields.is_primary,
|
||||
erased: contact.erased,
|
||||
}
|
||||
: // Erased: no personal value remains, only that there was a contact point.
|
||||
{ contactId: contact.id, kind: "email", value: "", purposes: [], isPrimary: false, erased: true }
|
||||
|
||||
/** What a generated operation answered: its data, or the kit's app error for the refusal (`businessAppError`). */
|
||||
async function answer<T>(
|
||||
call: Promise<{ data?: T; error?: unknown; response?: Response }>,
|
||||
): Promise<Exclude<T, undefined>> {
|
||||
let result: { data?: T; error?: unknown; response?: Response }
|
||||
try {
|
||||
result = await call
|
||||
} catch (error) {
|
||||
throw businessAppError(toBusinessError(error, undefined))
|
||||
}
|
||||
if (result.error !== undefined || result.data === undefined) {
|
||||
throw businessAppError(toBusinessError(result.error, result.response))
|
||||
}
|
||||
return result.data as Exclude<T, undefined>
|
||||
}
|
||||
|
||||
/** The port over one client, in one organization of one platform project. */
|
||||
export function sdkCrmService(binding: { client: Client; orgSlug: string; bindingId: string }): CrmService {
|
||||
const { client } = binding
|
||||
const org = { org_slug: binding.orgSlug }
|
||||
return {
|
||||
bindingId: binding.bindingId,
|
||||
async listParties(query) {
|
||||
const page = await answer(crmListParties({ client, path: org, query: { ...query, limit: BigInt(query.limit) } }))
|
||||
return { rows: page.data.map(partyView), ...(page.next_after ? { after: page.next_after } : {}) }
|
||||
},
|
||||
async getParty(partyId) {
|
||||
return partyView(await answer(crmGetParty({ client, path: { ...org, id: partyId } })))
|
||||
},
|
||||
async listContacts(partyId, query) {
|
||||
const page = await answer(
|
||||
crmListContactPoints({
|
||||
client,
|
||||
path: { ...org, id: partyId },
|
||||
query: { ...query, limit: BigInt(query.limit) },
|
||||
}),
|
||||
)
|
||||
return { rows: page.data.map(contactView), ...(page.next_after ? { after: page.next_after } : {}) }
|
||||
},
|
||||
async createParty(input) {
|
||||
const party = await answer(
|
||||
crmCreateParty({
|
||||
client,
|
||||
path: org,
|
||||
body: partyRequestBody(input),
|
||||
bodySerializer: canonicalActionJson,
|
||||
}),
|
||||
)
|
||||
return partyView(party)
|
||||
},
|
||||
async addContact(input) {
|
||||
const contact = await answer(
|
||||
crmAddContactPoint({
|
||||
client,
|
||||
path: { ...org, id: input.partyId },
|
||||
body: contactRequestBody(input),
|
||||
bodySerializer: canonicalActionJson,
|
||||
}),
|
||||
)
|
||||
return contactView(contact)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The service a handler of `kind` may use: the bound one (the token's view for the kind applies), or
|
||||
* the current verified request's, narrowed to its reads for a `read` or `write` handler as the token would.
|
||||
*/
|
||||
export function crmServiceOf(container: Container, kind: "effect"): CrmService
|
||||
export function crmServiceOf(container: Container, kind: ProcedureKind): CrmReads
|
||||
export function crmServiceOf(container: Container, kind: ProcedureKind): CrmReads {
|
||||
if (container.has(CRM_SERVICE)) {
|
||||
if (requiresAppActionTransport(container)) throw forbidden("app action requires prepared business transport")
|
||||
return container.resolve(CRM_SERVICE) as CrmReads
|
||||
}
|
||||
let service: CrmService
|
||||
try {
|
||||
const binding = requestBusinessBinding(container)
|
||||
service = sdkCrmService({
|
||||
client: binding.client(createClient),
|
||||
orgSlug: binding.orgSlug,
|
||||
bindingId: binding.bindingId,
|
||||
})
|
||||
} catch (error) {
|
||||
throw businessAppError(error)
|
||||
}
|
||||
return kind === "effect" ? service : crmReadsOf(service)
|
||||
}
|
||||
Reference in New Issue
Block a user