173 lines
6.2 KiB
TypeScript
173 lines
6.2 KiB
TypeScript
// Generated SDK adapter. Production dispatch requires a server-prepared request binding.
|
|
|
|
import { businessAppError, type Client, requestBusinessBinding, toBusinessError } from "@conusai-kit/business"
|
|
import type { Container, ProcedureKind } from "@conusai-kit/core"
|
|
import {
|
|
type CatalogImport,
|
|
catalogFindImports,
|
|
catalogFindProducts,
|
|
catalogGetProduct,
|
|
catalogImportProducts,
|
|
catalogSearchProducts,
|
|
type ImportOutcome,
|
|
type Product,
|
|
} from "@conusai-sdk/catalog"
|
|
import { createClient } from "@conusai-sdk/catalog/client"
|
|
import type { ImportOutcomeView, ImportView, ProductSearchField, ProductView } from "../contract/index.js"
|
|
import { CATALOG_SERVICE, type CatalogReads, type CatalogService, catalogReadsOf } from "../core/ports.js"
|
|
|
|
const productView = (product: Product): ProductView => ({
|
|
productId: product.id,
|
|
code: product.code,
|
|
name: product.name,
|
|
description: product.description ?? "",
|
|
baseUnit: product.base_unit,
|
|
status: product.status,
|
|
groupCode: product.group_code ?? null,
|
|
identifiers: (product.identifiers ?? []).map(({ kind, value }) => ({ kind, value })),
|
|
revision: String(product.revision),
|
|
})
|
|
|
|
const importView = (row: CatalogImport): ImportView => ({
|
|
importId: row.id,
|
|
sourceName: row.source_name,
|
|
format: row.format,
|
|
status: row.status,
|
|
revision: String(row.revision),
|
|
created: Number(row.created),
|
|
updated: Number(row.updated),
|
|
unchanged: Number(row.unchanged),
|
|
refused: Number(row.refused),
|
|
})
|
|
|
|
const outcomeView = (outcome: ImportOutcome): ImportOutcomeView => ({
|
|
import: importView(outcome.import),
|
|
rows: outcome.rows.map((row) => ({
|
|
sourceRow: row.source_row,
|
|
outcome: row.outcome,
|
|
productId: row.product_id ?? null,
|
|
reason: row.reason ?? null,
|
|
})),
|
|
})
|
|
|
|
/** What a lookup asks the service: a code prefix or an identifier. A name goes to the ranked search. */
|
|
function searchQuery(search: { by: Exclude<ProductSearchField, "name">; q: string }) {
|
|
switch (search.by) {
|
|
case "code":
|
|
return { code_prefix: search.q }
|
|
case "gtin":
|
|
case "erp":
|
|
return { identifier_kind: search.by, identifier_value: search.q }
|
|
}
|
|
}
|
|
|
|
/** 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. */
|
|
export function sdkCatalogService(binding: { client: Client; orgSlug: string }): CatalogService {
|
|
const { client } = binding
|
|
const org = { org_slug: binding.orgSlug }
|
|
return {
|
|
async listProducts({ search, after, limit }) {
|
|
const page = await answer(
|
|
catalogFindProducts({
|
|
client,
|
|
path: org,
|
|
query: {
|
|
...(search ? searchQuery(search) : {}),
|
|
...(after === undefined ? {} : { after }),
|
|
limit: BigInt(limit),
|
|
},
|
|
}),
|
|
)
|
|
return { rows: page.data.map(productView), ...(page.next_after ? { after: page.next_after } : {}) }
|
|
},
|
|
async searchProducts({ q, after, limit }) {
|
|
const page = await answer(
|
|
catalogSearchProducts({
|
|
client,
|
|
path: org,
|
|
query: { q, ...(after === undefined ? {} : { after }), limit: BigInt(limit) },
|
|
}),
|
|
)
|
|
return {
|
|
rows: page.data.map((hit) => productView(hit.product)),
|
|
...(page.next_after ? { after: page.next_after } : {}),
|
|
}
|
|
},
|
|
async getProduct(productId) {
|
|
return productView(await answer(catalogGetProduct({ client, path: { ...org, product_id: productId } })))
|
|
},
|
|
async listImports({ after, limit }) {
|
|
const page = await answer(
|
|
catalogFindImports({
|
|
client,
|
|
path: org,
|
|
query: { ...(after === undefined ? {} : { after }), limit: BigInt(limit) },
|
|
}),
|
|
)
|
|
return { rows: page.data.map(importView), ...(page.next_after ? { after: page.next_after } : {}) }
|
|
},
|
|
async applyImport(input) {
|
|
const outcome = await answer(
|
|
catalogImportProducts({
|
|
client,
|
|
path: org,
|
|
body: {
|
|
file_hash: input.fileHash,
|
|
source_name: input.sourceName,
|
|
format: input.format,
|
|
chunk: input.chunk,
|
|
final_chunk: input.finalChunk,
|
|
...(input.revision === undefined ? {} : { revision: BigInt(input.revision) }),
|
|
rows: input.rows.map((row) => ({
|
|
source_row: row.sourceRow,
|
|
...(row.revision === undefined ? {} : { revision: BigInt(row.revision) }),
|
|
product: {
|
|
code: row.product.code,
|
|
name: row.product.name,
|
|
base_unit: row.product.baseUnit,
|
|
...(row.product.description === undefined ? {} : { description: row.product.description }),
|
|
},
|
|
})),
|
|
idempotency_key: input.key,
|
|
},
|
|
}),
|
|
)
|
|
return outcomeView(outcome)
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 catalogServiceOf(container: Container, kind: "effect"): CatalogService
|
|
export function catalogServiceOf(container: Container, kind: ProcedureKind): CatalogReads
|
|
export function catalogServiceOf(container: Container, kind: ProcedureKind): CatalogReads {
|
|
if (container.has(CATALOG_SERVICE)) return container.resolve(CATALOG_SERVICE) as CatalogReads
|
|
let service: CatalogService
|
|
try {
|
|
const binding = requestBusinessBinding(container)
|
|
service = sdkCatalogService({ client: binding.client(createClient), orgSlug: binding.orgSlug })
|
|
} catch (error) {
|
|
throw businessAppError(error)
|
|
}
|
|
return kind === "effect" ? service : catalogReadsOf(service)
|
|
}
|