Sistema de Inventario Agranelos - Backend API

Sistema completo de gestión de inventario con Arquitectura Orientada a Eventos usando Azure Functions, Event Grid y Java 11.

Deploy to Azure Java Azure Functions PostgreSQL Event Grid

Backend serverless para sistema de inventario de bodegas implementado con Azure Functions, Azure Event Grid y PostgreSQL.

Arquitectura

Descripción

Sistema backend cloud-native para gestión de inventario que proporciona:

Características Principales

Arquitectura del Sistema

El sistema implementa una arquitectura orientada a eventos que incluye:

Componentes Principales

┌─────────────────────────────────────────────────────────────┐
│                    AZURE CLOUD                               │
│                                                              │
│  ┌────────────────────────────────────────────────────┐    │
│  │         Azure Functions (Java 11)                   │    │
│  │  • REST API (/api/productos, /api/bodegas)         │    │
│  │  • GraphQL API (/api/graphql)                       │    │
│  │  • CRUD Operations                                  │    │
│  └─────────────┬──────────────────────────────────────┘    │
│                │                                             │
│                │ Publica Eventos                             │
│                ▼                                             │
│  ┌────────────────────────────────────────────────────┐    │
│  │         Azure Event Grid Topic                      │    │
│  │  • ProductoCreado / Actualizado / Eliminado        │    │
│  │  • BodegaCreada / Actualizada / Eliminada          │    │
│  └─────────────┬──────────────────────────────────────┘    │
│                │                                             │
│                │ Distribuye Eventos                          │
│                ▼                                             │
│  ┌────────────────────────────────────────────────────┐    │
│  │       Event Grid Trigger Functions                  │    │
│  │  • Event Handlers para cada tipo de evento         │    │
│  │  • Notificaciones, auditoría, sincronización       │    │
│  └────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌────────────────────────────────────────────────────┐    │
│  │       Application Insights                          │    │
│  │  • Logs, métricas, alertas, monitoreo              │    │
│  └────────────────────────────────────────────────────┘    │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       │ JDBC (Pooled)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│              PostgreSQL Database (AWS EC2)                   │
│  Tablas: PRODUCTO, BODEGA, INVENTARIO, MOVIMIENTO          │
└─────────────────────────────────────────────────────────────┘

Flujo de Eventos

sequenceDiagram
    participant C as Cliente
    participant AF as Azure Function
    participant DB as PostgreSQL
    participant EG as Event Grid
    participant EH as Event Handler

    C->>AF: POST /api/productos
    AF->>DB: INSERT producto
    DB-->>AF: ID creado
    AF->>EG: Publicar ProductoCreado
    AF-->>C: 201 Created
    
    EG->>EH: Distribuir evento
    EH->>EH: Procesar (notificar, auditar)

Arquitectura Orientada a Eventos

El sistema utiliza Azure Event Grid para implementar una arquitectura event-driven:

Tipos de Eventos

Categoría Evento Descripción
Productos ProductoCreado Se dispara al crear un producto
  ProductoActualizado Se dispara al actualizar un producto
  ProductoEliminado Se dispara al eliminar un producto
Bodegas BodegaCreada Se dispara al crear una bodega
  BodegaActualizada Se dispara al actualizar una bodega
  BodegaEliminada Se dispara al eliminar una bodega

Beneficios de Event-Driven

Diagrama de Arquitectura Completo

graph TB
    subgraph "Frontend Layer"
        WEB[Web Application]
        MOBILE[Mobile App]
        POSTMAN[Postman Testing]
    end

    subgraph "BFF Layer (Otro Repo)"
        BFF[Backend for Frontend]
        BFF --> |"Orchestration"| API
    end

    subgraph "API Layer (Este Repo)"
        API[Azure Functions<br/>Java 11]
        
        subgraph "API Endpoints"
            REST[REST API<br/>/api/productos<br/>/api/bodegas]
            GQL[GraphQL API<br/>/api/graphql]
        end
        
        API --> REST
        API --> GQL
    end

    subgraph "Data Processing"
        MAPPER[Field Mapping<br/>cantidad ↔ cantidadEnStock]
        FETCHER[Data Fetchers<br/>ProductoDataFetcher<br/>BodegaDataFetcher]
        
        REST --> FETCHER
        GQL --> MAPPER
        MAPPER --> FETCHER
    end

    subgraph "Infrastructure"
        subgraph "AWS EC2"
            DOCKER[Docker Container]
            DB[(PostgreSQL<br/>inventario_agranelos)]
            DOCKER --> DB
        end
        
        AZURE[Azure Functions<br/>Consumption Plan]
    end

    subgraph "Development & Testing"
        SCRIPTS[Testing Scripts<br/>scripts/testing/]
        POSTMAN_COLL[Postman Collection<br/>postman/]
        DOCS[Documentation Site<br/>docs/]
    end

    %% Connections
    WEB --> BFF
    MOBILE --> BFF
    WEB -.-> API
    MOBILE -.-> API
    POSTMAN --> API
    API --> AZURE
    FETCHER --> DB
    SCRIPTS --> API
    POSTMAN_COLL --> API

    %% Styling
    classDef primary fill:#e1f5fe,stroke:#01579b,stroke-width:2px
    classDef secondary fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
    classDef infrastructure fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px
    classDef tools fill:#fff3e0,stroke:#e65100,stroke-width:2px

    class API,REST,GQL primary
    class MAPPER,FETCHER secondary
    class AZURE,DOCKER,DB infrastructure
    class SCRIPTS,POSTMAN_COLL,DOCS,POSTMAN tools

Modelo de Base de Datos

erDiagram
    PRODUCTO {
        int id PK
        string nombre
        string descripcion
        decimal precio_unitario
        int stock_minimo
        datetime fecha_creacion
        datetime fecha_actualizacion
    }
    
    BODEGA {
        int id PK
        string nombre
        string ubicacion
        string descripcion
        boolean activa
        datetime fecha_creacion
        datetime fecha_actualizacion
    }
    
    INVENTARIO {
        int id PK
        int producto_id FK
        int bodega_id FK
        int cantidad
        datetime fecha_actualizacion
    }
    
    MOVIMIENTO {
        int id PK
        int producto_id FK
        int bodega_id FK
        string tipo_movimiento
        int cantidad
        date fecha
    }
    
    PRODUCTO ||--o{ INVENTARIO : "stored_in"
    BODEGA ||--o{ INVENTARIO : "contains"
    PRODUCTO ||--o{ MOVIMIENTO : "involves"
    BODEGA ||--o{ MOVIMIENTO : "location"

Funciones Implementadas

El sistema expone los siguientes endpoints a través de las funciones serverless de Azure Functions:

Azure Functions Serverless (Puerto 7071)

Productos

Verbo Ruta Descripción
GET /api/productos Obtiene la lista de todos los productos.
GET /api/productos/{id} Obtiene un producto específico por su ID.
POST /api/productos Crea un nuevo producto.
PUT /api/productos/{id} Actualiza un producto existente por su ID.
DELETE /api/productos/{id} Elimina un producto por su ID.

Bodegas

Verbo Ruta Descripción
GET /api/bodegas Obtiene la lista de todas las bodegas.
GET /api/bodegas/{id} Obtiene una bodega específica por su ID.
POST /api/bodegas Crea una nueva bodega.
PUT /api/bodegas/{id} Actualiza una bodega existente por su ID.
DELETE /api/bodegas/{id} Elimina una bodega por su ID.

Utilidades

Verbo Ruta Descripción Seguridad
POST /api/init Inicializa la base de datos con esquemas y datos de prueba. Sólo desarrollo - Requiere ENABLE_INIT=true y clave de función

🔧 Componentes Técnicos

Backend API (Este Repositorio)

Base de Datos

BFF (Backend for Frontend)

GraphQL API (Alternativa Moderna)

Además de los endpoints REST tradicionales, el sistema incluye soporte completo para GraphQL como alternativa moderna y flexible:

Verbo Ruta Descripción

BFF (Backend for Frontend)| :—– | :———— | :——————————— |

Inicio RápidoQuery - Obtener todos los productos:


### Prerrequisitosquery {

  productos {

```bash    id

# Java 11 o superior    nombre

java -version    descripcion

    precio

# Maven 3.6+      cantidadEnStock

mvn -version    fechaCreacion

  }

# Azure Functions Core Tools (opcional para desarrollo local)}

func --version```

Query - Obtener producto específico:

Instalación y Despliegue```graphql

query {

```bash producto(id: “1”) {

Clonar repositorio id

git clone https://github.com/DiegoBarrosA/agranelos-functions-crud.git nombre

cd agranelos-functions-crud descripcion

precio

Compilar proyecto cantidadEnStock

mvn clean package }

}

Desplegar a Azure (configurar Azure CLI previamente)```

mvn azure-functions:deploy

```Mutation - Crear nuevo producto:


### Configuraciónmutation {

  crearProducto(input: {

Configurar variables de entorno en `local.settings.json`:    nombre: "Producto GraphQL"

    descripcion: "Creado via GraphQL"

```json    precio: 29.99

{    cantidad: 100

  "IsEncrypted": false,  }) {

  "Values": {    success

    "FUNCTIONS_WORKER_RUNTIME": "java",    message

    "DB_HOST": "your-db-host",    producto {

    "DB_PORT": "5432",      id

    "DB_NAME": "inventario_agranelos",      nombre

    "DB_USER": "postgres",      precio

    "DB_PASSWORD": "your-password",    }

    "DB_SSL_MODE": "disable"    error

  }  }

}}

📡 APIs DisponiblesMutation - Crear nueva bodega:


### 🔗 REST APImutation {

  crearBodega(input: {

**Base URL**: `https://agranelos-fybpb6duaadaaxfm.eastus2-01.azurewebsites.net/api`    nombre: "Bodega GraphQL"

    ubicacion: "Santiago Norte"

#### Productos    capacidad: 5000

```http  }) {

GET    /productos           # Obtener todos los productos    success

GET    /productos/{id}      # Obtener producto por ID    message

POST   /productos           # Crear producto    bodega {

PUT    /productos/{id}      # Actualizar producto      id

DELETE /productos/{id}      # Eliminar producto      nombre

```      ubicacion

      capacidad

#### Bodegas    }

```http    error

GET    /bodegas             # Obtener todas las bodegas  }

GET    /bodegas/{id}        # Obtener bodega por ID}

| Verbo  | Ruta          | Descripción                        |
| :----- | :------------ | :--------------------------------- |
| `POST` | `/api/graphql`| Endpoint único GraphQL para todas las operaciones |

### GraphQL API  

**Endpoint**: `${GRAPHQL_URL}`

#### Configuración de Variables de Entorno

Antes de ejecutar las consultas GraphQL, configura las variables de entorno:

```bash
# Desarrollo Local
export GRAPHQL_URL="http://localhost:7071/api/graphql"

# Staging
export GRAPHQL_URL="https://agranelos-staging.azurewebsites.net/api/graphql"

# Producción
export GRAPHQL_URL="https://agranelos-fybpb6duaadaaxfm.eastus2-01.azurewebsites.net/api/graphql"

Ejemplo de Query

query {
  productos {
    id
    nombre
    descripcion
    precio
    cantidad
    fechaCreacion
    fechaActualizacion
  }
}

Ejemplo con curl:

curl -X POST "${GRAPHQL_URL}" \
     -H "Content-Type: application/json" \
     -d '{
   "query": "query { productos { id nombre descripcion precio cantidad fechaCreacion fechaActualizacion } }"
 }'

Query - Health Check:

query {
  health
}

Ventajas de GraphQL vs REST

}

```> Tip: Puedes usar tanto REST como GraphQL según tus necesidades. REST para operaciones simples, GraphQL para consultas complejas y flexibilidad.

Ejemplo de Mutation> Importante: El endpoint /api/init debe estar deshabilitado en producción. Configure ENABLE_INIT=false y use autenticación con clave de función cuando esté habilitado.


mutation {## Despliegue y Ejecución

  crearProducto(input: {

    nombre: "Producto Nuevo"### Prerrequisitos

    descripcion: "Descripción del producto"- Java 11 (como está configurado en el workflow de GitHub Actions)

    precio: 29.99- Maven 3.6+

    cantidad: 100- PostgreSQL 13+

  }) {- Azure Functions Core Tools (para desarrollo local)

    success- Nix (opcional, para entorno de desarrollo)

    message

    producto {### Configuración de Base de Datos

      id1. **PostgreSQL Local**:

      nombre   ```bash

      precio   # Crear archivo .env en la raíz del proyecto (asegurar que esté en .gitignore)

      cantidad   echo "POSTGRES_USER=inventario_user" > .env

    }   echo "POSTGRES_PASSWORD=inventario_pass" >> .env

  }   echo "POSTGRES_DB=inventario_db" >> .env

}   

```   # Usar PostgreSQL con archivo .env (evita credenciales en historial)

   docker run --name postgres-agranelos --env-file .env -p 5432:5432 -d postgres:13

## Testing   ```



### Scripts Automatizados

```bash
# Ejecutar todos los tests
./scripts/testing/test-all-apis.sh

# Tests específicos
./scripts/testing/test-rest-api.sh      # Solo REST
./scripts/testing/test-graphql-api.sh   # Solo GraphQL
./scripts/testing/test-performance.sh   # Rendimiento

Postman Collection

  1. Importar colección desde postman/Agranelos-Inventario-API-Collection.postman_collection.json
  2. Las variables de entorno se configuran automáticamente
  3. Ejecutar tests en orden: Database Setup → REST → GraphQL

Ver documentación completa: postman/README.md

Inicio Rápido

Prerrequisitos

# Java 11 o superior
java -version

# Maven 3.6+
mvn -version

# Azure Functions Core Tools 4.x
func --version

# PostgreSQL con Docker
docker --version

Configuración de Base de Datos

  1. PostgreSQL con Docker:
# Levantar PostgreSQL en Docker
docker run --name postgres-agranelos -e POSTGRES_PASSWORD=inventario_pass -e POSTGRES_USER=inventario_user -e POSTGRES_DB=inventario_db -p 5432:5432 -d postgres:14
  1. Inicialización del esquema:

El esquema se crea automáticamente usando el archivo schema.sql. También puedes usar la función de inicialización:

# SOLO EN DESARROLLO - Configurar variables de entorno primero
export ENABLE_INIT=true
export FUNCTION_KEY="your-dev-function-key"

# Llamada POST con autenticación usando variables de entorno
curl -X POST "http://localhost:7071/api/init" \
     -H "x-functions-key: ${FUNCTION_KEY}" \
     -H "Content-Type: application/json"

Configuración de seguridad para /api/init:

🔒 Nota de Seguridad: Nunca incluyas claves literales en el historial de comandos o ejemplos públicos. En producción, obtén las claves desde Azure Key Vault o servicios de gestión de secretos seguros.

Testing Scripts#### Opción 1: Usando Nix (Recomendado)


Documentación detallada de scripts: [`scripts/testing/README.md`](scripts/testing/README.md)# Entrar al entorno de desarrollo

nix develop

## 🔧 Configuración de Desarrollo

# Compilar y ejecutar funciones serverless

### Estructura del Proyectomvn clean package

func host start --java

agranelos-functions-crud/

├── src/main/java/com/agranelos/inventario/#### Opción 2: Ejecución Manual

│ ├── Function.java # Azure Functions endpoints```bash

│ ├── graphql/# Compilar y ejecutar Azure Functions

│ │ ├── GraphQLSchemaBuilder.java # Schema y field mappingmvn clean package

│ │ ├── ProductoDataFetcher.java # Data fetchers productosfunc host start –java

│ │ └── BodegaDataFetcher.java # Data fetchers bodegas```

│ ├── model/ # Modelos de datos

│ └── db/ # Gestión de base de datos### Acceso a la Aplicación

├── scripts/testing/ # Scripts de testing- Azure Functions: http://localhost:7071

├── postman/ # Colección Postman

├── docs/ # Documentación Jekyll### Pruebas

├── assets/ # Imágenes y recursos```bash

└── README.md # Este archivo# Ejecutar pruebas de las funciones serverless

```mvn test


### Configuración Local

### Despliegue en Producción

```bash

# Configurar base de datos local (opcional)#### Azure Functions

docker-compose up -d```bash

# Compilar para producción

# Ejecutar función localmentemvn clean package

mvn clean package

cd target/azure-functions/*/# Despliegue en Azure (requiere Azure CLI configurado)

func host startfunc azure functionapp publish agranelos --java

Despliegue

Variables de Entorno

Azure Functions (local.settings.json)

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "java",
    "DB_HOST": "localhost",
    "DB_PORT": "5432", 
    "DB_NAME": "inventario_db",
    "DB_USER": "inventario_user",
    "DB_PASSWORD": "inventario_pass",
    "DB_SSL_MODE": "disable"
  }
}

Comandos de Despliegue

# Compilar y desplegar
mvn clean package azure-functions:deploy

# Configurar variables de entorno en Azure Portal
# Settings → Configuration → Application Settings

Variables de Producción

Configurar en Azure Portal las siguientes variables:

Estructura del Proyecto

agranelos-functions-crud-create/
├── src/                                    # Funciones serverless Azure
│   ├── main/java/com/agranelos/inventario/
│   │   ├── Function.java                   # Endpoints CRUD (productos y bodegas)
│   │   ├── db/                            # Gestión de base de datos
│   │   └── model/                         # Modelos de datos
│   └── test/java/                         # Tests unitarios
├── scripts/testing/                       # Scripts de pruebas automatizadas
├── postman/                              # Colección de Postman
├── assets/                               # Imágenes y recursos
├── docs/                                 # Documentación Jekyll/GitHub Pages
├── pom.xml                               # Configuración Maven
├── host.json                             # Configuración Azure Functions
└── local.settings.json                  # Variables locales (no versionado)

📖 Documentación Adicional

│ └── test/ # Pruebas unitarias

🔑 Características Técnicas Destacadas├── .github/workflows/main.yml # Pipeline CI/CD GitHub Actions

├── schema.sql # Esquema de base de datos PostgreSQL

Field Mapping Automático├── flake.nix # Entorno de desarrollo Nix

El sistema incluye mapeo automático de campos entre GraphQL y Java:├── pom.xml # Build principal (Azure Functions)

🗃️ Manejo de Base de Datos## Configuración de Entornos

🔒 Validaciones y Error Handling


## 👥 Contribución{

  "IsEncrypted": false,

1. Fork del repositorio  "Values": {

2. Crear branch de feature (`git checkout -b feature/nueva-funcionalidad`)    "AzureWebJobsStorage": "UseDevelopmentStorage=true",

3. Commit de cambios (`git commit -m 'Agregar nueva funcionalidad'`)    "FUNCTIONS_WORKER_RUNTIME": "java",

4. Push al branch (`git push origin feature/nueva-funcionalidad`)    "DB_HOST": "localhost",

5. Crear Pull Request    "DB_PORT": "5432",

    "DB_NAME": "inventario_db", 

## 📄 Licencia    "DB_USER": "inventario_user",

    "DB_PASSWORD": "inventario_pass",

Este proyecto está bajo la Licencia MIT. Ver archivo `LICENSE` para detalles.    "DB_SSL_MODE": "disable"

  }

## 📞 Contacto}

GitHub Actions Secrets (Para Despliegue Automático)

—El proyecto incluye un workflow de GitHub Actions (.github/workflows/main.yml) que despliega automáticamente las Azure Functions cada vez que se hace push a la rama main.

**Secretos necesarios en GitHub:** Sistema de Inventario Agranelos
1. Ve a **Settings > Secrets and variables > Actions** Backend API con Azure Functions + PostgreSQL + GraphQL2. Agrega los siguientes secretos:

Despliegue Automático:

Azure Function App Configuration

  1. Portal de Azure > Function App > Configuration
  2. Application Settings:
    • DB_CONNECTION_STRING
    • Otras variables según necesidades

Guía de Despliegue Completo

Métodos de Despliegue

Opción 1: Despliegue Automático con GitHub Actions (Recomendado)

El proyecto incluye un workflow automatizado que se ejecuta en cada push a main:

# El workflow se ejecuta automáticamente cuando:
# - Se hace push a la rama main
# - Se crea un Pull Request
# - Se ejecuta manualmente desde GitHub Actions

Configuración:

  1. Fork del repositorio o configurar secretos en tu repo
  2. Configurar secretos en GitHub (ver sección anterior)
  3. Obtener Publish Profile de Azure Function App:
    # Descargar perfil desde Azure Portal
    az functionapp deployment list-publishing-profiles --name agranelos --resource-group <resource-group> --xml
    
  4. Push a main - El despliegue se ejecuta automáticamente

Monitoreo del despliegue:

⚙️ Opción 2: Despliegue Manual

1. Preparación del Entorno

# Clonar el repositorio
git clone <repository-url>
cd agranelos-functions-crud-create

# Entrar al entorno Nix (recomendado)
nix develop

# O instalar dependencias manualmente
# Java 17, Maven, Docker, Azure Functions Core Tools

2. Configuración de Base de Datos

# Iniciar PostgreSQL con Docker
docker run --name postgres-agranelos -e POSTGRES_PASSWORD=inventario_pass -e POSTGRES_USER=inventario_user -e POSTGRES_DB=inventario_db -p 5432:5432 -d postgres:13

# Verificar esquema (se crea automáticamente)
psql -h localhost -U inventario_user -d inventario_db -f schema.sql

3. Despliegue Local Completo

# Terminal 1: Azure Functions
mvn clean package
func host start --java

# Terminal 2: Verificar servicios
curl http://localhost:7071/api/productos
curl http://localhost:7071/api/bodegas

4. Despliegue a Azure

Opción A: Automático con GitHub Actions

# 1. Configurar secretos en GitHub (ver sección anterior)
# 2. Push a main trigger deployment automático
git add .
git commit -m "Deploy to Azure"
git push origin main

# 3. Monitorear en GitHub Actions tab
# 4. Verificar despliegue
curl https://agranelos.azurewebsites.net/api/productos

Opción B: Despliegue Manual con Azure CLI

# Autenticar con Azure
az login

# Crear recursos (si no existen)
az group create --name rg-agranelos --location eastus
az storage account create --name saagranelos --resource-group rg-agranelos
az functionapp create --name agranelos --resource-group rg-agranelos --storage-account saagranelos --runtime java --runtime-version 11

# Desplegar funciones manualmente
mvn azure-functions:deploy

# Configurar variables de entorno en Azure
az functionapp config appsettings set --name agranelos --resource-group rg-agranelos --settings DB_HOST="<db-host>" DB_PORT="5432" DB_NAME="inventario_db" DB_USER="<db-user>" DB_PASSWORD="<db-password>" DB_SSL_MODE="require"

5. Verificación Post-Despliegue

# Verificar Azure Functions (despliegue automático)
curl https://agranelos.azurewebsites.net/api/productos
curl https://agranelos.azurewebsites.net/api/bodegas

# Verificar estado del despliegue en GitHub
# Ve a: https://github.com/<tu-usuario>/<tu-repo>/actions

Monitoreo y Mantenimiento

Despliegue Continuo

Logs y Diagnósticos

Health Checks

Troubleshooting Común

Arquitectura Implementada

El sistema implementa un patrón serverless con las siguientes características:

Este diseño permite escalabilidad automática, costos optimizados y mantenimiento simplificado, ideal para sistemas de inventario serverless.

CI/CD Pipeline - GitHub Actions

El proyecto incluye un pipeline de integración y despliegue continuo configurado en .github/workflows/main.yml:

Workflow Automático

Trigger: Push a main, Pull Request, o Manual
Pipeline: Build → Test → Package → Deploy
Runtime: Java 11 con Maven
Target: Azure Function App "agranelos"

Proceso Completo

  1. Checkout: Descarga el código fuente
  2. Setup Java: Configura JDK 11 Microsoft
  3. Cache Maven: Optimiza builds con cache de dependencias
  4. Compile: mvn clean compile
  5. Test: mvn test - Ejecuta pruebas unitarias
  6. Package: mvn clean package - Genera JAR
  7. Deploy: Despliega a Azure Function App usando publish profile

Secretos Requeridos

Para que el workflow funcione correctamente, configura estos secretos en GitHub:

Secreto Descripción Ejemplo
DB_HOST Host PostgreSQL mydb.postgres.database.azure.com
DB_PORT Puerto de base de datos 5432
DB_NAME Nombre de la base de datos inventario_db
DB_USER Usuario de base de datos inventario_user
DB_PASSWORD Contraseña de base de datos your-secure-password
DB_SSL_MODE Modo SSL require
AZUREAPPSERVICE_PUBLISHPROFILE_* Perfil de publicación de Azure XML desde Azure Portal

Monitoreo del Pipeline

Uso del Pipeline

# Desarrollo normal - trigger automático
git add .
git commit -m "feat: nueva funcionalidad"
git push origin main  # ← Esto dispara el despliegue automático

# Verificar despliegue
# Ve a GitHub > Actions para monitorear el progreso
# El despliegue toma aproximadamente 3-5 minutos