00

Como encaja el adapter

GCP · rtp-transversal-dev · us-central1 IAM POST HTTPS Webhook Publica resultado Suscripcion push rtp-csb Orquestador Cloud Run Produce tareas Consume resultados Cloud Tasks HTTP POST · OIDC csb-delivery-adapter NestJS 11 · Puerto 8080 · Cloud Run CONTROLLERS DeliveryController POST /delivery/create-order GET · POST cancel WebhooksCtrl POST /webhooks/bringoz HealthController GET /health · GET /health/ready DELIVERY PROVIDERS BringozProvider DEFAULT API Key + Tenant ID NexusProvider ALTERNATIVO API Key + Client ID SERVICIOS DeliveryService WebhooksService PubSubService Common Layer Bringoz Platform Last-mile Delivery API (REST) POST /orders Crear orden GET /orders/:id Tracking POST /orders/:id/cancel Callbacks → webhooks X-API-Key + X-Tenant-ID via Secret Manager Nexus (alternativo) Last-mile Delivery API API Key + Client ID Secret Manager csb-dev-bringoz-* (6 secrets) Cloud Pub/Sub Topic: delivery.completed.v1 · CloudEvents v1.0 Nota · El adapter nunca se expone a internet · ordenes via Cloud Tasks (IAM) · estado via webhooks Bringoz · resultados via Pub/Sub · DELIVERY_PROVIDER: Bringoz (default) o Nexus
01

Conectar el CSB con el adapter

1
Registrar los endpoints en el seed del CSB
En rtp-csb (donde el CSB registre servicios externos), agrega las rutas del adapter con el URL del Cloud Run como destino de Cloud Tasks.
rtp-csb — seed / config de destinations
// URL de Cloud Run (una vez desplegado)
const DELIVERY_ADAPTER_URL = 'https://csb-delivery-adapter-XXXX-uc.a.run.app';

// Rutas que Cloud Tasks debe enviar al adapter
const destinations = {
  delivery_create_order: `${DELIVERY_ADAPTER_URL}/delivery/create-order`,
  delivery_track:        `${DELIVERY_ADAPTER_URL}/delivery/track`,
  delivery_cancel:       `${DELIVERY_ADAPTER_URL}/delivery/cancel`,
};
2
Crear la Cloud Tasks queue apuntando al adapter
La queue necesita la service account del adapter como oidcToken.serviceAccountEmail para que Cloud Tasks pueda llamar al Cloud Run con IAM.
gcloud — crear queue
gcloud tasks queues create csb-delivery-adapter-queue \
  --location=us-central1 \
  --max-concurrent-dispatches=10 \
  --max-attempts=5 \
  --min-backoff=10s \
  --max-backoff=300s \
  --project=rtp-transversal-dev
3
Formato del payload que el CSB envia al adapter
El CSB construye un DeliveryPayloadDto (envelope con metadata de tarea) y lo pone como body del HTTP POST que Cloud Tasks entrega al adapter.
POST /delivery/create-order — DeliveryPayloadDto
{
  "envelope": {
    "id":            "evt-delivery-001",      // UUID del evento original
    "entityId":      "ORD-MX-2024-001",
    "correlationId": "corr-abc123",
    "data": {
      "orderId":          "ORD-MX-2024-001",
      "customerName":     "Maria Lopez",
      "deliveryAddress":  "Av. Reforma 222, CDMX",
      "postalCode":       "06600",
      "phone":            "5512345678",
      "items": [
        { "sku": "FILT-001", "qty": 2, "description": "Filtro Bebbia" }
      ],
      "scheduledDate":   "2026-09-01",
      "timeWindow":      "09:00-13:00"
    }
  },
  "taskMetadata": {
    "queueName":  "csb-delivery-adapter-queue",
    "retryCount": 0
  }
}
4
Respuesta sincrona del adapter al CSB
El adapter responde inmediatamente con el resultado del provider. Cloud Tasks considera 2xx como exitoso.
Respuesta sincrona — 200 OK
{
  "status":   "ok",
  "provider": "bringoz",
  "result": {
    "orderId":     "ORD-MX-2024-001",
    "status":      "pending",           // accepted | rejected | pending | delivered | cancelled
    "providerRef": "BRZ-789456",        // ID interno de Bringoz
    "trackingUrl": "https://track.bringoz.com/BRZ-789456"
  }
}
5
Resultado asincrono via Pub/Sub (webhooks)
Cuando Bringoz envia un webhook con cambio de estado, el adapter publica el resultado en el topic configurado. El CSB lo consume desde ahi.
Pub/Sub — Mensaje publicado por el adapter
{
  "eventId":     "ORD-MX-2024-001",
  "result": {
    "orderId":     "ORD-MX-2024-001",
    "status":      "delivered",
    "providerRef": "ORD-MX-2024-001",
    "trackingUrl": "https://track.bringoz.com/BRZ-789456"
  },
  "publishedAt": "2026-09-01T14:30:00.000Z",
  "source":      "csb-delivery-adapter"
}
// attributes del mensaje Pub/Sub:
// eventId, provider, status, webhookEvent
6
Suscripcion del CSB al topic de resultados
El CSB necesita una suscripcion al topic delivery.completed.v1 para recibir actualizaciones de estado de las entregas.
gcloud — crear suscripcion en CSB
gcloud pubsub subscriptions create csb-delivery-results-sub \
  --topic=delivery.completed.v1 \
  --push-endpoint=https://<CSB_URL>/events/delivery-status \
  --ack-deadline=60 \
  --project=rtp-transversal-dev
02

Flujo de Webhooks (Bringoz → CSB)

A diferencia del ERP adapter (solo salida), el delivery adapter tiene un flujo bidireccional: recibe tareas del CSB y tambien recibe callbacks de Bringoz cuando el estado de una entrega cambia.

Bringoz
→
POST /webhooks/bringoz
WebhookEventDto
→
WebhooksService
→
PubSubService
delivery.completed.v1
→
CSB

Eventos de webhook soportados

eventType (Bringoz) Status mapeado Descripcion
order_accepted accepted Orden asignada a un repartidor
order_in_transit pending Repartidor en camino al destino
order_delivered delivered Entrega confirmada con evidencia
order_rejected rejected Orden rechazada por el proveedor
order_cancelled cancelled Orden cancelada
POST /webhooks/bringoz — WebhookEventDto (payload de Bringoz)
{
  "eventType":   "order_delivered",
  "orderId":     "ORD-MX-2024-001",
  "status":      "completed",
  "trackingUrl": "https://track.bringoz.com/BRZ-789456",
  "details": {
    "driverName":      "Carlos M.",
    "deliveredAt":     "2026-09-01T14:28:00.000Z",
    "signatureUrl":    "https://bringoz.com/sig/abc123.png",
    "photoUrl":        "https://bringoz.com/photo/abc123.jpg"
  },
  "timestamp":  "2026-09-01T14:30:00.000Z"
}
!
El endpoint /webhooks/bringoz debe ser accesible desde internet (Bringoz lo llama directamente). Configurar un Cloud Run con --allow-unauthenticated solo para la ruta de webhooks, o usar un API Gateway / Cloud Endpoints con validacion de firma de Bringoz.
03

Prerequisitos antes del primer deploy

Secrets en Secret Manager

Todos los secrets siguen el patron csb-{env}-{name}. Crealos en rtp-transversal-dev:

Secret Variable de entorno Descripcion
csb-dev-bringoz-host BRINGOZ_HOST URL base de la API Bringoz
csb-dev-bringoz-api-key BRINGOZ_API_KEY API Key para autenticacion
csb-dev-bringoz-tenant-id BRINGOZ_TENANT_ID Tenant ID del cliente en Bringoz
csb-dev-pubsub-result-topic PUBSUB_RESULT_TOPIC Topic para publicar resultados
csb-dev-delivery-provider DELIVERY_PROVIDER bringoz o nexus (default: bringoz)
!
Si se usa Nexus como provider alternativo, tambien se necesitan: csb-dev-nexus-host, csb-dev-nexus-api-key, csb-dev-nexus-client-id.
gcloud — crear secrets (shell loop)
# Crear cada secret (reemplaza VAL con el valor real)
for name in bringoz-host bringoz-api-key bringoz-tenant-id \
            pubsub-result-topic delivery-provider; do
  echo -n "VAL" | gcloud secrets create "csb-dev-$name" \
    --data-file=- \
    --project=rtp-transversal-dev
done

Permisos IAM minimos

Service Account Rol Para que
csb-delivery-adapter-sa roles/secretmanager.secretAccessor Leer secrets en runtime
csb-delivery-adapter-sa roles/pubsub.publisher Publicar resultados
csb-delivery-adapter-sa roles/logging.logWriter Escribir logs estructurados
csb-delivery-adapter-sa roles/monitoring.metricWriter Metricas de Cloud Monitoring
csb-cloudtasks-sa roles/run.invoker Cloud Tasks puede llamar al Cloud Run
04

Deploy manual a Cloud Run (primer deploy)

Para el primer deploy antes de tener el pipeline CI/CD conectado al trigger.

1
Build y push de la imagen
El Dockerfile usa multi-stage build (3 etapas: deps, builder, runner) con usuario no-root csb y healthcheck incluido.
shell
gcloud auth configure-docker us-central1-docker.pkg.dev

IMAGE=us-central1-docker.pkg.dev/rtp-transversal-dev/csb-adapters/csb-delivery-adapter:latest

docker build -t $IMAGE .
docker push $IMAGE
2
Deploy a Cloud Run con variables de entorno
Solo para el primer arranque manual. El servicio que gobierna Terraform se llama csb-delivery-adapter-<env>-api (p. ej. csb-delivery-adapter-dev-api): un deploy con otro nombre crea un servicio aparte que el pipeline no actualiza.
gcloud — deploy
gcloud run deploy csb-delivery-adapter \
  --image=$IMAGE \
  --region=us-central1 \
  --platform=managed \
  --no-allow-unauthenticated \
  --service-account=csb-delivery-adapter-sa@rtp-transversal-dev.iam.gserviceaccount.com \
  --set-env-vars="NODE_ENV=development,\
USE_SECRET_MANAGER=true,\
GOOGLE_CLOUD_PROJECT=rtp-transversal-dev,\
PUBSUB_RESULT_TOPIC=delivery.completed.v1,\
DELIVERY_PROVIDER=bringoz" \
  --port=8080 \
  --min-instances=0 \
  --max-instances=5 \
  --memory=512Mi \
  --project=rtp-transversal-dev
3
Verificar health check
shell — health check con identidad GCP
URL=$(gcloud run services describe csb-delivery-adapter \
  --region=us-central1 --format='value(status.url)')

TOKEN=$(gcloud auth print-identity-token)

curl -H "Authorization: Bearer $TOKEN" $URL/health | jq .

# Respuesta esperada:
# {
#   "status": "ok",
#   "timestamp": "2026-08-27T...",
#   "service": "csb-delivery-adapter",
#   "version": "1.0.0",
#   "provider": "bringoz"
# }
shell — readiness check
curl -H "Authorization: Bearer $TOKEN" $URL/health/ready | jq .

# Si Bringoz host configurado:
# { "status": "ok", "timestamp": "...", "provider": "bringoz" }

# Si no configurado:
# { "status": "not_configured", "timestamp": "...", "provider": "bringoz" }
05

Terraform — infraestructura listo

✓
El directorio infra/terraform/ esta creado con 4 modulos y 3 ambientes. El pipeline de Cloud Build ejecuta terraform init → plan → apply automaticamente.

Modulos

Modulo Recursos Descripcion
compute Cloud Run, Service Account, IAM Servicio principal con probes, VPC access, secret injection via value_source
networking VPC, Subnet, Router, NAT, VPC Connector Red privada con private_ip_google_access y egress NAT
secrets Secret Manager secrets + IAM Secrets csb-{env}-* con acceso para la SA de Cloud Run
monitoring Alert policies, Uptime checks, Notification channels Alertas: 5xx rate, p95 latency > 5s, instance count near max

Ambientes

Ambiente Directorio Descripcion
dev infra/terraform/environments/dev/ Desarrollo — rtp-transversal-dev
qa infra/terraform/environments/qa/ Quality Assurance
prd infra/terraform/environments/prd/ Produccion (requiere aprobacion humana)
infra/terraform/modules/compute/main.tf — fragmento clave
resource "google_cloud_run_v2_service" "api" {
  name     = "${local.prefix}-api"
  location = var.region

  template {
    service_account = google_service_account.run.email

    containers {
      image = "...csb-delivery-adapter:${var.image_tag}"

      // Secrets inyectados directamente como env vars:
      dynamic "env" {
        for_each = local.secret_env_map  // BRINGOZ_HOST, BRINGOZ_API_KEY, etc.
        content {
          name = env.key
          value_source {
            secret_key_ref {
              secret  = "${local.secret_prefix}-${env.value}"
              version = "latest"
            }
          }
        }
      }

      liveness_probe  { http_get { path = "/health" } }
      startup_probe   { http_get { path = "/health" } }
    }

    vpc_access {
      connector = var.vpc_connector_id
      egress    = "PRIVATE_RANGES_ONLY"
    }
  }
}
06

Pipeline CI/CD — cloudbuild.yaml listo

✓
cloudbuild.yaml esta en la raiz del repo con el pipeline completo de 6 pasos. Trigger: push a dev o main.
1
npm audit
Auditoria de seguridad con --audit-level=high.
2
npm ci
Instalacion de dependencias con gcr.io/cloud-builders/npm.
3
Lint + Test en paralelo
npm run lint y npm test -- --coverage corren en paralelo (waitFor: ['install']). Actualmente 27 tests pasando (23 unit + 4 e2e).
4
Docker build + push
Construye imagen csb-delivery-adapter con tags $SHORT_SHA y latest. Push a Artifact Registry (csb-adapters).
5
Terraform init → plan → apply
Ejecuta en infra/terraform/environments/${_ENV}, pasa -var=image_tag=$SHORT_SHA al plan.
6
Health check
Verifica GET /health del servicio desplegado usando gcloud run services describe para obtener la URL.
cloudbuild.yaml — substitutions
substitutions:
  _REGION:  us-central1
  _ENV:     dev
  _SERVICE: csb-delivery-adapter

options:
  logging:     CLOUD_LOGGING_ONLY
  machineType: E2_HIGHCPU_8

tags:
  - csb-delivery-adapter
  - ${_ENV}
07

Estructura del codigo fuente

src/ — arbol de modulos
src/
 ├── main.ts                              Bootstrap: ValidationPipe, CORS, GlobalExceptionFilter
 ├── app.module.ts                        Root: Health, Delivery, Webhooks, PubSub + middlewares
 │
 ├── config/
 │   └── app.config.ts                    loadDeliveryConfig() — env vars o Secret Manager
 │
 ├── delivery/
 │   ├── delivery.controller.ts           POST /create-order, GET /track/:id, POST /cancel/:id
 │   ├── delivery.service.ts              Provider pattern — selecciona Bringoz o Nexus
 │   ├── delivery.module.ts               DI: DELIVERY_CONFIG, providers, PubSubModule
 │   ├── dto/
 │   │   └── delivery-payload.dto.ts      DeliveryPayloadDto, DeliveryEnvelopeDto, TaskMetadataDto
 │   └── providers/
 │       ├── delivery-provider.interface.ts DeliveryProvider + DeliveryResult
 │       ├── bringoz.provider.ts          Bringoz (default) — TODO: implementar API real
 │       └── nexus.provider.ts            Nexus (alternativo) — TODO: implementar API real
 │
 ├── webhooks/
 │   ├── webhooks.controller.ts           POST /webhooks/bringoz
 │   ├── webhooks.service.ts              mapToDeliveryResult() → pubsub.publishResult()
 │   ├── webhooks.module.ts
 │   └── dto/
 │       └── webhook-event.dto.ts         WebhookEventDto: eventType, orderId, status, details
 │
 ├── pubsub/
 │   ├── pubsub.service.ts                publishResult() → topic.publishMessage() con attributes
 │   └── pubsub.module.ts
 │
 ├── health/
 │   ├── health.controller.ts             GET /health (liveness) + GET /health/ready (readiness)
 │   └── health.module.ts
 │
 └── common/
     ├── filters/
     │   └── global-exception.filter.ts   Catch-all: log estructurado + correlationId
     └── middlewares/
         ├── correlation-id.middleware.ts X-Correlation-ID: propaga o genera UUID
         └── request-logger.middleware.ts Log JSON: method, path, status, duration_ms
08

Smoke test E2E

Una vez desplegado, verifica los tres flujos principales:

Flujo 1 — Crear orden de entrega

CSB
→
Cloud Tasks
POST /delivery/create-order
→
Delivery Adapter
→
Bringoz API
curl — crear orden (con IAM token)
URL=$(gcloud run services describe csb-delivery-adapter-dev-api \
  --region=us-central1 --format='value(status.url)')
TOKEN=$(gcloud auth print-identity-token)

curl -X POST "$URL/delivery/create-order" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "envelope": {
      "id": "test-evt-001",
      "entityId": "ORD-TEST-001",
      "data": {
        "orderId": "ORD-TEST-001",
        "customerName": "Test Bebbia",
        "deliveryAddress": "Calle Test 1, CDMX",
        "postalCode": "06600",
        "phone": "5500000000"
      }
    }
  }' | jq .

# Respuesta esperada:
# {
#   "status": "ok",
#   "provider": "bringoz",
#   "result": { "orderId": "ORD-TEST-001", "status": "pending", ... }
# }

Flujo 2 — Tracking

CSB
→
Adapter
GET /delivery/track/ORD-TEST-001
→
Bringoz API
curl — consultar tracking
curl -H "Authorization: Bearer $TOKEN" \
  "$URL/delivery/track/ORD-TEST-001" | jq .

# { "status": "ok", "result": { "orderId": "ORD-TEST-001", "status": "pending" } }

Flujo 3 — Webhook callback

Bringoz
→
POST /webhooks/bringoz
WebhookEventDto
→
PubSub
→
CSB
curl — simular webhook de Bringoz
curl -X POST "$URL/webhooks/bringoz" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "order_delivered",
    "orderId": "ORD-TEST-001",
    "status": "completed",
    "trackingUrl": "https://track.bringoz.com/test",
    "details": { "driverName": "Test Driver" },
    "timestamp": "2026-08-27T20:00:00Z"
  }' | jq .

# { "status": "ok", "acknowledged": true }
09

Orden de ejecucion recomendado

# Tarea Estado
1 Crear secrets en Secret Manager (csb-dev-bringoz-*) pendiente
2 Crear service accounts y permisos IAM pendiente
3 Crear Artifact Registry repo csb-adapters (compartido con erp-adapter) pendiente
4 Crear directorio infra/terraform/ con modulos listo
5 Crear cloudbuild.yaml listo
6 Crear Dockerfile (multi-stage, non-root) listo
7 Obtener spec de API de Bringoz pendiente
8 Implementar llamadas reales en BringozProvider pendiente
9 Deploy manual (seccion 04) para validar imagen pendiente
10 Registrar URL del Cloud Run en seed del CSB pendiente
11 Crear Cloud Tasks queue pendiente
12 Crear suscripcion Pub/Sub en CSB pendiente
13 Configurar webhook URL en panel de Bringoz pendiente
14 Smoke test E2E (seccion 08) pendiente
✓
27 tests pasando en rama dev (23 unit + 4 e2e). Codigo scaffolding completo con 6 modulos, infra Terraform (4 modulos, 3 ambientes), Dockerfile multi-stage y pipeline CI/CD. Lo pendiente es obtener la spec de Bringoz para implementar las llamadas reales en los providers, y la configuracion GCP (secrets, IAM, Cloud Build trigger).
⚠
Bloqueante: Los providers (BringozProvider, NexusProvider) tienen implementaciones stub (retornan datos ficticios). Se necesita la spec de la API de Bringoz para implementar las llamadas HTTP reales con autenticacion X-API-Key + X-Tenant-ID.