71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
// What the catalog module does, as pure TypeScript over the port in ports.ts.
|
|
//
|
|
// Products are the organization's: every project of the app sees the same ones (a multi-tenant app keeps
|
|
// all its tenants' products in its deployment's one organization; partitioning by app tenant is an open
|
|
// owner decision, B-wave contract §11.4).
|
|
|
|
import type { ProjectScope } from "@conusai-kit/core"
|
|
import {
|
|
type ImportOutcomeView,
|
|
type ImportRow,
|
|
type ImportView,
|
|
PAGE_LIMIT,
|
|
type ProductSearchField,
|
|
type ProductView,
|
|
} from "../contract/index.js"
|
|
import type { CatalogReads, CatalogService, Page } from "./ports.js"
|
|
|
|
/** The service's idempotency key of a request: one project's request id never answers another's. */
|
|
export const requestKey = (scope: Pick<ProjectScope, "projectId">, requestId: string): string =>
|
|
`app-project:${scope.projectId}:${requestId}`
|
|
|
|
const cursor = (after: string | undefined) => (after === undefined ? {} : { after })
|
|
|
|
export function listProducts(
|
|
reads: CatalogReads,
|
|
query: { after?: string; limit?: number },
|
|
): Promise<Page<ProductView>> {
|
|
return reads.listProducts({ ...cursor(query.after), limit: query.limit ?? PAGE_LIMIT })
|
|
}
|
|
|
|
export function searchProducts(
|
|
reads: CatalogReads,
|
|
query: { q: string; by?: ProductSearchField; after?: string; limit?: number },
|
|
): Promise<Page<ProductView>> {
|
|
const q = query.q.trim()
|
|
const by = query.by ?? "name"
|
|
const limit = query.limit ?? PAGE_LIMIT
|
|
if (by === "name") return reads.searchProducts({ q, ...cursor(query.after), limit })
|
|
return reads.listProducts({ search: { by, q }, ...cursor(query.after), limit })
|
|
}
|
|
|
|
export function listImports(reads: CatalogReads, query: { after?: string; limit?: number }): Promise<Page<ImportView>> {
|
|
return reads.listImports({ ...cursor(query.after), limit: query.limit ?? PAGE_LIMIT })
|
|
}
|
|
|
|
export function applyImport(
|
|
service: CatalogService,
|
|
scope: ProjectScope,
|
|
input: {
|
|
fileHash: string
|
|
sourceName: string
|
|
format: "csv" | "xlsx" | "json"
|
|
chunk: number
|
|
finalChunk: boolean
|
|
revision?: string
|
|
rows: readonly ImportRow[]
|
|
requestId: string
|
|
},
|
|
): Promise<ImportOutcomeView> {
|
|
return service.applyImport({
|
|
fileHash: input.fileHash,
|
|
sourceName: input.sourceName.trim(),
|
|
format: input.format,
|
|
chunk: input.chunk,
|
|
finalChunk: input.finalChunk,
|
|
...(input.revision === undefined ? {} : { revision: input.revision }),
|
|
rows: input.rows,
|
|
key: requestKey(scope, input.requestId),
|
|
})
|
|
}
|