Validador de campañas

Overview

services/api/src/modules/campaigns/campaigns.validators.js define la validación local de payloads de campaña. Su objetivo es revisar los datos antes de que otras capas los usen, y devolver un resultado uniforme con valid y errors .

El archivo soporta validación completa y validación parcial. En modo parcial omite los campos obligatorios, pero sigue revisando todo campo que sí llegue en el payload. Ese comportamiento lo convierte en una barrera de entrada para creación y edición de campañas fileciteturn0file10turn0file11.

[!NOTE] Este módulo no lanza ApiError. Devuelve un objeto estructurado para que la capa llamadora decida cómo responder.

How it works

flowchart TB
  Input[Payload de campaña] --> Validator[validateCampaignPayload]
  Validator --> Required[Campos obligatorios]
  Validator --> Status[VALID_STATUSES]
  Validator --> Objective[VALID_OBJECTIVES]
  Validator --> Budget[Budget]
  Validator --> Dates[Fechas]
  Required --> Result[valid y errors]
  Status --> Result
  Objective --> Result
  Budget --> Result
  Dates --> Result

La función empieza con un arreglo vacío de errores. Si partial es false, exige name y listingId. Después valida cada campo presente: name, status, objective, budget.total, budget.spent, startDate y endDate. Al final devuelve { valid: errors.length === 0, errors } fileciteturn0file10turn0file11.

El flujo es acumulativo. La función no interrumpe en el primer fallo, sino que sigue revisando el resto de reglas. Eso permite devolver varios errores en una sola respuesta .

Key components

{
    "title": "validateCampaignPayload",
    "content": "Funci\u00f3n p\u00fablica exportada que valida el payload de una campa\u00f1a y devuelve un resultado con `valid` y `errors`. Admite validaci\u00f3n completa o parcial.",
    "type": "function",
    "filePath": "services/api/src/modules/campaigns/campaigns.validators.js",
    "badges": [
        "public"
    ]
}
  • VALID_STATUSES [badge:read-only] — Limita data.status a draft, active, paused, completed o cancelled fileciteturn0file10turn0file11.
  • VALID_OBJECTIVES [badge:read-only] — Limita data.objective a leads, visibility, calls o messages fileciteturn0file10turn0file11.
  • validateCampaignPayload [badge:public] — Revisa el payload y devuelve { valid, errors } sin lanzar excepciones fileciteturn0file10turn0file11.

Data & fields

[
    {
        "name": "data",
        "type": "object",
        "required": true,
        "description": "Payload de campaña que se quiere validar.",
        "children": [
            {
                "name": "name",
                "type": "string",
                "description": "Nombre de la campaña. En modo completo es obligatorio. Si llega, debe ser un string no vacío."
            },
            {
                "name": "listingId",
                "type": "any",
                "description": "Identificador del anuncio asociado. En modo completo es obligatorio por presencia."
            },
            {
                "name": "status",
                "type": "string",
                "description": "Estado de la campaña. Debe pertenecer a `VALID_STATUSES` cuando se envía."
            },
            {
                "name": "objective",
                "type": "string",
                "description": "Objetivo de la campaña. Debe pertenecer a `VALID_OBJECTIVES` cuando se envía."
            },
            {
                "name": "budget",
                "type": "object",
                "description": "Objeto opcional de presupuesto.",
                "children": [
                    {
                        "name": "total",
                        "type": "number",
                        "description": "Monto total del presupuesto. Debe ser un número mayor o igual a 0."
                    },
                    {
                        "name": "spent",
                        "type": "number",
                        "description": "Monto gastado. Debe ser un número mayor o igual a 0."
                    }
                ]
            },
            {
                "name": "startDate",
                "type": "date-compatible value",
                "description": "Fecha de inicio. Debe convertirse en una fecha válida si se envía."
            },
            {
                "name": "endDate",
                "type": "date-compatible value",
                "description": "Fecha de fin. Debe convertirse en una fecha válida si se envía."
            }
        ]
    },
    {
        "name": "options",
        "type": "object",
        "description": "Configuración opcional del validador.",
        "children": [
            {
                "name": "partial",
                "type": "boolean",
                "description": "Omite la exigencia de `name` y `listingId` cuando es `true`. Por defecto es `false`."
            }
        ]
    },
    {
        "name": "result",
        "type": "object",
        "required": true,
        "description": "Objeto devuelto por la función.",
        "children": [
            {
                "name": "valid",
                "type": "boolean",
                "required": true,
                "description": "Indica si el payload cumple todas las reglas."
            },
            {
                "name": "errors",
                "type": "string[]",
                "required": true,
                "description": "Lista de mensajes de validación acumulados."
            }
        ]
    }
]

Behaviors

  • validateCampaignPayload — Construye un arreglo de errores y devuelve el estado final con valid y errors .
  • VALID_STATUSES — Define el conjunto cerrado de estados aceptados para campañas fileciteturn0file10turn0file11.
  • VALID_OBJECTIVES — Define el conjunto cerrado de objetivos aceptados para campañas fileciteturn0file10turn0file11.
  • partial = false — Activa la validación completa y exige name y listingId fileciteturn0file10turn0file11.
  • partial = true — Omite la exigencia de campos obligatorios, pero conserva la validación del resto de propiedades presentes fileciteturn0file10turn0file11.
  • budget.total y budget.spent — Solo aceptan números mayores o iguales a cero fileciteturn0file10turn0file11.
  • startDate y endDate — Se validan por separado y cada una debe producir una fecha válida fileciteturn0file10turn0file11.
1. Recibir payload | La función recibe `data` y, opcionalmente, `{ partial }`.
2. Verificar obligatorios | Si `partial` es `false`, exige `name` y `listingId`.
3. Validar campos presentes | Revisa `name`, `status`, `objective`, `budget`, `startDate` y `endDate`.
4. Acumular errores | Cada incumplimiento añade un mensaje al arreglo `errors`.
5. Devolver resultado | Retorna `{ valid: errors.length === 0, errors }`.

Gotchas & edge cases

[!WARNING] validateCampaignPayload desestructura data.budget cuando data.budget !== undefined. Si budget llega como null, la función falla antes de devolver { valid, errors }.

[!IMPORTANT] name se valida dos veces cuando viene en el payload. Primero como obligatorio en modo completo, y después como string no vacío. listingId solo se exige por presencia, no por formato, dentro de este archivo. fileciteturn0file10turn0file11

[!NOTE] budget solo valida total y spent. Cualquier otra propiedad interna del objeto se ignora en esta capa fileciteturn0file10turn0file11

[!NOTE] startDate y endDate se evalúan de forma independiente. Cada campo puede fallar sin afectar al otro fileciteturn0file10turn0file11

Este validador se conecta con el módulo de campañas del backend. services/api/src/modules/campaigns/campaigns.routes.js lo importa para crear y actualizar campañas, y services/api/src/models/Campaign.js define campos que coinciden con estas reglas, como status, objective, budget, startDate y endDate .

La relación funcional es directa: la ruta prepara el payload, llama a validateCampaignPayload, y solo sigue con la operación si no hay errores. Eso convierte a este archivo en una barrera previa a la persistencia de campañas fileciteturn0file10turn0file11.