pending-requirement-recovery.service.js
Overview
Este módulo implementa la recuperación interna de eliminaciones de Requirements que ya estaban solicitadas. Reanuda el protocolo de borrado sobre documentos con deletion.status = "pending", incluso cuando la cuenta dueña ya no está activa. También reutiliza el flujo de deleteRequirement en modo resumeOnly, con la misma barrera A3, el archivado condicionado de Conversation y el borrado físico condicional de medios. fileciteturn0file8turn0file0
El archivo no expone endpoints HTTP ni usa autenticación HTTP. En su lugar, exige un contexto interno marcado con un Symbol privado del módulo. Ese contexto limita el uso accidental desde otros módulos, pero no actúa como una frontera criptográfica. fileciteturn0file8turn0file0
[!NOTE] La recuperación solo actúa si el
Requirementya está pendiente. Si el documento no cumple las precondiciones, el servicio devuelve un estado de salida sin escribir nada. fileciteturn0file7turn0file8
How it works
El flujo principal empieza con una validación del contexto interno. Después relee el estado actual del Listing, calcula si la recuperación es elegible y, solo entonces, llama a deleteRequirement con resumeOnly: true. Si el borrado termina con éxito, el servicio clasifica si la operación se completó aquí o en otro lugar. Si la operación falla de forma operativa, devuelve pending_again en lugar de lanzar la excepción. fileciteturn0file7turn0file8
flowchart TD
Start[resumePendingRequirementDeletion]
Ctx[assertContext]
Read[inspectPendingRequirement]
Check[eligibility]
StopA[Return non eligible]
Step1[recovery afterPreconditions]
Delete[deleteRequirement resumeOnly]
Map[Map delete result]
Recheck[Recheck existence and refs]
StopB[Return completed or not pending]
Retry[Return pending again]
Start --> Ctx --> Read --> Check
Check -->|not eligible| StopA
Check -->|eligible| Step1 --> Delete --> Map
Map -->|deleted| StopA
Map -->|not found| Recheck --> StopB
Map -->|other| Retry
La lectura de estado es siempre anterior a la escritura. El servicio reevalúa las precondiciones justo antes de actuar, no solo a partir de una clasificación previa. Eso evita usar un inventario antiguo como si fuera definitivo. fileciteturn0file7turn0file8
Key components
Estos son los exports públicos del módulo y su responsabilidad inmediata. El archivo también define helpers internos para validación, identificación y manejo de errores. fileciteturn0file0turn0file7
{
"title": "createRecoveryContext",
"content": "Crea el contexto interno de recuperaci\u00f3n. Valida operatorRef, reason y runId, y devuelve un objeto congelado con una marca privada.",
"type": "function",
"filePath": "services/api/src/modules/reconciliation/pending-requirement-recovery.service.js",
"badges": [
"public"
]
}
{
"title": "inspectPendingRequirement",
"content": "Lee el estado observado de un Requirement pendiente. Devuelve una vista de solo lectura con estado pendiente, due\u00f1o, medios y referencias a conversaciones.",
"type": "function",
"filePath": "services/api/src/modules/reconciliation/pending-requirement-recovery.service.js",
"badges": [
"public"
]
}
{
"title": "eligibility",
"content": "Convierte la observaci\u00f3n en una decisi\u00f3n de elegibilidad. Distingue entre not_found, not_pending, owner_unknown, erasing_present y eligible.",
"type": "function",
"filePath": "services/api/src/modules/reconciliation/pending-requirement-recovery.service.js",
"badges": [
"public"
]
}
{
"title": "resumePendingRequirementDeletion",
"content": "Reanuda la eliminaci\u00f3n de un Requirement pendiente. Relee las precondiciones, invoca deleteRequirement en modo resumeOnly y traduce el resultado a una salida de recuperaci\u00f3n.",
"type": "function",
"filePath": "services/api/src/modules/reconciliation/pending-requirement-recovery.service.js",
"badges": [
"public"
]
}
Estas funciones forman la superficie pública del archivo. El resto del módulo son utilidades internas, como assertContext, step, isObjectId y errorCode. fileciteturn0file0turn0file7
Data & fields
El módulo trabaja con un conjunto pequeño de constantes y estructuras de retorno. Las validaciones son estrictas, y los valores inválidos terminan en errores o en salidas no elegibles. fileciteturn0file0turn0file7
createRecoveryContext input
[
{
"name": "operatorRef",
"type": "string",
"required": true,
"description": "Referencia interna del operador. Debe cumplir SAFE_REF."
},
{
"name": "reason",
"type": "string",
"required": true,
"description": "Motivo interno de recuperación. Solo se acepta D1_RECOVERY."
},
{
"name": "runId",
"type": "string",
"required": true,
"description": "Identificador de ejecución. Debe cumplir SAFE_REF."
}
]
resumePendingRequirementDeletion input
[
{
"name": "requirementId",
"type": "string",
"required": true,
"description": "Id de MongoDB del Requirement a recuperar."
},
{
"name": "context",
"type": "object",
"required": true,
"description": "Contexto interno marcado por createRecoveryContext.",
"children": [
{
"name": "operatorRef",
"type": "string",
"required": true,
"description": "Referencia interna del operador."
},
{
"name": "reason",
"type": "string",
"required": true,
"description": "Motivo interno. Solo D1_RECOVERY es válido."
},
{
"name": "runId",
"type": "string",
"required": true,
"description": "Id de ejecución interna."
}
]
},
{
"name": "hooks",
"type": "object",
"description": "Hooks opcionales para pruebas. El servicio invoca recovery:afterPreconditions si existe."
}
]
Constantes internas
RECOVERY_REASONS[badge:read-only]string[]— Solo contiene"D1_RECOVERY".CONTEXT_BRAND[badge:read-only]Symbol— Marca privada del contexto interno.SAFE_REF[badge:read-only]RegExp— Acepta solo letras, números, punto, guion bajo y guion medio. La longitud va de 1 a 64.
exists[badge:required]boolean— Indica si elListingexiste y coincide conintent: "requirement".reason[badge:optional]string— Solo aparece cuando elrequirementIdes inválido. Devuelveinvalid_id.pending[badge:optional]boolean—truesideletion.status === "pending".attempts[badge:optional]number | null— Número de intentos de borrado observados.requestedAt[badge:optional]string | null— Fecha ISO de la solicitud de borrado.ownerId[badge:optional]string | null— Valor persistido encreatedBy, solo si es unObjectIdválido.ownerAccountStatus[badge:optional]string | null— Estado de cuenta leído desdeUser.accountStatus.mediaFiles[badge:required]number— Cantidad de ids de archivos derivados porlistingImageKitFileIds.claims[badge:required]object— Conteo por estado deMediaFileClaim. Las claves son estados y los valores son números.conversationRefs[badge:required]number— Conteo deConversationconactiveRequirementIdigual alListing.
outcome[badge:required]string— Puede sercompleted,completed_elsewhere,not_found,not_pending,owner_unknown,erasing_presentopending_again.reason[badge:optional]string— Explica salidas comoerror,pendingo causas devueltas pordeleteRequirement.code[badge:optional]string— Código normalizado conerrorCode, truncado a 64 caracteres.counts[badge:optional]object— Solo aparece cuando el borrado se completa. Incluye contadores de conversaciones y medios.conversationRefsBefore[badge:required]number— Referencias de conversación observadas antes de actuar.mediaDeleted[badge:required]number— Medios borrados pordeleteRequirement.mediaPreserved[badge:required]number— Medios preservados pordeleteRequirement.
Behaviors
El archivo combina validación, lectura de estado y coordinación de recuperación. Cada función tiene una responsabilidad acotada y predecible. fileciteturn0file0turn0file7
createRecoveryContext— Valida los campos de entrada, marca el objeto conCONTEXT_BRANDy lo congela.assertContext— Rechaza cualquier contexto que no tenga la marca privada interna.step— Ejecuta un hook por nombre solo si existe y es una función. El comentario del archivo lo marca como utilitario para pruebas.isObjectId— Acepta solo valores que sonObjectIdválidos de 24 hex caracteres.errorCode— NormalizacodeName,codeonamey lo recorta a 64 caracteres.inspectPendingRequirement— LeeListing,MediaFileClaim,UseryConversationpara construir una observación sin datos personales.eligibility— Convierte la observación en un estado de decisión. Devuelve una etiqueta de elegibilidad única.resumePendingRequirementDeletion— Relee el estado, valida elegibilidad, llama adeleteRequirementconresumeOnly: truey clasifica el resultado. fileciteturn0file7turn0file8
[!IMPORTANT] El dueño no llega desde el llamador. El servicio toma
createdBydel documento persistido y lo usa comoownerId. Si ese valor no es válido, la recuperación devuelveowner_unknowny no escribe nada. fileciteturn0file7turn0file8
Workflows
Este flujo resume la secuencia operacional del módulo. El servicio relee el estado antes de actuar y no confía en una clasificación previa. fileciteturn0file7turn0file8
1. Crear contexto | `createRecoveryContext` valida `operatorRef`, `reason` y `runId`, y devuelve un contexto congelado.
2. Verificar contexto | `resumePendingRequirementDeletion` llama a `assertContext` antes de continuar.
3. Releer estado | `inspectPendingRequirement` consulta `Listing`, `MediaFileClaim`, `User` y `Conversation`.
4. Calcular elegibilidad | `eligibility` descarta casos no encontrados, no pendientes, sin dueño o con medios en `erasing`.
5. Reanudar borrado | `deleteRequirement` se ejecuta con `resumeOnly: true`.
6. Clasificar salida | El resultado se traduce a `completed`, `completed_elsewhere`, `not_pending`, `owner_unknown`, `erasing_present` o `pending_again`.
El archivo también contempla una doble lectura del final. Si deleteRequirement responde not_found, el servicio vuelve a revisar si el Listing y sus referencias de conversación siguen existiendo. Esa segunda comprobación distingue entre completed_elsewhere y not_pending.
Gotchas & edge cases
El comportamiento del módulo es estricto en varios puntos. Las salidas operativas están diseñadas para no lanzar excepciones, salvo la validación del contexto y de los parámetros internos. fileciteturn0file0turn0file7
[!WARNING]
resumePendingRequirementDeletionexige un contexto creado porcreateRecoveryContext. Un objeto plano o sin la marca privada dispara un error inmediato.
- Si
deletion.statusno espending, la función devuelvenot_pendingsin escribir nada. - Si
createdByno es unObjectIdválido, la salida esowner_unknown. - Si existe algún
MediaFileClaimen estadoerasing, la salida eserasing_present. - Si
deleteRequirementlanza un error, el módulo devuelvepending_againcon un código normalizado. - Si el documento desaparece y ya no hay referencias de
Conversation, la salida puede sercompleted_elsewhere. - El módulo no define endpoints HTTP ni usa autenticación HTTP.
[!NOTE] La marca privada del contexto solo evita usos accidentales dentro del proceso. El propio archivo aclara que no es una frontera criptográfica.
Related
Este módulo se apoya en varias piezas del backend de services/api. Cada una aporta una parte del estado o del flujo de eliminación. fileciteturn0file7turn0file8
services/api/src/models/Listing.js— Fuente principal delRequirementy de su estado de borrado.services/api/src/models/Conversation.js— Conteo de referencias activas conactiveRequirementId.services/api/src/models/User.js— Lectura delaccountStatusdel dueño persistido.services/api/src/models/MediaFileClaim.js— Estado de los archivos ligados alListing.services/api/src/modules/listings/requirement-deletion.service.js— Protocolo que este módulo reanuda conresumeOnly: true.services/api/src/modules/media/media-claims.service.js— ProveelistingImageKitFileIds, que deriva los ids de medios observados.