listing-public-assembly.js
Overview
Este módulo centraliza el ensamblaje público canónico de Listings ya obtenidos desde Mongo, con createdBy ya poblado por el caller. Extrae un pipeline compartido para que GET /api/listings, GET /api/listings/:id y el consumidor de Match usen exactamente la misma transformación pública. El archivo no decide qué Listings cargar, ni en qué orden, ni con qué filtro o paginación; solo transforma resultados ya recuperados.
[!IMPORTANT] Este módulo solo transforma Listings ya obtenidas. La query, el orden y la paginación siguen siendo responsabilidad del caller.
[!NOTE]
attachRelatedOfferSummaries,ListingLike.findysanitizeListingForPublicViewse usan como operaciones de solo lectura aquí. El módulo no muta el listing recibido.
How it works
flowchart TD
Caller[Caller]
Assemble[assemblePublicListingsForViewer]
Attach[attachRelatedOfferSummaries]
Sanitize[sanitizeListingForPublicView]
Output[Public listings]
LikeMap[buildLikedByCurrentUserMap]
ListingLikeFind[ListingLike find]
MapOut[Map listingId true]
Caller -->|listings| Assemble
Assemble -->|empty input| Output
Assemble -->|enrich| Attach
Attach -->|sanitize| Sanitize
Sanitize --> Output
Caller -->|listingIds and viewerUserId| LikeMap
LikeMap -->|single batch query| ListingLikeFind
ListingLikeFind --> MapOut
El flujo principal toma un arreglo de Listings ya cargados, llama primero a attachRelatedOfferSummaries, y después aplica sanitizeListingForPublicView a cada elemento. El segundo flujo es independiente y construye un Map con los likes del usuario actual usando una sola consulta batched. Ambos caminos evitan lógica de escritura y conservan el comportamiento de solo lectura.
assemblePublicListingsForViewernormaliza la entrada conArray.isArray.- Si el arreglo queda vacío, retorna
[]inmediatamente. - La enriquecimiento de Offer relacionado ocurre antes del saneamiento público.
- El saneamiento conserva el orden original del arreglo.
buildLikedByCurrentUserMapretorna unMapvacío si no hayviewerUserIdo si no hay IDs.- La consulta de likes usa
ListingLike.findcon$in,select("listingId")ylean().
Key components
{
"title": "assemblePublicListingsForViewer",
"content": "Aplica el pipeline p\u00fablico compartido sobre Listings ya obtenidas. Primero enriquece con attachRelatedOfferSummaries y luego sanea cada elemento con sanitizeListingForPublicView. Conserva el orden de entrada.",
"type": "function",
"filePath": "services/api/src/modules/listings/listing-public-assembly.js",
"badges": [
"public"
]
}
{
"title": "buildLikedByCurrentUserMap",
"content": "Construye un Map<string, boolean> con los Listings que el usuario ya dio like. Usa una sola consulta batched sobre ListingLike y no consulta Mongo si faltan el viewer o los IDs.",
"type": "function",
"filePath": "services/api/src/modules/listings/listing-public-assembly.js",
"badges": [
"public"
]
}
Este módulo depende de tres piezas visibles en el archivo:
ListingLikedesdeservices/api/src/models/ListingLike.js.attachRelatedOfferSummariesdesdelisting-referral.js.sanitizeListingForPublicViewdesdelisting-public-view.js.
Data & fields
Parámetros de assemblePublicListingsForViewer
[
{
"name": "listings",
"type": "Array<object>",
"required": true,
"description": "Listings lean ya obtenidos por el caller. El helper conserva el orden original."
},
{
"name": "options",
"type": "object",
"description": "Opciones para el ensamblaje público.",
"children": [
{
"name": "viewerUserId",
"type": "string|null",
"description": "Identidad opcional del usuario visor. Se pasa a los helpers internos."
}
]
}
]
Parámetros de buildLikedByCurrentUserMap
[
{
"name": "listingIds",
"type": "Array<string|object>",
"required": true,
"description": "IDs de Listings o valores convertibles a string para la consulta batched."
},
{
"name": "viewerUserId",
"type": "string|null",
"description": "Identidad del usuario visor. Si falta, no se consulta Mongo."
}
]
Salidas
assemblePublicListingsForViewer[badge:public] devuelvePromise<Array<object>>.buildLikedByCurrentUserMap[badge:public] devuelvePromise<Map<string, boolean>>.- El orden de salida de
assemblePublicListingsForViewercoincide con el arreglo de entrada. - Las claves del
Mapde likes se guardan comoString(like.listingId).
Behaviors
assemblePublicListingsForViewer[badge:public] — normaliza la entrada, aplicaattachRelatedOfferSummaries, y luego aplicasanitizeListingForPublicViewa cada listing. Devuelve el arreglo público final.buildLikedByCurrentUserMap[badge:public] — consultaListingLikeen un solo batch para saber qué listings ya fueron liked porviewerUserId. Devuelve unMapconlistingId -> true.assemblePublicListingsForViewer— no añadelikedByCurrentUser. Ese campo se maneja por separado en superficies tipo lista.buildLikedByCurrentUserMap— evita toda consulta si faltaviewerUserIdo si no hay IDs.buildLikedByCurrentUserMap— usaselect("listingId")ylean(), y solo recupera el dato mínimo necesario.
Workflows
Este archivo no define un flujo de usuario completo ni un endpoint propio. Su trabajo es componer dos rutas de datos: el ensamblaje público de Listings y el mapa de likes del usuario actual.
- Ensamblaje público: Listings ya cargados →
attachRelatedOfferSummaries→sanitizeListingForPublicView→ respuesta pública. - Mapa de likes:
listingIds+viewerUserId→ consulta única enListingLike→Map<string, boolean>.
Gotchas & edge cases
[!WARNING]
buildLikedByCurrentUserMapno hace consultas si no recibeviewerUserIdo si el arreglo de IDs está vacío. En ese caso retorna unMapvacío.
[!NOTE]
attachRelatedOfferSummarieses un no-op seguro para Listings sinrequirement.referral. El propio comentario del archivo lo describe como barato y sin consulta para esos casos.
[!IMPORTANT]
likedByCurrentUserno forma parte de la forma canónica de un Listing. El archivo indica que ese campo se compone aparte cuando la superficie lo necesita.
[!CAUTION] El módulo asume que el caller ya decidió qué Listings hidratar y con qué orden. No corrige ni reconstruye esa decisión.
Related
services/api/src/models/ListingLike.js— modelo usado para recuperar likes del usuario con una consulta batched.services/api/src/modules/listings/listing-referral.js— aportaattachRelatedOfferSummaries.services/api/src/modules/listings/listing-public-view.js— aportasanitizeListingForPublicView.services/api/src/modules/listings/listings.routes.js— este ensamblaje se extrajo para compartirlo con las rutas públicas de Listings, según el comentario del módulo.services/api/test/listing-public-assembly.test.js— verifica en aislamiento que el helper preserva el orden y evita N+1 en la consulta de likes.
Este archivo existe para evitar divergencias entre superficies públicas que muestran la misma Listing. Su función es mantener una sola forma canónica de salida pública, mientras cada caller conserva su propia query de Mongo, su filtro y su paginación. Esa separación aparece explícitamente en los comentarios del archivo y en las notas de integración del repositorio.