@horizon-integrations/si9-crm-client
v2.0.0
Published
Client HTTP tipado pra consumir a API do horizon-integrations-hub (rotas /api/providers/si9/v1/*). Instalado nos sites das imobiliárias pra puxar imóveis/leads via hub, preservando Anti-Corruption Layer.
Maintainers
Readme
@horizon-integrations/si9-crm-client
SDK HTTP tipado pra consumir a API do horizon-integrations-hub (rotas /api/providers/si9/v1/*). Instalado nos sites das imobiliárias pra puxar imóveis/leads do SI9 via hub — preservando Anti-Corruption Layer (DDD).
Arquitetura
┌────────────────────┐ ┌──────────────────┐ ┌───────────────┐
│ Site Horizon │ HTTP │ hub │ HTTP │ API SI9 │
│ @horizon- │───────▶│ @horizon- │───────▶│ externa │
│ integrations/ │ │ integrations/ │ │ │
│ si9-crm-client │ │ si9-crm │ │ │
└────────────────────┘ └──────────────────┘ └───────────────┘Site consome só o SDK (este pacote, leve). Hub consome o runtime (@horizon-integrations/si9-crm). SDK e runtime são pacotes separados — sem bloat no bundle do site.
Instalação
pnpm add @horizon-integrations/si9-crm-clientDeps peer: @horizon-js/integrations-core (via transitiva — não precisa instalar explicitamente).
Uso básico
import { Si9Client } from "@horizon-integrations/si9-crm-client"
const si9 = new Si9Client({
baseUrl: process.env.HUB_URL!, // https://hub.horizonintegrations.com
authToken: process.env.HUB_TOKEN!, // x-api-key do hub
tenantCredentials: {
username: process.env.SI9_USERNAME!,
password: process.env.SI9_PASSWORD!,
authorization: process.env.SI9_AUTH!,
},
})
// Streaming de imóveis — yield cada record (internamente, 1 fetchAll do hub)
for await (const property of si9.properties.streamAll()) {
await db.properties.upsert(property)
}
// Listing leve — só refs, pra delta
const { index } = await si9.properties.getListing()
// Fetch pontual por ref
const property = await si9.properties.get("438")
if (!property) console.log("Imóvel não existe")
// Validar credenciais (hub + SI9)
const check = await si9.check()
if (!check.ok) throw new Error(check.message)Delta via sync_hash
Records vêm com sync_hash (SHA-256 truncado). Pra detectar mudanças:
// 1. Pega hashes existentes no banco local
const existing = await db.query<{ reference: string; sync_hash: string }>(
"SELECT reference as ref, sync_hash FROM properties WHERE source_key = 'si9-api-imoveis'"
)
// 2. Pega records atuais do SI9 via SDK
const { properties } = await si9.properties.fetchAll()
// 3. Compara hashes — só escreve quem mudou
for (const p of properties) {
const local = existing.find((e) => e.reference === p.reference)
if (!local) {
await db.insert(p) // novo
} else if (local.sync_hash !== p.sync_hash) {
await db.update(p.reference, p) // mudou
}
// senão: igual → skip
}
// 4. Records que sumiram no SI9 → delete local
const currentRefs = new Set(properties.map((p) => p.reference))
for (const e of existing) {
if (!currentRefs.has(e.reference)) {
await db.delete(e.reference)
}
}Alternativamente, use compareListings de @horizon-js/integrations-core (já resolve esse padrão).
Tipagem específica
Pra acessar o shape completo HorizonPropertySchemaBySi9Type:
import { Si9Client } from "@horizon-integrations/si9-crm-client"
import type { HorizonPropertySchemaBySi9Type } from "@horizon-integrations/si9-crm"
const si9 = new Si9Client({ ... })
const { properties } = await si9.properties.fetchAll<HorizonPropertySchemaBySi9Type>()
// ^^^^^^^^^^ agora é HorizonPropertySchemaBySi9Type[] com todos os campos tipadosEnviar leads
const result = await si9.leads.create({
name: "João Silva",
email: "[email protected]",
phone: "46999990001",
message: "Quero mais info deste imóvel",
propertyId: 438,
propertyOperation: "venda",
classification: "high",
})
if (result.success) {
console.log(`Lead criado: ${result.leadId}`)
} else {
console.error(result.error)
}⚠️ Rota /leads no hub ainda não implementada na v1 do hub. Preparada no SDK pra quando for criada.
Métodos do PropertiesResource
| Método | Retorna | Uso |
|---|---|---|
| fetchAll<T>() | {properties, errors, total} | Full fetch |
| streamAll<T>() | AsyncGenerator<T> | Streaming (sugar sobre fetchAll) |
| getListing() | {index, total} | Listing leve — só refs |
| get<T>(ref) | T \| null | Fetch por ref (404 retorna null) |
| check() | {ok, message?} | Valida credenciais |
Capabilities
O Si9Client.capabilities espelha o manifest.capabilities do runtime. Consumers podem inspecionar antes de executar operações:
if (!si9.capabilities.nativeTimestamp) {
console.log("SI9 não tem updated_at — usando sync_hash pra delta")
}
if (!si9.capabilities.webhooks) {
console.log("Sem webhooks — precisa de cron agendado pra sync")
}Design
- Zero deps de runtime do
@horizon-integrations/si9-crm— SDK é autônomo - Usa só
@horizon-js/integrations-corepros tipos comuns (Client,SourceCapabilities) - Bundle minúsculo — site não leva auth/schemas/converter do pacote runtime
- Versões independentes: SDK amarra versão da API do hub (
/v1), não da versão do runtime
Relacionado
@horizon-integrations/si9-crm— runtime (instalado no hub, não no site)@horizon-js/integrations-core— core genérico de integrações@horizon-js/property-domain-schema— schema canônicoHorizonProperty
License
MIT — Horizon Modules
