Compare commits
15 Commits
45a0d84caa
...
774b6e89a9
| Author | SHA1 | Date | |
|---|---|---|---|
| 774b6e89a9 | |||
| 819bb264bc | |||
| 63dd103205 | |||
| 5095c1ce76 | |||
| b2a3b2fb9e | |||
| f73ec58a50 | |||
| 17c7c2710a | |||
| d869499990 | |||
| e5242bf79c | |||
| cb97d95cdf | |||
| a7aff82a81 | |||
| 983c0dbcd9 | |||
| c269f2773b | |||
| ce5013c926 | |||
| c8aec7caad |
165
.agent/scripts/gen_index_ucrm_apib.php
Executable file
165
.agent/scripts/gen_index_ucrm_apib.php
Executable file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* Generador de Índice Comprimido para unmscrm.apib (API Blueprint UCRM)
|
||||
*
|
||||
* USO: php gen_index_ucrm_apib.php
|
||||
* SALIDA: INDEX_api_ucrm.md en la raíz del plugin
|
||||
*
|
||||
* Reduce ~266KB (65K tokens) → ~10KB (2-3K tokens)
|
||||
*/
|
||||
|
||||
$apibPath = __DIR__ . '/../references/unmscrm.apib';
|
||||
$outputPath = __DIR__ . '/../../INDEX_api_ucrm.md';
|
||||
|
||||
if (!file_exists($apibPath)) {
|
||||
die("ERROR: No se encontró unmscrm.apib en: $apibPath\n");
|
||||
}
|
||||
|
||||
echo "Leyendo unmscrm.apib (" . round(filesize($apibPath) / 1024, 1) . " KB)...\n";
|
||||
|
||||
$content = file_get_contents($apibPath);
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
$groups = [];
|
||||
$current_group = 'General';
|
||||
$current_resource = '';
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = rtrim($line);
|
||||
|
||||
// Grupo: "# Group Clients"
|
||||
if (preg_match('/^#\s+Group\s+(.+)$/i', $line, $m)) {
|
||||
$current_group = trim($m[1]);
|
||||
if (!isset($groups[$current_group])) {
|
||||
$groups[$current_group] = [];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recurso nivel 1: "# /clients{?param1,param2}" — solo si empieza con /
|
||||
if (preg_match('/^#\s+(\/[^\s{]+)/', $line, $m)) {
|
||||
// Limpiar parámetros de query string del path
|
||||
$raw_path = $m[1];
|
||||
$current_resource = preg_replace('/\{[^}]*\}/', '', $raw_path); // quitar {?params}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recurso nivel 2 con path en brackets: "## Client [/clients/{id}]"
|
||||
if (preg_match('/^##\s+[^[]+\[([^\]]+)\]/', $line, $m)) {
|
||||
$current_resource = trim($m[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recurso nivel 2 que es un path: "## /clients/{id}"
|
||||
if (preg_match('/^##\s+(\/[^\s{]+)/', $line, $m)) {
|
||||
$current_resource = $m[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Método HTTP nivel 2 directo: "## GET" or "## POST"
|
||||
if (preg_match('/^##\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s*$/i', $line, $m)) {
|
||||
$method = strtoupper($m[1]);
|
||||
if (!isset($groups[$current_group])) {
|
||||
$groups[$current_group] = [];
|
||||
}
|
||||
$groups[$current_group][] = [
|
||||
'method' => $method,
|
||||
'path' => $current_resource ?: '/',
|
||||
'desc' => '—',
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Método HTTP nivel 3: "### Descripción [METHOD /path]"
|
||||
if (preg_match('/^###\s+(.+)$/i', $line, $m)) {
|
||||
$raw = trim($m[1]);
|
||||
$method = '';
|
||||
$path = $current_resource;
|
||||
$desc = $raw;
|
||||
|
||||
// Formato: "Create Client [POST /clients]"
|
||||
if (preg_match('/\[(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s*([^\]]*)\]/i', $raw, $pm)) {
|
||||
$method = strtoupper($pm[1]);
|
||||
$pathInBracket = trim($pm[2]);
|
||||
if ($pathInBracket) $path = $pathInBracket;
|
||||
$desc = trim(preg_replace('/\[.+\]/', '', $raw));
|
||||
}
|
||||
// Formato: "GET /path Description"
|
||||
elseif (preg_match('/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+(\S+)\s*(.*)$/i', $raw, $pm)) {
|
||||
$method = strtoupper($pm[1]);
|
||||
$path = $pm[2];
|
||||
$desc = trim($pm[3]) ?: '—';
|
||||
}
|
||||
// Formato: Solo METHOD
|
||||
elseif (preg_match('/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/i', $raw, $pm)) {
|
||||
$method = strtoupper($pm[1]);
|
||||
$desc = '—';
|
||||
}
|
||||
|
||||
if ($method) {
|
||||
if (!isset($groups[$current_group])) {
|
||||
$groups[$current_group] = [];
|
||||
}
|
||||
$groups[$current_group][] = [
|
||||
'method' => $method,
|
||||
'path' => $path ?: '/',
|
||||
'desc' => $desc ?: '—',
|
||||
];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Generar Markdown
|
||||
$output_lines = [];
|
||||
$output_lines[] = "# Índice API UCRM — CRM v1.0";
|
||||
$output_lines[] = "";
|
||||
$output_lines[] = "> **Generado automáticamente** desde `unmscrm.apib`";
|
||||
$output_lines[] = "> Ejecutar `php .agent/scripts/gen_index_ucrm_apib.php` para regenerar.";
|
||||
$output_lines[] = "";
|
||||
$output_lines[] = "**Base URL:** `https://{ipserver}/crm/api/v1.0/`";
|
||||
$output_lines[] = "**Auth:** Header `X-Auth-App-Key: {apitoken}`";
|
||||
$output_lines[] = "";
|
||||
$output_lines[] = "---";
|
||||
$output_lines[] = "";
|
||||
$output_lines[] = "## Resumen de Grupos";
|
||||
$output_lines[] = "";
|
||||
|
||||
$total = 0;
|
||||
foreach ($groups as $group => $ops) {
|
||||
$count = count($ops);
|
||||
$total += $count;
|
||||
$output_lines[] = "- **{$group}** ({$count} endpoints)";
|
||||
}
|
||||
$output_lines[] = "";
|
||||
$output_lines[] = "**Total: {$total} endpoints en " . count($groups) . " grupos**";
|
||||
$output_lines[] = "";
|
||||
$output_lines[] = "---";
|
||||
$output_lines[] = "";
|
||||
|
||||
foreach ($groups as $group => $ops) {
|
||||
if (empty($ops)) continue;
|
||||
$output_lines[] = "## {$group}";
|
||||
$output_lines[] = "";
|
||||
foreach ($ops as $op) {
|
||||
$output_lines[] = sprintf(
|
||||
" - %-8s `%s` — %s",
|
||||
$op['method'],
|
||||
$op['path'],
|
||||
$op['desc']
|
||||
);
|
||||
}
|
||||
$output_lines[] = "";
|
||||
}
|
||||
|
||||
$output = implode("\n", $output_lines);
|
||||
file_put_contents($outputPath, $output);
|
||||
|
||||
$sizeKb = round(strlen($output) / 1024, 1);
|
||||
$tokenEst = round(strlen($output) / 4);
|
||||
|
||||
echo "✅ Índice generado: INDEX_api_ucrm.md\n";
|
||||
echo " Tamaño: {$sizeKb} KB (~{$tokenEst} tokens)\n";
|
||||
echo " Reducción: " . round((1 - strlen($output) / filesize($apibPath)) * 100) . "% vs original\n";
|
||||
echo " Grupos: " . count($groups) . "\n";
|
||||
echo " Endpoints: {$total}\n";
|
||||
126
.agent/scripts/gen_index_unms_swagger.php
Executable file
126
.agent/scripts/gen_index_unms_swagger.php
Executable file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* Generador de Índice Comprimido para unms-swagger.json
|
||||
*
|
||||
* USO: php gen_index_unms_swagger.php
|
||||
* SALIDA: INDEX_api_unms.md en la raíz del plugin
|
||||
*
|
||||
* Reduce ~3MB (750K tokens) → ~15KB (3-4K tokens)
|
||||
*/
|
||||
|
||||
$swaggerPath = __DIR__ . '/../references/unms-swagger.json';
|
||||
$outputPath = __DIR__ . '/../../INDEX_api_unms.md';
|
||||
|
||||
if (!file_exists($swaggerPath)) {
|
||||
die("ERROR: No se encontró unms-swagger.json en: $swaggerPath\n");
|
||||
}
|
||||
|
||||
echo "Leyendo unms-swagger.json (" . round(filesize($swaggerPath) / 1024 / 1024, 2) . " MB)...\n";
|
||||
|
||||
$swagger = json_decode(file_get_contents($swaggerPath), true);
|
||||
|
||||
if (!$swagger) {
|
||||
die("ERROR: No se pudo parsear el JSON. Error: " . json_last_error_msg() . "\n");
|
||||
}
|
||||
|
||||
$paths = $swagger['paths'] ?? [];
|
||||
$info = $swagger['info'] ?? [];
|
||||
$basePath = $swagger['basePath'] ?? '';
|
||||
$host = $swagger['host'] ?? '';
|
||||
|
||||
// Agrupar por tag
|
||||
$grouped = [];
|
||||
$tagDescriptions = [];
|
||||
|
||||
// Recolectar descripciones de tags
|
||||
foreach ($swagger['tags'] ?? [] as $tag) {
|
||||
$tagDescriptions[$tag['name']] = $tag['description'] ?? '';
|
||||
}
|
||||
|
||||
foreach ($paths as $path => $methods) {
|
||||
foreach ($methods as $method => $operation) {
|
||||
if (!in_array(strtoupper($method), ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])) continue;
|
||||
|
||||
$tags = $operation['tags'] ?? ['sin-tag'];
|
||||
$summary = $operation['summary'] ?? $operation['description'] ?? '—';
|
||||
$opId = $operation['operationId'] ?? '';
|
||||
|
||||
// Extraer parámetros clave (no el body completo)
|
||||
$params = [];
|
||||
foreach ($operation['parameters'] ?? [] as $param) {
|
||||
if (in_array($param['in'], ['path', 'query'])) {
|
||||
$required = ($param['required'] ?? false) ? '' : '?';
|
||||
$params[] = $param['name'] . $required;
|
||||
}
|
||||
}
|
||||
$paramStr = $params ? ' [' . implode(', ', $params) . ']' : '';
|
||||
|
||||
// Códigos de respuesta clave
|
||||
$responses = array_keys($operation['responses'] ?? []);
|
||||
$respStr = $responses ? ' → ' . implode('/', $responses) : '';
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$grouped[$tag][] = sprintf(
|
||||
" - %-8s `%s`%s — %s%s",
|
||||
strtoupper($method),
|
||||
$path,
|
||||
$paramStr,
|
||||
$summary,
|
||||
$respStr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ksort($grouped);
|
||||
|
||||
// Generar Markdown
|
||||
$lines = [];
|
||||
$lines[] = "# Índice API UNMS (UISP) — NMS v2.1";
|
||||
$lines[] = "";
|
||||
$lines[] = "> **Generado automáticamente** desde `unms-swagger.json`";
|
||||
$lines[] = "> Ejecutar `php .agent/scripts/gen_index_unms_swagger.php` para regenerar.";
|
||||
$lines[] = "";
|
||||
$lines[] = "**Host:** `{$host}` **Base Path:** `{$basePath}`";
|
||||
$lines[] = "**Versión API:** " . ($info['version'] ?? 'N/A');
|
||||
$lines[] = "";
|
||||
$lines[] = "---";
|
||||
$lines[] = "";
|
||||
$lines[] = "## Resumen de Grupos";
|
||||
$lines[] = "";
|
||||
|
||||
$total = 0;
|
||||
foreach ($grouped as $tag => $ops) {
|
||||
$count = count($ops);
|
||||
$total += $count;
|
||||
$desc = $tagDescriptions[$tag] ?? '';
|
||||
$lines[] = "- **{$tag}** ({$count} endpoints)" . ($desc ? " — {$desc}" : '');
|
||||
}
|
||||
$lines[] = "";
|
||||
$lines[] = "**Total: {$total} endpoints en " . count($grouped) . " grupos**";
|
||||
$lines[] = "";
|
||||
$lines[] = "---";
|
||||
$lines[] = "";
|
||||
|
||||
// Detalle por grupo
|
||||
foreach ($grouped as $tag => $ops) {
|
||||
$desc = $tagDescriptions[$tag] ?? '';
|
||||
$lines[] = "## {$tag}" . ($desc ? " — _{$desc}_" : '');
|
||||
$lines[] = "";
|
||||
foreach ($ops as $op) {
|
||||
$lines[] = $op;
|
||||
}
|
||||
$lines[] = "";
|
||||
}
|
||||
|
||||
$output = implode("\n", $lines);
|
||||
file_put_contents($outputPath, $output);
|
||||
|
||||
$sizeKb = round(strlen($output) / 1024, 1);
|
||||
$tokenEst = round(strlen($output) / 4);
|
||||
|
||||
echo "✅ Índice generado: INDEX_api_unms.md\n";
|
||||
echo " Tamaño: {$sizeKb} KB (~{$tokenEst} tokens)\n";
|
||||
echo " Reducción: " . round((1 - strlen($output) / filesize($swaggerPath)) * 100) . "% vs swagger original\n";
|
||||
echo " Grupos: " . count($grouped) . "\n";
|
||||
echo " Endpoints: {$total}\n";
|
||||
58
.agent/skills/update-plugin-docs/SKILL.md
Normal file
58
.agent/skills/update-plugin-docs/SKILL.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
name: update-plugin-docs
|
||||
description: Guía y procedimiento estándar para actualizar la documentación de un plugin UCRM (manifest.json, CHANGELOG.md, README.md), empaquetar la versión ZIP y publicar en GitHub. Úsalo cuando el usuario solicite "actualizar la documentación", "actualizar docs", "empaquetar plugin" o "crear nueva versión del plugin".
|
||||
---
|
||||
|
||||
# Procedimiento Estándar de Actualización de Documentación y Publicación de Plugins
|
||||
|
||||
Este documento define la metodología obligatoria que debe seguirse cada vez que el usuario solicite **actualizar la documentación** de un plugin de UISP UCRM.
|
||||
|
||||
## 📋 Archivos a Actualizar (Obligatorio)
|
||||
|
||||
Cuando se actualice la versión o documentación de un plugin, se deben modificar obligatoriamente los tres archivos siguientes:
|
||||
|
||||
### 1. `manifest.json`
|
||||
- Incrementar el número de versión en `"information": { "version": "X.Y.Z" }`.
|
||||
- Si el manifiesto incluye un arreglo `"changelog"`, añadir el nuevo objeto de versión con la fecha actual y la descripción del cambio.
|
||||
|
||||
### 2. `CHANGELOG.md`
|
||||
- Añadir la nueva entrada en la parte superior bajo la sección `## Changelog`:
|
||||
```markdown
|
||||
## [X.Y.Z] - YYYY-MM-DD
|
||||
### Corregido / Mejorado / Agregado
|
||||
- **Módulo / Funcionalidad**: Descripción clara del cambio realizado.
|
||||
```
|
||||
|
||||
### 3. `README.md`
|
||||
- Actualizar el badge de versión al inicio del documento:
|
||||
``
|
||||
- Añadir la versión y los aspectos destacados en la sección de novedades/historial de cambios recientes.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Empaquetado de la Versión (.zip)
|
||||
|
||||
Una vez actualizados `manifest.json`, `CHANGELOG.md` y `README.md`:
|
||||
|
||||
1. Ejecutar el script de empaquetado versionado desde la raíz del plugin:
|
||||
```bash
|
||||
./pack-plugin-versioned.php
|
||||
```
|
||||
2. Confirmar que se haya generado el archivo ZIP con el nombre exacto de la versión: `{nombre-plugin}-{version}.zip`.
|
||||
|
||||
---
|
||||
|
||||
## 🐙 Publicación en Git (Commit & Push)
|
||||
|
||||
1. Agregar los archivos modificados:
|
||||
```bash
|
||||
git add manifest.json CHANGELOG.md README.md public.php vendor/composer/installed.php
|
||||
```
|
||||
2. Realizar el commit especificando la versión:
|
||||
```bash
|
||||
git commit -m "docs: bump version X.Y.Z & update documentation"
|
||||
```
|
||||
3. Enviar los cambios al repositorio remoto:
|
||||
```bash
|
||||
git push
|
||||
```
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,7 +9,8 @@
|
||||
*.jrxml
|
||||
*.jasper
|
||||
vouchers_oxxo/
|
||||
comprobantes/
|
||||
comprobantes/*
|
||||
!comprobantes/.gitkeep
|
||||
pack-plugin-versioned.php
|
||||
*.txt
|
||||
Callbell Public API.postman_collection.json
|
||||
|
||||
47
CHANGELOG.md
47
CHANGELOG.md
@ -1,5 +1,52 @@
|
||||
# CHANGELOG - SIIP WhatsApp Notifications Plugin
|
||||
|
||||
## VERSIÓN 4.7.5 - 02-08-2026
|
||||
|
||||
### 🔄 Mejoras (Enhancements)
|
||||
1️⃣ **Soporte para Modo Embebido (`embedded=1` / `is-embedded`)**: Ocultamiento automático de la cabecera (`.header`), del menú dashboard principal (`#mainDashboard`) y de las pestañas superiores (`.tabs`) al renderizarse dentro del iframe del portal unificado de cajas (`siip-cashier-tools-ui`).
|
||||
|
||||
## VERSIÓN 4.7.4 - 15-07-2026
|
||||
|
||||
### 🐛 Correcciones (Bug Fixes)
|
||||
1️⃣ **Preservación de Contraseña de Antena Desconectada**: Se implementó una lógica de validación para evitar que el script de auditoría (`audit_client_passwords.php`) y la automatización del plugin sobrescriban una contraseña real y válida que ya esté registrada en UCRM con la leyenda `"Antena desconectada al momento de obtener la contraseña"`. Si el CPE está desconectado pero ya tenía una clave configurada en UCRM, ésta se preserva para que los instaladores puedan consultarla y utilizarla en las órdenes de soporte en sitio.
|
||||
|
||||
## VERSIÓN 4.7.3 - 13-07-2026
|
||||
|
||||
### 🔄 Mejoras (Enhancements)
|
||||
1️⃣ **Sincronización Inmediata en CallBell**: Se implementó el refresco en tiempo real del objeto de notificación `$notification->clientData` antes de llamar a `verifyClientActionToDo()`, garantizando que la contraseña corregida y el nuevo Site/Antena se actualicen en CallBell inmediatamente en el mismo webhook.
|
||||
2️⃣ **Robustez y Detección de Etiquetas**: Se actualizó la lógica en `client.edit` de `Plugin.php` para buscar las etiquetas (`OBTENER PASSWORD ANTENA`, etc.) directamente en `$notification->clientData['tags']`, haciendo que funcione correctamente con la estructura de webhooks de producción de UCRM que no contienen `extraData`.
|
||||
3️⃣ **Integración del Logger del Plugin**: Se mejoró el script de auditoría de contraseñas (`audit_client_passwords.php`) para registrar sus mensajes de progreso y excepciones de la API de UISP directamente en el log principal de UCRM (`data/plugin.log`) en lugar de enmascararlos en un log local.
|
||||
4️⃣ **Prevención de Sobrescritura de Red**: Se agregaron salvaguardas en `audit_client_passwords.php` y `ejemplo_script_actualizador.php` para evitar que fallos de conexión o respuestas parciales de la API de UISP sobrescriban el Site con `"Sin SITE"` o la Antena con `"REPETIDOR"` si el CRM ya tiene información sectorial válida.
|
||||
5️⃣ **Resolución Inteligente de Torres (AP Fallback)**: Se implementó un fallback que consulta el Site del AP Device al que está conectado inalámbricamente la antena si el Sitio del Cliente no tiene configurada la jerarquía Padre-Hijo en UISP, logrando sincronizar la Torre de manera automática en el CRM.
|
||||
|
||||
|
||||
## VERSIÓN 4.7.2 - 10-06-2026
|
||||
|
||||
### 🔄 Mejoras (Enhancements)
|
||||
1️⃣ **Ordenamiento de Tareas de Instaladores**: Se implementó el ordenamiento descendente (de más reciente a más antiguo) por fecha en la consulta de tareas/tickets activos del instalador, permitiendo al administrador ver primero los trabajos más recientes asignados.
|
||||
|
||||
## VERSIÓN 4.7.1 - 05-06-2026
|
||||
|
||||
### 🐛 Correcciones (Bug Fixes)
|
||||
1️⃣ **Robustez en Navegación (Tabs UI)**: Se reemplazó la búsqueda de pestañas basada en la inspección de atributos inline `onclick` (que fallaba si el servidor/UCRM en producción los sanitizaba, los eliminaba o reescribía sus comillas) por un atributo estándar `data-tab="..."` en el HTML, resolviendo el problema que bloqueaba los menús del portal en producción.
|
||||
2️⃣ **Filtro de Tareas de Instaladores**: Se corrigió el parámetro de consulta a la API de UCRM en la sección de gestión de instaladores. Ahora se pasa `'statuses' => [0, 1]` para obtener únicamente los tickets con estado *Abierto* (`0`) o *En curso/Pendiente* (`1`), excluyendo correctamente los tickets *Cerrados* (`2`).
|
||||
|
||||
## VERSIÓN 4.7.0 - 04-06-2026
|
||||
|
||||
### ✨ Nuevas Características (Features)
|
||||
1️⃣ **Soporte para Eventos de Pagos Completo**: Se implementó el soporte para procesar los eventos `payment.delete` (pago eliminado), `payment.unmatch` (pago desvinculado) y `payment.edit` (pago editado) en UISP. Al recibir cualquiera de estos eventos, se recalcula y sincroniza automáticamente en tiempo real el nuevo saldo y estado del cliente en Callbell.
|
||||
2️⃣ **Evitar Consultas redundantes 404**: Se optimizó la fábrica `NotificationDataFactory` para mapear los objetos de la entidad directamente desde el payload del webhook (`extraData.entity` o `extraData.entityBeforeEdit`), evitando errores 404 en la API de UCRM al procesar la eliminación de pagos (`payment.delete`).
|
||||
|
||||
### ⚡ Optimización y Limpieza (Performance & Chores)
|
||||
1️⃣ **Remoción de Dependencias Innecesarias**: Se eliminó `"google/apiclient"` de `composer.json`, reduciendo el tamaño en disco de la carpeta `vendor` de **222 MB a sólo 28 MB** (un 87% menos).
|
||||
2️⃣ **Exclusiones de Empaquetado**: Se optimizó el empaquetador `pack-plugin-versioned.php` para ignorar archivos locales de prueba, ejecutables de composer, logs locales (`data/`) y comprobantes PDF temporales (`comprobantes/`), reduciendo el tamaño final del archivo ZIP de **~45 MB a sólo 8.1 MB** (un 82% menos).
|
||||
|
||||
## VERSIÓN 4.6.1 - 11-05-2026
|
||||
|
||||
### 🐛 Correcciones (Bug Fixes)
|
||||
1️⃣ **Fix UI Dashboard (Stripe)**: Resolución de errores de sintaxis JavaScript en el portal de pagos (uso de comillas simples en strings multilínea en `views/stripe.php`) que bloqueaban el renderizado y la navegación del menú administrativo.
|
||||
2️⃣ **Mejora del Feedback SPEI**: Ajuste en la lógica de estados de pago de Stripe para mostrar feedback más claro ("Pago en proceso") en lugar de mensajes genéricos de error al recibir estados de procesamiento de transferencias SPEI.
|
||||
|
||||
## VERSIÓN 4.6.0 - 11-04-2026
|
||||
|
||||
### 🚀 Nuevas Características (Features)
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
📢 Buenos días equipo, Mega Actualización del Plugin Notificaciones y Pagos WhatsApp SIIP 🚀 *Versiones 3.0.0 hasta 4.3.0*
|
||||
|
||||
Se ha completado una de las actualizaciones tecnológicas más grandes e importantes en la historia del sistema central de notificaciones y cobranza. Aquí se detallan todos los cambios monumentales:
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
💻 *1. El Nuevo Portal Administrativo SIIP*
|
||||
|
||||
Se integró un portal web completo y dedicado directamente dentro del CRM. Este portal centraliza toda la operación en 4 módulos principales:
|
||||
|
||||
• 👷♂️ *Módulo de Instaladores*: Permite gestionar y vincular a los técnicos e instaladores de la empresa para el enrutamiento de notificaciones.
|
||||
• ✉️ *Módulo de Notificaciones y Comprobantes*: Brinda un historial de los pagos de cada cliente, con la función (altamente solicitada) para **reenviar manualmente los comprobantes de pago** por WhatsApp con un solo clic.
|
||||
• 💳 *Módulo de Cobranza con Tarjetas (Stripe)*: Permite realizar cargos a tarjetas de crédito/débito en tiempo real o generar enlaces de pago, además de visualizar el historial financiero de los clientes.
|
||||
• 🏪 *Módulo de Cobranza en Efectivo (Oxxo)*: Desde aquí se generan referencias de Oxxo Pay al instante para que el cliente pague en tienda, llevando un control exacto de las referencias emitidas.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🤖 *2. El Nuevo Cerebro de WhatsApp (Callbell)*
|
||||
|
||||
• 📱 *Nuevo Bot de Callbell*: Se migró toda la operación a un nuevo bot construido desde cero. Esto permite un envío de mensajes más estable y con mucha mayor capacidad de respuesta.
|
||||
• ⚡ *Multiservicio*: Soporte ampliado para procesar múltiples servicios simultáneos sin cuellos de botella.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🧾 *3. La Revolución de Oxxo Pay y Comprobantes*
|
||||
|
||||
• 🛒 *Flujo por Órdenes y Base de Datos*: Se cambió la forma en que el sistema de Oxxo Pay funciona. Ahora trabaja bajo un sistema de órdenes respaldado por base de datos, ¡eliminando por fin los fallos y cobros no reflejados por *timeout*!
|
||||
• 🏭 *Nuevos Microservicios Dedicados*: Se extrajo la carga de trabajo pesada a un nuevo microservicio.
|
||||
→ Ahora este se encarga de generar los vouchers de Oxxo y los comprobantes de pago.
|
||||
→ Realiza el recorte automático (cropping) preciso de las imágenes.
|
||||
→ Inserta dinámicamente el *nombre del cliente* en la imagen ("overlay"), dejándolos listos para enviar.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
☁️ *4. MinIO: La Nueva Nube Privada (Estilo AWS)*
|
||||
|
||||
• 🚀 *Adiós al FTP de WordPress*: Se ha dejado de depender de cargas lentas e inseguras por FTP hacia WordPress.
|
||||
• 📦 *Almacenamiento MinIO*: Se implementó un sistema de almacenamiento de objetos idéntico a Amazon Web Services S3. Ahora todos los vouchers, comprobantes y PDFs se guardan de forma instantánea, segura y generan enlaces públicos a la velocidad de la luz.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🎨 *5. Interfaz Gráfica y Experiencia (UI/UX)*
|
||||
|
||||
• 💎 *Diseño Premium (Glassmorphism)*: Renovación visual completa usando estética de cristal moderno en el nuevo Portal Administrativo.
|
||||
• 🔐 *Sistema de Login Seguro*: Ya no cualquiera puede entrar; ahora el portal valida la sesión de manera segura directo con el core de UCRM (al igual que el Hub de Herramientas de Pagos).
|
||||
• 👁️ *Vouchers Inline*: Ahora se pueden visualizar los vouchers generados directamente dentro de la interfaz del portal sin tener que descargarlos previamente.
|
||||
• 🌙 *Modo Oscuro*: El portal ahora respeta la vista con un modo oscuro elegante y funcional para todo el equipo.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📦 *Historial de Versiones Resumido (Desde v2.9.2)*
|
||||
|
||||
*v4.3.0* (Actual)
|
||||
→ Sistema de Login con validación UCRM y Rediseño Premium Glassmorphism UI.
|
||||
|
||||
*v4.1.0*
|
||||
→ Integración de Microservicio PDF y MinIO, Auto-recorte de comprobantes y textos dinámicos sobre imagen.
|
||||
|
||||
*v4.0.0*
|
||||
→ Re-diseño UI con Nuevo Portal, integración Stripe (SPEI/OXXO), visualización inline y Optimización de seguridad FTP/Nube.
|
||||
|
||||
*v3.1.0*
|
||||
→ Módulos Administrativos y Re-envío manual de notificaciones de pago.
|
||||
|
||||
*v3.0.0*
|
||||
→ Soporte multiservicios y optimizaciones pesadas de rendimiento backend.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🚀 *Cómo Acceder*
|
||||
|
||||
Al igual que siempre, lo encuentran integrado en el CRM en:
|
||||
*Reportes → Portal Administrativo de Pagos de STRIPE y Notificaciones WhatsApp*.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📝 *Notas Finales*
|
||||
|
||||
Esta no es solo una actualización visual, se cambió por completo el *motor interno* del plugin. Ahora el sistema opera con arquitectura de microservicios, cuenta con un portal administrativo completo y usa nubes privadas (MinIO) de nivel empresarial. Los fallos de timeout al cruzar cobros con Oxxo y las lentitudes de subir archivos por FTP pasaron a la historia.
|
||||
|
||||
Cualquier duda, favor de reportarla de inmediato para revisión.
|
||||
|
||||
*¡A sacarle provecho!* 💪
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
_Desarrollado con ❤️ por SIIP Internet_
|
||||
_Versión 4.3.0 - Marzo 2026_
|
||||
388
INDEX_api_ucrm.md
Executable file
388
INDEX_api_ucrm.md
Executable file
@ -0,0 +1,388 @@
|
||||
# Índice API UCRM — CRM v1.0
|
||||
|
||||
> **Generado automáticamente** desde `unmscrm.apib`
|
||||
> Ejecutar `php .agent/scripts/gen_index_ucrm_apib.php` para regenerar.
|
||||
|
||||
**Base URL:** `https://{ipserver}/crm/api/v1.0/`
|
||||
**Auth:** Header `X-Auth-App-Key: {apitoken}`
|
||||
|
||||
---
|
||||
|
||||
## Resumen de Grupos
|
||||
|
||||
- **Clients** (4 endpoints)
|
||||
- **Client Bank Accounts** (5 endpoints)
|
||||
- **Client Contacts** (5 endpoints)
|
||||
- **Client Logs** (4 endpoints)
|
||||
- **Client Tags** (5 endpoints)
|
||||
- **Credit Note Templates** (0 endpoints)
|
||||
- **Credit Notes** (3 endpoints)
|
||||
- **Custom Attributes** (5 endpoints)
|
||||
- **Document Templates** (0 endpoints)
|
||||
- **Documents** (5 endpoints)
|
||||
- **Geocoding** (2 endpoints)
|
||||
- **Email** (0 endpoints)
|
||||
- **Fees** (2 endpoints)
|
||||
- **Invoice Templates** (0 endpoints)
|
||||
- **Invoices** (3 endpoints)
|
||||
- **Invoice Items** (4 endpoints)
|
||||
- **Job Attachments** (3 endpoints)
|
||||
- **Job Comments** (3 endpoints)
|
||||
- **Job Tasks** (3 endpoints)
|
||||
- **Jobs** (3 endpoints)
|
||||
- **Organizations** (0 endpoints)
|
||||
- **Payment Methods** (5 endpoints)
|
||||
- **Payment Plans** (4 endpoints)
|
||||
- **Payments** (3 endpoints)
|
||||
- **Payment Tokens** (0 endpoints)
|
||||
- **Credit Cards** (3 endpoints)
|
||||
- **Permission Groups** (0 endpoints)
|
||||
- **Products** (10 endpoints)
|
||||
- **Proforma Invoice Templates** (0 endpoints)
|
||||
- **Quote Templates** (0 endpoints)
|
||||
- **Quotes** (2 endpoints)
|
||||
- **Refund** (0 endpoints)
|
||||
- **Service Plans** (6 endpoints)
|
||||
- **Service Plan Groups** (3 endpoints)
|
||||
- **Service Surcharges** (5 endpoints)
|
||||
- **Service Suspension** (5 endpoints)
|
||||
- **Service UISP Location** (2 endpoints)
|
||||
- **Services** (10 endpoints)
|
||||
- **Service Change Requests** (2 endpoints)
|
||||
- **Prepaid Service Periods** (2 endpoints)
|
||||
- **Surcharges** (5 endpoints)
|
||||
- **Taxes** (4 endpoints)
|
||||
- **Tickets** (3 endpoints)
|
||||
- **Ticket Activity** (0 endpoints)
|
||||
- **Ticket Comment Attachments** (0 endpoints)
|
||||
- **Ticket Comments** (0 endpoints)
|
||||
- **Ticket Groups** (5 endpoints)
|
||||
- **Ticket Tags** (5 endpoints)
|
||||
- **Users** (0 endpoints)
|
||||
- **Webhook Events** (0 endpoints)
|
||||
- **General** (0 endpoints)
|
||||
- **Mobile** (0 endpoints)
|
||||
- **Client Zone Credit Cards** (3 endpoints)
|
||||
- **Client Zone Dashboard** (0 endpoints)
|
||||
- **Client Zone Invoices** (1 endpoints)
|
||||
- **Client Zone Payments** (1 endpoints)
|
||||
- **Client Zone Quotes** (1 endpoints)
|
||||
- **Client Zone Services** (1 endpoints)
|
||||
- **Client Zone Service Plans** (3 endpoints)
|
||||
- **Client Zone Tickets** (2 endpoints)
|
||||
- **Client Zone Tickets Activities** (1 endpoints)
|
||||
- **Client Zone Tickets Comments** (2 endpoints)
|
||||
- **Client Zone Tickets Comments Attachments** (1 endpoints)
|
||||
- **Client Zone Authentication** (0 endpoints)
|
||||
- **Client Zone Client** (0 endpoints)
|
||||
- **Client Zone Service Change Requests** (2 endpoints)
|
||||
- **Client Zone Prepaid Service Periods** (1 endpoints)
|
||||
- **Client Zone Overview** (0 endpoints)
|
||||
- **Client Zone Login Token** (0 endpoints)
|
||||
|
||||
**Total: 162 endpoints en 69 grupos**
|
||||
|
||||
---
|
||||
|
||||
## Clients
|
||||
|
||||
- GET `/clients` — —
|
||||
- GET `/clients/` — —
|
||||
- PATCH `/clients/` — —
|
||||
- POST `/clients/authenticated` — —
|
||||
|
||||
## Client Bank Accounts
|
||||
|
||||
- GET `/clients/bank-accounts/` — —
|
||||
- PATCH `/clients/bank-accounts/` — —
|
||||
- DELETE `/clients/bank-accounts/` — —
|
||||
- GET `/clients/` — —
|
||||
- POST `/clients/` — —
|
||||
|
||||
## Client Contacts
|
||||
|
||||
- GET `/clients/contacts/` — —
|
||||
- PATCH `/clients/contacts/` — —
|
||||
- DELETE `/clients/contacts/` — —
|
||||
- GET `/clients/` — —
|
||||
- POST `/clients/` — —
|
||||
|
||||
## Client Logs
|
||||
|
||||
- GET `/client-logs` — —
|
||||
- GET `/client-logs` — —
|
||||
- PATCH `/client-logs` — —
|
||||
- DELETE `/client-logs` — —
|
||||
|
||||
## Client Tags
|
||||
|
||||
- GET `/client-tags` — —
|
||||
- POST `/client-tags` — —
|
||||
- PATCH `/client-tags` — —
|
||||
- GET `/client-tags` — —
|
||||
- DELETE `/client-tags` — —
|
||||
|
||||
## Credit Notes
|
||||
|
||||
- GET `/credit-notes/` — —
|
||||
- PATCH `/credit-notes/` — —
|
||||
- DELETE `/credit-notes/` — —
|
||||
|
||||
## Custom Attributes
|
||||
|
||||
- GET `/custom-attributes` — —
|
||||
- POST `/custom-attributes` — —
|
||||
- GET `/custom-attributes` — —
|
||||
- PATCH `/custom-attributes` — —
|
||||
- DELETE `/custom-attributes` — —
|
||||
|
||||
## Documents
|
||||
|
||||
- GET `/documents` — —
|
||||
- POST `/documents` — —
|
||||
- GET `/documents` — —
|
||||
- DELETE `/documents` — —
|
||||
- GET `/documents` — —
|
||||
|
||||
## Geocoding
|
||||
|
||||
- GET `/geocode` — —
|
||||
- GET `/geocode/suggest` — —
|
||||
|
||||
## Fees
|
||||
|
||||
- GET `/geocode/suggest` — —
|
||||
- DELETE `/geocode/suggest` — —
|
||||
|
||||
## Invoices
|
||||
|
||||
- GET `/invoices/` — —
|
||||
- PATCH `/invoices/` — —
|
||||
- DELETE `/invoices/` — —
|
||||
|
||||
## Invoice Items
|
||||
|
||||
- GET `/invoices/items/` — —
|
||||
- PATCH `/invoices/items/` — —
|
||||
- DELETE `/invoices/items/` — —
|
||||
- GET `/invoices/` — —
|
||||
|
||||
## Job Attachments
|
||||
|
||||
- GET `/scheduling/jobs/attachments/` — —
|
||||
- PATCH `/scheduling/jobs/attachments/` — —
|
||||
- DELETE `/scheduling/jobs/attachments/` — —
|
||||
|
||||
## Job Comments
|
||||
|
||||
- GET `/scheduling/jobs/comments/` — —
|
||||
- PATCH `/scheduling/jobs/comments/` — —
|
||||
- DELETE `/scheduling/jobs/comments/` — —
|
||||
|
||||
## Job Tasks
|
||||
|
||||
- GET `/scheduling/jobs/tasks/` — —
|
||||
- PATCH `/scheduling/jobs/tasks/` — —
|
||||
- DELETE `/scheduling/jobs/tasks/` — —
|
||||
|
||||
## Jobs
|
||||
|
||||
- GET `/scheduling/jobs/` — —
|
||||
- PATCH `/scheduling/jobs/` — —
|
||||
- DELETE `/scheduling/jobs/` — —
|
||||
|
||||
## Payment Methods
|
||||
|
||||
- GET `/payment-methods` — —
|
||||
- POST `/payment-methods` — —
|
||||
- GET `/payment-methods/` — —
|
||||
- PATCH `/payment-methods/` — —
|
||||
- DELETE `/payment-methods/` — —
|
||||
|
||||
## Payment Plans
|
||||
|
||||
- GET `/payment-plans` — —
|
||||
- POST `/payment-plans` — —
|
||||
- GET `/payment-plans/` — —
|
||||
- PATCH `/payment-plans/` — —
|
||||
|
||||
## Payments
|
||||
|
||||
- GET `/payments/` — —
|
||||
- DELETE `/payments/` — —
|
||||
- PATCH `/payments/` — —
|
||||
|
||||
## Credit Cards
|
||||
|
||||
- GET `/payments/` — —
|
||||
- DELETE `/payments/` — —
|
||||
- PATCH `/payments/` — —
|
||||
|
||||
## Products
|
||||
|
||||
- GET `/products` — —
|
||||
- POST `/products` — —
|
||||
- PATCH `/products/` — —
|
||||
- GET `/products/` — —
|
||||
- DELETE `/products/` — —
|
||||
- GET `/products` — —
|
||||
- POST `/products` — —
|
||||
- PATCH `/products/` — —
|
||||
- GET `/products/` — —
|
||||
- DELETE `/products/` — —
|
||||
|
||||
## Quotes
|
||||
|
||||
- GET `/quotes/` — —
|
||||
- PATCH `/quotes/` — —
|
||||
|
||||
## Service Plans
|
||||
|
||||
- GET `/service-plans` — —
|
||||
- POST `/service-plans` — —
|
||||
- PATCH `/service-plans` — —
|
||||
- GET `/service-plans` — —
|
||||
- DELETE `/service-plans` — —
|
||||
- GET `/service-plans/statistics` — —
|
||||
|
||||
## Service Plan Groups
|
||||
|
||||
- PATCH `/service-plans/statistics` — —
|
||||
- GET `/service-plans/statistics` — —
|
||||
- DELETE `/service-plans/statistics` — —
|
||||
|
||||
## Service Surcharges
|
||||
|
||||
- GET `/clients/services/` — —
|
||||
- POST `/clients/services/` — —
|
||||
- PATCH `/clients/services/service-surcharges/` — —
|
||||
- GET `/clients/services/service-surcharges/` — —
|
||||
- DELETE `/clients/services/service-surcharges/` — —
|
||||
|
||||
## Service Suspension
|
||||
|
||||
- GET `/service-suspension-reasons` — —
|
||||
- POST `/service-suspension-reasons` — —
|
||||
- PATCH `/service-suspension-reasons/` — —
|
||||
- GET `/service-suspension-reasons/` — —
|
||||
- DELETE `/service-suspension-reasons/` — —
|
||||
|
||||
## Service UISP Location
|
||||
|
||||
- PATCH `/clients/services/` — —
|
||||
- PATCH `/clients/services/` — —
|
||||
|
||||
## Services
|
||||
|
||||
- GET `/clients/services/` — —
|
||||
- PATCH `/clients/services/` — —
|
||||
- DELETE `/clients/services/` — —
|
||||
- POST `/clients/` — —
|
||||
- GET `/clients/services/` — —
|
||||
- PATCH `/clients/services/` — —
|
||||
- PATCH `/clients/services/` — —
|
||||
- PATCH `/clients/services/` — —
|
||||
- PATCH `/clients/services/` — —
|
||||
- DELETE `/clients/services/` — —
|
||||
|
||||
## Service Change Requests
|
||||
|
||||
- GET `/service-change-requests/` — —
|
||||
- DELETE `/service-change-requests/` — —
|
||||
|
||||
## Prepaid Service Periods
|
||||
|
||||
- GET `/prepaid-service-periods/` — —
|
||||
- DELETE `/prepaid-service-periods/` — —
|
||||
|
||||
## Surcharges
|
||||
|
||||
- GET `/surcharges` — —
|
||||
- POST `/surcharges` — —
|
||||
- PATCH `/surcharges/` — —
|
||||
- GET `/surcharges/` — —
|
||||
- DELETE `/surcharges/` — —
|
||||
|
||||
## Taxes
|
||||
|
||||
- GET `/taxes` — —
|
||||
- POST `/taxes` — —
|
||||
- GET `/taxes` — —
|
||||
- DELETE `/taxes` — —
|
||||
|
||||
## Tickets
|
||||
|
||||
- GET `/ticketing/tickets/` — —
|
||||
- PATCH `/ticketing/tickets/` — —
|
||||
- DELETE `/ticketing/tickets/` — —
|
||||
|
||||
## Ticket Groups
|
||||
|
||||
- GET `/ticketing/ticket-groups` — —
|
||||
- POST `/ticketing/ticket-groups` — —
|
||||
- GET `/ticketing/ticket-groups/` — —
|
||||
- PATCH `/ticketing/ticket-groups/` — —
|
||||
- DELETE `/ticketing/ticket-groups/` — —
|
||||
|
||||
## Ticket Tags
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
- POST `/ticket-tags` — —
|
||||
- PATCH `/ticket-tags` — —
|
||||
- GET `/ticket-tags` — —
|
||||
- DELETE `/ticket-tags` — —
|
||||
|
||||
## Client Zone Credit Cards
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
- DELETE `/ticket-tags` — —
|
||||
- PATCH `/ticket-tags` — —
|
||||
|
||||
## Client Zone Invoices
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Payments
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Quotes
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Services
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Service Plans
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
- GET `/ticket-tags` — —
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Tickets
|
||||
|
||||
- POST `/ticket-tags` — —
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Tickets Activities
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Tickets Comments
|
||||
|
||||
- POST `/ticket-tags` — —
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Tickets Comments Attachments
|
||||
|
||||
- GET `/ticket-tags` — —
|
||||
|
||||
## Client Zone Service Change Requests
|
||||
|
||||
- GET `/client-zone/service-change-requests/` — —
|
||||
- DELETE `/client-zone/service-change-requests/` — —
|
||||
|
||||
## Client Zone Prepaid Service Periods
|
||||
|
||||
- GET `/client-zone/prepaid-service-periods/` — —
|
||||
662
INDEX_api_unms.md
Executable file
662
INDEX_api_unms.md
Executable file
@ -0,0 +1,662 @@
|
||||
# Índice API UNMS (UISP) — NMS v2.1
|
||||
|
||||
> **Generado automáticamente** desde `unms-swagger.json`
|
||||
> Ejecutar `php .agent/scripts/gen_index_unms_swagger.php` para regenerar.
|
||||
|
||||
**Host:** `` **Base Path:** `/nms/api/v2.1`
|
||||
**Versión API:** 1.5.0
|
||||
|
||||
---
|
||||
|
||||
## Resumen de Grupos
|
||||
|
||||
- **Authorization** (33 endpoints) — Login and user authorization.
|
||||
- **Backups** (13 endpoints) — UISP manual and automatic backups.
|
||||
- **CRM** (5 endpoints) — Customer relationship management.
|
||||
- **Data Links** (7 endpoints) — Data Links between devices.
|
||||
- **Devices** (326 endpoints) — Configuration and monitoring for all devices.
|
||||
- **Discovery** (6 endpoints) — Scan for devices and connect them to UISP.
|
||||
- **Export** (1 endpoints) — Client data export.
|
||||
- **Firmware** (5 endpoints) — Manage firmware files in UISP.
|
||||
- **Gateways** (6 endpoints) — Setup network device gateways.
|
||||
- **Logs** (2 endpoints) — View devices log lines.
|
||||
- **Outages** (1 endpoints) — View devices outages.
|
||||
- **Server** (56 endpoints) — UISP settings with SMTP configuration, SSL and backups configuration.
|
||||
- **Sites** (39 endpoints) — Manage sites and clients (former endpoints), their pictures and network structure.
|
||||
- **Speed Test** (6 endpoints) — Speedtest between device to device or device to internet.
|
||||
- **Tasks** (8 endpoints) — View, start or cancel UISP background tasks, for example firmware upgrade.
|
||||
- **Token** (4 endpoints) — Manage access tokens.
|
||||
- **Traffic** (12 endpoints) — Devices and clients traffic statistics generated thanks to NetFlow.
|
||||
- **Users** (21 endpoints) — Manage UISP users and their profiles.
|
||||
- **Vault** (9 endpoints) — Device credentials vault.
|
||||
|
||||
**Total: 560 endpoints en 19 grupos**
|
||||
|
||||
---
|
||||
|
||||
## Authorization — _Login and user authorization._
|
||||
|
||||
- GET `/nms/account/login/ubiquiti` [code?, state?, error?, error_description?, deviceUrl?, deviceName?, token?] — SSO authentication response endpoint. → 200/302/400/401/403/404/500
|
||||
- GET `/user` — Get the authenticated user. → 200/401/403/500
|
||||
- PUT `/user` — Updates authenticated user. → 200/400/401/403/500
|
||||
- GET `/access-groups/sites` [groupId?, withInternal?, withOverview?] — Return site access groups. → 200/401/403/500
|
||||
- POST `/access-groups/sites` — Create new site access group. → 200/400/401/403/404/500
|
||||
- GET `/user/totpauth` — Gets new information for two factor authentication. → 200/401/403/500
|
||||
- PUT `/user/totpauth` — Sets two factor authentication for user. → 200/400/401/403/500
|
||||
- GET `/access-groups/sites/{groupId}` [groupId] — Get site access group. → 200/400/401/403/404/500
|
||||
- PUT `/access-groups/sites/{groupId}` [groupId] — Update site access group. → 200/400/401/403/404/409/500
|
||||
- DELETE `/access-groups/sites/{groupId}` [groupId] — Delete site access group. → 200/400/401/403/404/409/500
|
||||
- GET `/user/offline-passwords/requested` — Show status if offline passwords request is present for user. → 200/401/403/404/500
|
||||
- POST `/user/check-credentials` — Check user credentials. → 200/400/401/403/500
|
||||
- POST `/user/check-session` — Check that the session token and cookie are valid. → 200/401/403/500
|
||||
- POST `/user/last-release-notes-seen` — Updates authenticated last release notes seen version. → 200/400/401/403/500
|
||||
- POST `/user/login` — Login. → 200/201/400/401/500
|
||||
- POST `/user/logout` — Logout. → 200/401/403/500
|
||||
- POST `/user/offline-passwords` — Generates random offline passwords for user. → 200/401/403/500
|
||||
- POST `/nms/info/login` — Authenticate client and return device access token → 200/400/403/405/500
|
||||
- POST `/token/ubiquiti/exchange` — Exchange of SSO token to x-auth-token. → 200/400/401/403/500
|
||||
- POST `/user/login/totpauth` — Two Factor Authentication login step 2. → 200/400/500
|
||||
- POST `/user/login/ubiquiti` — Login using Ubiquiti SSO. → 200/400/401/500
|
||||
- POST `/user/offline-passwords/confirm` — Confirms request to generate random offline passwords for authenticated user. → 200/401/403/404/500
|
||||
- POST `/user/password/requestreset` — Request password reset. → 200/400/403/500
|
||||
- POST `/user/password/reset` — Reset user password. → 200/400/401/500
|
||||
- POST `/user/password/strength` — Check password strength. → 200/400/500
|
||||
- POST `/user/sso/verify` — Verify SSO login result. → 200/400/401/403/500
|
||||
- POST `/user/login/invite/local` — Finish user invitation using local password. → 200/400/401/500
|
||||
- POST `/user/login/invite/ubiquiti` — Finish user invitation using Ubiquiti SSO. → 200/400/401/500
|
||||
- PUT `/user/disabledtotpauth` — Disable two factor authorization for user. → 200/400/401/403/500
|
||||
- PUT `/user/preferences` — Updates authenticated user's preferences. → 200/400/401/403/500
|
||||
- PUT `/user/sso/enable` — Enable Ubiquiti SSO for current user. → 200/400/401/403/500
|
||||
- PUT `/access-groups/sites/{groupId}/{siteId}` [groupId, siteId] — Add or change access to single site. → 200/400/401/403/404/409/500
|
||||
- DELETE `/access-groups/sites/{groupId}/{siteId}` [groupId, siteId] — Remove access to single site. → 200/400/401/403/404/409/500
|
||||
|
||||
## Backups — _UISP manual and automatic backups._
|
||||
|
||||
- GET `/nms/backups` — Get UISP backups. → 200/400/401/403/500
|
||||
- GET `/nms/backups/{backupId}` [backupId] — Get UISP backup file. → 400/401/403/404/500
|
||||
- DELETE `/nms/backups/{backupId}` [backupId] — Delete UISP backup. → 200/400/401/403/404/500
|
||||
- GET `/nms/downloads/{token}` — Download UISP backups. → 400/404/500
|
||||
- GET `/nms/backups/{backupId}/download-token` [backupId] — Get temporary download token for this backup. → 400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}/tools/unms-controller/backups` [id] — Returns list of backups on the microSD and sync status. → 200/400/401/404/409/500
|
||||
- GET `/devices/uisprs/{id}/tools/unms-controller/backup-storage` [id] — Get details about the storage. → 200/400/401/404/500
|
||||
- GET `/devices/uisprs/{id}/tools/unms-controller/backup-storage/format` [id] — Get status of microSD card format. → 200/400/401/404/500
|
||||
- POST `/devices/uisprs/{id}/tools/unms-controller/backup-storage/format` [id] — Format the microSD card. → 200/400/401/404/409/500
|
||||
- POST `/nms/backups/create` — Create UISP backup. → 200/400/401/403/404/500
|
||||
- POST `/nms/backups/restore/set-user-password` — Restore UISP backup. → 200/400/401/403/404/500
|
||||
- POST `/nms/backups/{backupId}/restore` [backupId] — Restore UISP backup. → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/tools/unms-controller/backup-storage/detach` [id] — Unmount the microSD card. → 200/400/401/404/409/500
|
||||
|
||||
## CRM — _Customer relationship management._
|
||||
|
||||
- GET `/crm/roles` — Get CRM admin users. → 200/401/403/500/503
|
||||
- GET `/crm/service-plans` — Get CRM service plans. → 200/401/403/500/503
|
||||
- GET `/crm/{ucrmClientId}/tickets` [ucrmClientId, limit?, offset?, order?, direction?] — List of CRM tickets belonging to the subscriber (CRM client). → 200/401/403/409/500/503
|
||||
- POST `/crm/clients` — Create CRM client. → 200/401/403/409/500/503
|
||||
- PATCH `/crm/service-change-requests/{requestId}/accept` [requestId] — Accept service change request in CRM. → 200/401/403/409/500/503
|
||||
|
||||
## Data Links — _Data Links between devices._
|
||||
|
||||
- GET `/data-links` [siteLinksOnly?] — List of all data links. → 200/400/401/403/500
|
||||
- POST `/data-links` — Create data link. → 200/401/403/409/500
|
||||
- GET `/data-links/{id}` [id] — Get data link based on its ID. → 200/400/401/403/404/500
|
||||
- PUT `/data-links/{id}` [id] — Update data link. → 200/400/401/403/409/500
|
||||
- DELETE `/data-links/{id}` [id] — Delete data link based on its ID. → 200/400/401/403/404/500
|
||||
- GET `/data-links/device/{id}` [id] — Get data link based on device ID. → 200/400/401/403/404/500
|
||||
- GET `/data-links/sites/{siteId}` [siteId] — List of data links based siteId → 200/400/401/403/404/500
|
||||
|
||||
## Devices — _Configuration and monitoring for all devices._
|
||||
|
||||
- GET `/devices` [siteId?, withInterfaces?, authorized?, type?, role?] — List of all devices in UISP. → 200/400/401/403/500
|
||||
- GET `/devices/discovered` — List of discovered devices. → 200/400/401/403/500
|
||||
- GET `/devices/images` — Returns list of all devices model image. → 200/401/403/404/500
|
||||
- GET `/devices/ips` [suspended?, management?, siteId?, ucrmId?, includeObsolete?] — Return a list of devices IPs which are in UISP monitored IP ranges. → 200/400/401/403/500
|
||||
- GET `/devices/login` [mac?, ip?] — Create device ticket to login via controller → 200/400/401/403/500
|
||||
- GET `/devices/macs` — Return a list of all devices MAC address (except for ONUs). → 200/400/401/403/500
|
||||
- GET `/devices/onus` [parentId?] — List of ONU in UISP. → 200/400/401/403/404/500
|
||||
- GET `/devices/ssids` — Get devices wireless configuration → 200/401/403/500
|
||||
- GET `/devices/unknown` [minTraffic?] — Get unknown client devices (based on detected network traffic) ordered by total traffic. → 200/400/401/403/500
|
||||
- GET `/devices/{id}` [id] — Device status overview. → 200/400/401/403/500
|
||||
- DELETE `/devices/{id}` [id] — Delete device. → 200/400/401/403/500
|
||||
- GET `/airlink/proxy/airlink-be` [lat1, lon1, lat2, lon2, samples?] — Get elevation data between two points. → 200/400/401/403/500/502
|
||||
- GET `/airlink/proxy/countries-map.geo.json` [version?] — Get countries map data file. → 400/401/403/500/502
|
||||
- GET `/airlink/proxy/elevation` [file, version?] — Get elevation data file. → 400/401/403/500/502
|
||||
- GET `/airlink/proxy/lidar-boundaries.geojson` [version?] — Get lidar boundaries data file. → 400/401/403/500/502
|
||||
- GET `/airlink/proxy/rainfall-average.map` [version?] — Get average rainfall map data file. → 400/401/403/500/502
|
||||
- GET `/airlink/proxy/rainfall-r001.map` [version?] — Get rainfall map data file. → 400/401/403/500/502
|
||||
- GET `/devices/aircubes/{id}` [id, withStations?] — Return AirCube detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/airfibers/{id}` [id, withStations?] — Return AirFiber detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/airmaxes/{id}` [id, withStations?] — Return AirMax detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/aps/profiles` — List of all access points and their connection profiles. → 200/400/401/403/500
|
||||
- GET `/devices/blackboxes/{id}` [id] — Return Blackbox device. → 200/400/401/403/404/500
|
||||
- GET `/devices/discovered/head` — First of discovered devices without BlackBox and Express. → 200/400/401/403/500
|
||||
- GET `/devices/epowers/{id}` [id] — Get epower device. → default
|
||||
- GET `/devices/erouters/{id}` [id] — Return EdgeRouter detail. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/detail] → 200/400/401/403/404/500
|
||||
- GET `/devices/eswitches/{id}` [id] — Return EdgeSwitch detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/import/status` — Status of devices import process. → 200/401/403/500
|
||||
- GET `/devices/mac/{mac}` [mac] — Get info on device by mac address. → 200/400/401/403/404/500
|
||||
- GET `/devices/olts/{id}` [id] — Return OLT detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/onus/{id}` [id] — Return ONU detail. → 200/400/401/403/500
|
||||
- GET `/devices/remoteuiproxy/sessions` — Get valid remote-ui-proxy session tokens. → 200/401/403/404/500
|
||||
- GET `/devices/solarbeams/{id}` [id] — Return SolarBeam detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/top/active` [count?] — Get most active devices → 200/400/401/403/404/500
|
||||
- GET `/devices/toughswitches/{id}` [id] — Return ToughSwitch detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}` [id] — Return UISPRLXC detail. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/detail] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}` [id] — Return UISPR detail. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/detail] → 200/400/401/403/404/500
|
||||
- GET `/devices/uispss/{id}` [id] — Return UISP Switch detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/waves/{id}` [id, withStations?] — Return Wave detail. → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceId}/interfaces` [deviceId] — Return list of device interfaces. → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceType}/configuration-history` [deviceType] — Get all saved history of configurations by device type. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/operators` [id] — Return list of available operators [UISP-LTE] → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceId}/mac-table-refresh` [deviceId] — Fetch mac table from device and save to UISP. [EdgeSwitch, EdgeRouter, AirMax, UISPRouter, UISPSwitch, AirFiber, AirCube] → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/capabilities` [id] — Device supported features lists. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/system` [id] — Device system configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/system` [id, isComposeRequest?] — Update a device's system settings. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/statistics` [id, interval, start, period] — Return device statistics. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/detail` [id, withStations?] — Get device detail with interfaces and/or stations. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/supportfile` [id] — Get device's support file. → 401/403/404/500
|
||||
- GET `/devices/{id}/configuration` [id] — Get device system and services configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/configuration` [id, isComposeRequest?] — Update device system and services configuration. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/services` [id, payload?] — Device services. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/services` [id] — Update device services. → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceId}/location` [deviceId] — Return location of the device. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{deviceId}/location` [deviceId] — Update location of the device. → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceId}/mac-table` [deviceId, sortBy?, sortDesc?, count?, page?, search?] — Return mac table of device. [EdgeSwitch, EdgeRouter, AirMax, UISPRouter, UISPSwitch, AirFiber, AirCube] → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/vlans` [id] — Get device's VLANs. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/vlans` [id, isComposeRequest?] — Update device's VLANs. → 200/400/401/403/500
|
||||
- GET `/devices/{id}/netflow` [id, interfaceId?] — Get information on device's flow-accounting status. → 200/400/401/404
|
||||
- GET `/devices/{id}/clients` [id] — Return a devices' connected devices [UISP-LTE] → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/perform-default-login` [id] — Try connecting device using factory default credentials. → 200/400/401/403/500
|
||||
- GET `/devices/{id}/remoteuiproxy` [id] — Get initial remote UI. → 401/403/404/500
|
||||
- GET `/devices/{deviceId}/backups` [deviceId] — Return list of device backups. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/backups` [deviceId] — Create new device backup. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{deviceId}/backups` [deviceId] — Upload device backup. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/network` [id] — Get AirCube network configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/aircubes/{id}/network` [id] — Update AirCube network configuration. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/frequency-lists` [id] — Return AirCube frequency lists. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/stations` [id] — Return AirCube station list. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/tx-power-lists` [id] — Return AirCube tx power lists. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/wireless` [id] — Get AirCube wireless config → 200/400/401/403/404/500
|
||||
- PUT `/devices/aircubes/{id}/wireless` [id] — Update AirCube wireless configuration. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/system` [id] — Get AirCube system configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/aircubes/{id}/system` [id] — Update AirCube system configuration. → 200/400/401/403/404/500
|
||||
- GET `/devices/airfibers/{id}/stations` [id] — Return AirFiber station list. → 200/400/401/403/404/500
|
||||
- GET `/devices/airmaxes/{id}/system` [id] — Get AirMax system config. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- GET `/devices/airmaxes/{id}/stations` [id] — Return AirMax station list. → 200/400/401/403/404/500
|
||||
- GET `/devices/airmaxes/{id}/site-survey` [id] — Scan sites. → 200/400/401/403/404/500
|
||||
- GET `/devices/airmaxes/{id}/frequency-bands` [id] — Get AirMax stations frequency bands. → 200/400/401/403/404/500
|
||||
- GET `/devices/airos/{id}/regdomain` [id, countryCode?] — Get device regulatory domain information for configured or provided via query parameter country). → 200/400/401/403/404/500
|
||||
- GET `/devices/airos/{id}/configuration` [id] — Get airOS device configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/airos/{id}/configuration` [id, isComposeRequest?, isLinkConfiguration?] — Update airOs device configuration. → 200/400/401/403/404/500
|
||||
- GET `/devices/airos/{id}/countries` [id] — Get list of available countries on device. → 200/400/401/403/404/500
|
||||
- GET `/devices/blackboxes/{id}/config` [id] — Return BlackBox device config. → 200/400/401/403/404/500
|
||||
- PUT `/devices/blackboxes/{id}/config` [id] — Update BlackBox device config. → 200/400/401/403/404/409/500
|
||||
- GET `/devices/erouters/{id}/netflow` [id, interfaceId?] — Get information on device's flow-accounting status. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/netflow] → 200/400/401/404/500
|
||||
- GET `/devices/eswitches/{id}/system` [id] — Get EdgeSwitch system configuration. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- GET `/devices/express/{id}/refresh` [id] — Refresh Express data → 200/400/401/403/404/500
|
||||
- GET `/devices/onus/{id}/network` [id] — Get an Onu's network settings. [Deprecated in favor of route "/nms/api/v2.1/devices/get-configuration"] → 200/400/401/403/404/500
|
||||
- PUT `/devices/onus/{id}/network` [id] — Update an Onu's network settings. [Deprecated in favor of route "/nms/api/v2.1/devices/update-configuration"] → 200/400/401/403/404/500
|
||||
- GET `/devices/onus/{id}/services` [id] — Get an Onu's services. [Deprecated in favor of route POST "/nms/api/v2.1/devices/get-configuration"] → 200/400/401/403/404/500
|
||||
- GET `/devices/onus/{id}/clients` [id] — Get an Onu's connected devices MAC addressees. → 200/400/401/403/500
|
||||
- GET `/devices/onus/{id}/wireless` [id] — Get an Onu's wireless settings. [Deprecated in favor of route "/nms/api/v2.1/devices/get-configuration"] → 200/400/401/403/404/500/501
|
||||
- PUT `/devices/onus/{id}/wireless` [id] — Update an Onu's wireless settings. [Deprecated in favor of route "/nms/api/v2.1/devices/update-configuration"] → 200/400/401/403/404/500
|
||||
- GET `/devices/onus/{id}/vlans` [id] — Deprecated. Get list of OLT's VLANs. → 200/400/401/403/404/500/501
|
||||
- GET `/devices/toughswitches/{id}/system` [id] — Get ToughSwitch system configuration. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/netflow` [id, interfaceId?] — Get information on device's flow-accounting status. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/netflow] → 200/400/401/404/500
|
||||
- GET `/devices/uisprs/{id}/netflow` [id, interfaceId?] — Get information on device's flow-accounting status. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/netflow] → 200/400/401/404/500
|
||||
- GET `/devices/uispss/{id}/system` [id] — Get UISP Switch system configuration. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- GET `/devices/waves/{id}/stations` [id] — Return Wave station list. → 200/400/401/403/404/500
|
||||
- GET `/devices/wifi-router/{id}/configuration` [id] — Returns configuration of wifiRouter → 200/400/401/403/404/500
|
||||
- PUT `/devices/wifi-router/{id}/configuration` [id] — Update configuration of wifiRouter → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceId}/speed-tests/{testId}` [deviceId, testId] — Return detail about running speedtest. → 200/401/403/422/500
|
||||
- DELETE `/devices/{deviceId}/speed-tests/{testId}` [deviceId, testId] — Stop speed test. → 200/400/401/403/404/422/500
|
||||
- GET `/devices/{deviceId}/interfaces/{interfaceName}` [deviceId, interfaceName] — Get interface configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{deviceId}/interfaces/{interfaceName}` [deviceId, interfaceName, isComposeRequest?] — Update interface configuration. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/{deviceId}/interfaces/{interfaceName}` [deviceId, interfaceName] — Delete interface. → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceId}/backups/{backupId}` [deviceId, backupId, replaceUnmsKey?] — Return device configuration backup file. → 400/401/403/404/500
|
||||
- PUT `/devices/{deviceId}/backups/{backupId}` [deviceId, backupId] — Update backup. → 400/401/403/404/500
|
||||
- DELETE `/devices/{deviceId}/backups/{backupId}` [deviceId, backupId] — Delete backup. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/dhcp-details/{interface}` [id, interface] — Returns details of DHCP IP address - gateway and DNS servers. → 200/400/401/403/500
|
||||
- GET `/devices/{id}/router/routes` [id] — Get all routes. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/router/routes` [id] — Create new static route. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/router/routes` [id] — Edit static route. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/system/unms` [id] — Device specific UISP settings. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/system/unms` [id] — Update device specific UISP settings. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/router/ospf` [id] — Get OSPF configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/router/ospf` [id] — Update OSPF configuration. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/remoteuiproxy/init` [id] — Get device remote ui proxy token. → 401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/config/system` [id] — Get AirCube system config. This API endpoint is DEPRECATED.
|
||||
Please refer to '/nms/api/v2.1/devices/aircubes/{id}/system' instead. → 200/400/401/403/404/500
|
||||
- PUT `/devices/aircubes/{id}/config/system` [id] — Update AirCube system settings. This API endpoint is DEPRECATED.
|
||||
Please refer to '/nms/api/v2.1/devices/aircubes/{id}/system' instead. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/config/wireless` [id] — Get AirCube wireless config. This API endpoint is DEPRECATED.
|
||||
Please refer to '/nms/api/v2.1/devices/aircubes/{id}/wireless' instead. → 200/400/401/403/404/500
|
||||
- PUT `/devices/aircubes/{id}/config/wireless` [id] — Update AirCube wireless config. This API endpoint is DEPRECATED.
|
||||
Please refer to '/nms/api/v2.1/devices/aircubes/{id}/wireless' instead. → 200/400/401/403/404/500
|
||||
- GET `/devices/aircubes/{id}/config/network` [id] — Get AirCube network config. This API endpoint is DEPRECATED.
|
||||
Please refer to '/nms/api/v2.1/devices/aircubes/{id}/network' instead. → 200/400/401/403/404/500
|
||||
- PUT `/devices/aircubes/{id}/config/network` [id] — Update AirCube network config. This API endpoint is DEPRECATED.
|
||||
Please refer to '/nms/api/v2.1/devices/aircubes/{id}/network' instead. → 200/400/401/403/404/500
|
||||
- GET `/devices/airmaxes/{id}/config/wireless` [id] — Get AirMax wireless config. → 200/400/401/403/404/500
|
||||
- PUT `/devices/airmaxes/{id}/config/wireless` [id] — Update AirMax wireless config. → 200/400/401/403/404/500
|
||||
- GET `/devices/epowers/{id}/configuration/power` [id] — Get epower configuration: [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/system/configuration/power] → default
|
||||
- GET `/devices/erouters/{id}/dhcp/leases` [id] — DHCP IP address leases. → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/dhcp/leases` [id] — Create DHCP IP address lease. → 200/400/401/403/404/500
|
||||
- GET `/devices/erouters/{id}/dhcp/servers` [id] — Device DHCP servers. → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/dhcp/servers` [id] — Create new DHCP server. → 200/400/401/403/404/500
|
||||
- GET `/devices/erouters/{id}/router/routes` [id] — Get all routes. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/router/routes` [id] — Create new static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- PUT `/devices/erouters/{id}/router/routes` [id] — Edit static route. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- GET `/devices/solarbeams/{id}/config/system` [id] — Get SolarBeam system configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/solarbeams/{id}/config/system` [id] — Update SolarBeam system settings. → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/firewall/sets` [id] — Get information on device's firewall sets. → 200/400/401/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/firewall/mangles` [id] — Get information on device's firewall mangles chains. → 200/400/401/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/router/routes` [id] — Get all routes. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/router/routes` [id] — Create new static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/router/routes` [id] — Edit static route. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/dhcp/servers` [id] — Device DHCP servers. → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/dhcp/servers` [id] — Create new DHCP server. → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/dhcp/leases` [id] — DHCP IP address leases. → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/dhcp/leases` [id] — Create DHCP IP address lease. → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/firewall/filters` [id] — Get information on device's firewall filter chains. → 200/400/401/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/firewall/nats` [id] — Get information on device's firewall NATs. → 200/400/401/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/firewall/settings` [id] — Get information on device's firewall settings. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/firewall/settings` [id] — Set firewall settings on the device. → 200/400/401/404/500
|
||||
- GET `/devices/uisprs/{id}/firewall/sets` [id] — Get information on device's firewall sets. → 200/400/401/404/500
|
||||
- GET `/devices/uisprs/{id}/firewall/mangles` [id] — Get information on device's firewall mangles chains. → 200/400/401/404/500
|
||||
- GET `/devices/uisprs/{id}/router/routes` [id] — Get all routes. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/router/routes` [id] — Create new static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprs/{id}/router/routes` [id] — Edit static route. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/router/routes] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}/dhcp/servers` [id] — Device DHCP servers. → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/dhcp/servers` [id] — Create new DHCP server. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}/dhcp/leases` [id] — DHCP IP address leases. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/dhcp/leases` [id] — Create DHCP IP address lease. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}/firewall/filters` [id] — Get information on device's firewall filter chains. → 200/400/401/404/500
|
||||
- GET `/devices/uisprs/{id}/firewall/nats` [id] — Get information on device's firewall NATs. → 200/400/401/404/500
|
||||
- GET `/devices/uisprs/{id}/firewall/settings` [id] — Get information on device's firewall settings. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprs/{id}/firewall/settings` [id] — Set firewall settings on the device. → 200/400/401/404/500
|
||||
- GET `/devices/{id}/remoteuiproxy/resources/{path*}` [id, path?] — Load local UI resources → 401/403/404/500
|
||||
- POST `/devices/{id}/remoteuiproxy/resources/{path*}` [id, path?] — Update local UI → 401/403/404/500
|
||||
- PUT `/devices/{id}/remoteuiproxy/resources/{path*}` [id, path?] — Update local UI → 401/403/404/500
|
||||
- DELETE `/devices/{id}/remoteuiproxy/resources/{path*}` [id, path?] — Update local UI → 401/403/404/500
|
||||
- PATCH `/devices/{id}/remoteuiproxy/resources/{path*}` [id, path?] — Update local UI → 401/403/404/500
|
||||
- GET `/devices/{id}/system/configuration/power` [id] — Get configuration - power. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/system/configuration/power` [id] — Set configuration - power. → 200/400/401/403/404/500
|
||||
- GET `/devices/{id}/router/ospf/areas` [id] — All OSPF areas. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/router/ospf/areas` [id] — Create new OSPF area. → 200/400/401/403/404/500
|
||||
- GET `/devices/{deviceId}/udapi/{version}/{udapiUrl*}` [deviceId, version, udapiUrl] — Udapi device request. → 200/400/401/403/404/500/501
|
||||
- POST `/devices/{deviceId}/udapi/{version}/{udapiUrl*}` [deviceId, version, udapiUrl] — Udapi device request. → 200/400/401/403/404/500/501
|
||||
- PUT `/devices/{deviceId}/udapi/{version}/{udapiUrl*}` [deviceId, version, udapiUrl] — Udapi device request. → 200/400/401/403/404/500/501
|
||||
- PATCH `/devices/{deviceId}/udapi/{version}/{udapiUrl*}` [deviceId, version, udapiUrl] — Udapi device request. → 200/400/401/403/404/500/501
|
||||
- DELETE `/devices/{deviceId}/udapi/{version}/{udapiUrl*}` [deviceId, version, udapiUrl] — Udapi device request. → 200/400/401/403/404/500/501
|
||||
- GET `/devices/{deviceId}/interfaces/data-link/available` [deviceId, dataLinkId?] — Return list of interfaces available to create data link. → 200/400/401/403/404/500
|
||||
- GET `/devices/erouters/{id}/router/ospf/areas` [id] — All OSPF areas. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/router/ospf/areas] → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/router/ospf/areas` [id] — Create new OSPF area. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/ospf/areas] → 200/400/401/403/404/500
|
||||
- GET `/devices/erouters/{id}/dhcp/servers/{serverName}` [id, serverName] — Get DHCP server configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/erouters/{id}/dhcp/servers/{serverName}` [id, serverName] — Update DHCP server configuration. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/erouters/{id}/dhcp/servers/{serverName}` [id, serverName] — Delete DHCP server. → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/dhcp/servers/{serverName}` [id, serverName] — Get DHCP server configuration. → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/dhcp/servers/{serverName}` [id, serverName] — Update DHCP server configuration. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/dhcp/servers/{serverName}` [id, serverName] — Delete DHCP server. → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/router/ospf/areas` [id] — All OSPF areas. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/router/ospf/areas] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/router/ospf/areas` [id] — Create new OSPF area. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/ospf/areas] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprlxcs/{id}/router/ospf/interfaces` [id] — All OSPF Interface. [Deprecated in favor of route GET /nms/api/v2.1/devices/{deviceId}/interfaces] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}/dhcp/servers/{serverName}` [id, serverName] — Get DHCP server configuration. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprs/{id}/dhcp/servers/{serverName}` [id, serverName] — Update DHCP server configuration. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprs/{id}/dhcp/servers/{serverName}` [id, serverName] — Delete DHCP server. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}/router/ospf/areas` [id] — All OSPF areas. [Deprecated in favor of route GET /nms/api/v2.1/devices/{id}/router/ospf/areas] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/router/ospf/areas` [id] — Create new OSPF area. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/ospf/areas] → 200/400/401/403/404/500
|
||||
- GET `/devices/uisprs/{id}/router/ospf/interfaces` [id] — All OSPF Interface. [Deprecated in favor of route GET /nms/api/v2.1/devices/{deviceId}/interfaces] → 200/400/401/403/404/500
|
||||
- POST `/devices/authorize` — Authorize single or multiple devices. → 200/400/401/403/500
|
||||
- POST `/devices/backups` — Create and download a new multi device backup. → 400/401/403/500
|
||||
- POST `/devices/bulkdelete` — Delete devices. → 200/400/401/403/500
|
||||
- POST `/devices/bulkrestart` — Restart devices, devices action reboot. → 200/400/401/403/500
|
||||
- POST `/devices/endpoints-for-parallel-firmware-upgrade` — Returns endpoint devices for parallel firmware upgrade in groups. → 200/400/401/403/500
|
||||
- POST `/devices/get-configuration` — Get multiple devices configuration. → 200/400/401/403/404/500
|
||||
- POST `/devices/import` —
|
||||
Complex devices import which internally use discovery and creates blackboxes for unreachable and non-UBNT devices.
|
||||
→ 200/400/401/403/404/500
|
||||
- POST `/devices/merge` — Merge two devices. → 200/400/401/403/500
|
||||
- POST `/devices/preregistration` — Create a preregistered device, sort of a "placeholder" for a device to be adopted in the future → 200/400/401/403/500
|
||||
- POST `/devices/refresh` — Reset devices specific AES keys to universal AES keys. → 200/400/401/403/500
|
||||
- POST `/devices/update-configuration` — Update multiple devices and/or onus configuration. → 200/400/401/403/404/500
|
||||
- POST `/devices/airmaxes/stations` — Returns AirMax stations for AirMax access points. → 200/400/401/403/500
|
||||
- POST `/devices/blackboxes/config` — Create new BlackBox device config. → 201/400/401/403/404/409/500
|
||||
- POST `/devices/configuration-history/delete` — Delete saved history of configurations by configuration IDs. → 200/400/401/403/404/500
|
||||
- POST `/devices/connect/other` — Find and connect other device using IP address → 200/400/401/404/500
|
||||
- POST `/devices/connect/ubnt` — Find and connect UBNT device using IP and provided credentials. → 200/400/401/500
|
||||
- POST `/devices/maintenance/disable` — Disable maintenance mode on one or more devices. → 200/401/404/500
|
||||
- POST `/devices/maintenance/enable` — Enable maintenance mode on one or more devices. → 200/401/404/500
|
||||
- POST `/devices/{deviceId}/switch-static-to-dhcp-ip` [deviceId] — Switch static IP address to DHCP - gateway (route), DNS servers (system) and interface. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/unassign` [id] — Unassign device. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/refresh` [id] — Reset device specific AES key to universal AES key. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/debug` [id] — Enable debug logging. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/authorize` [id] — Authorize device. [Deprecated in favor of route POST /nms/api/v2.1/devices/authorize] → 200/400/401/403/500
|
||||
- POST `/devices/{id}/restart` [id] — Restart device, device action reboot. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/upgrade-to-latest` [id] —
|
||||
Upgrade device FW to the latest FW version in UISP. It's possible to track upgrade process via /tasks API.
|
||||
→ 200/400/401/403/404/422/500
|
||||
- POST `/devices/{id}/update` [id] — Set update of device to realtime profile. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/reset-link-score` [id] — Reset link score. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/switch-dhcp-to-static-ip` [id] — Switch DHCP to static IP address - gateway (route), DNS servers (system) and interface. → 200/400/401/403/500
|
||||
- POST `/devices/onus/{id}/update` [id] — Set update of ONU to realtime detail. → 200/400/401/403/404/500
|
||||
- POST `/devices/system/unms/hostname` — Update devices UISP Key hostname and port. → 200/400/401/403/404/422/500
|
||||
- POST `/devices/system/unms/key` — Update devices UISP Key. → 200/400/401/403/404/422/500
|
||||
- POST `/devices/{deviceId}/speed-tests/start` [deviceId] — Execute speed test on device [device to internet, device to device]. → 200/400/401/403/422/500
|
||||
- POST `/devices/{deviceId}/interfaces/lag` [deviceId] — Create new Lag interface. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/vlan` [deviceId] — Create new VLAN interface. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/delete-with-ip-transfer` [deviceId] — Delete interface and transfer its IP configuration to another interface. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/system/reset-total-power` [id] — Reset total power consumption. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/system/power-cycle` [id] — Turns off selected output for a short time. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/disablelag` [deviceId] — Disable link aggregation. → 200/400/401/403/404/500/501
|
||||
- POST `/devices/{id}/locate/stop` [id] — Stop locating indication on device. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/iplink/redirect` [id] — Perform device authorization before opening a devices UI. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/locate/start` [id] — Start locating indication on device. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/enablelag` [deviceId] — Enable link aggregation. → 200/400/401/403/404/500/501
|
||||
- POST `/devices/{id}/system/reset-battery-status` [id] — Reset battery status. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/bridge` [deviceId] — Create new Bridge interface. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/pppoe` [deviceId] — Create new PPPoE interface. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/{interfaceName}/reset` [deviceId, interfaceName] — Reset port. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/{interfaceName}/unblock` [deviceId, interfaceName] — Unblock interface. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/router/routes/unblock` [id] — Unblock static route. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/router/routes/delete` [id] — Delete route. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/{interfaceId}/resetstats` [deviceId, interfaceId] — Reset interface statistics. → 200/400/401/403/500
|
||||
- POST `/devices/{id}/router/routes/block` [id] — Block static route. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/tools/sms/send` [id] — Sends SMS. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/backups/{backupId}/apply` [deviceId, backupId] — Restore the device configuration from a backup file (followed by a device restart if necessary). → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/{interfaceName}/block` [deviceId, interfaceName] — Block interface. → 200/400/401/403/404/500
|
||||
- POST `/devices/{deviceId}/interfaces/{interfaceName}/cable-test` [deviceId, interfaceName] — Cable test port. → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/router/routes/block` [id] — Block static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/block] → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/router/routes/delete` [id] — Delete route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/delete] → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/router/routes/unblock` [id] — Unblock static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/unblock] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/firewall/sets/set` [id] — Create firewall set. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/firewall/sets/set` [id, isComposeRequest?] — Updates firewall set. → 200/400/401/404/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/firewall/sets/set` [id, name, type] — Deletes firewall set. → 200/400/401/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/router/routes/unblock` [id] — Unblock static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/unblock] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/router/routes/delete` [id] — Delete route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/delete] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/router/routes/block` [id] — Block static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/block] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/router/ospf/interface` [id] — Create new OSPF Interface. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{deviceId}/interfaces/{interfaceName}/ospf] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/router/ospf/interface` [id] — Update OSPF Interface. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{deviceId}/interfaces/{interfaceName}/ospf] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/firewall/nats/rule` [id] — Create firewall NAT rule. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/firewall/nats/rule` [id] — Updates firewall NAT rule. → 200/400/401/404/500
|
||||
- POST `/devices/uisprs/{id}/firewall/sets/set` [id] — Create firewall set. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprs/{id}/firewall/sets/set` [id, isComposeRequest?] — Updates firewall set. → 200/400/401/404/500
|
||||
- DELETE `/devices/uisprs/{id}/firewall/sets/set` [id, name, type, isComposeRequest?] — Deletes firewall set. → 200/400/401/404/500
|
||||
- POST `/devices/uisprs/{id}/router/routes/unblock` [id] — Unblock static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/unblock] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/router/routes/delete` [id] — Delete route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/delete] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/router/routes/block` [id] — Block static route. [Deprecated in favor of route POST /nms/api/v2.1/devices/{id}/router/routes/block] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/router/ospf/interface` [id] — Create new OSPF Interface. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{deviceId}/interfaces/{interfaceName}/ospf] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprs/{id}/router/ospf/interface` [id] — Update OSPF Interface. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{deviceId}/interfaces/{interfaceName}/ospf] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/firewall/nats/rule` [id, isComposeRequest?] — Create firewall NAT rule. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprs/{id}/firewall/nats/rule` [id, isComposeRequest?] — Updates firewall NAT rule. → 200/400/401/404/500
|
||||
- POST `/devices/{id}/system/unms/key/reachable` [id] — Checks if the UISP Key is reachable from the device. → 200/400/401/403/404/500
|
||||
- POST `/devices/{id}/system/unms/hostname/reachable` [id] — Checks if the hostname and port is reachable from the device. → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/dhcp/servers/{serverName}/block` [id, serverName] — Block DHCP server. → 200/400/401/403/404/500
|
||||
- POST `/devices/erouters/{id}/dhcp/servers/{serverName}/unblock` [id, serverName] — Unblock DHCP server. → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/firewall/mangles/{mangleName}/rule` [id, mangleName] — Create firewall mangles rule. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/firewall/mangles/{mangleName}/rule` [id, mangleName] — Updates firewall mangle rule. → 200/400/401/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/dhcp/servers/{serverName}/block` [id, serverName] — Block DHCP server. → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/dhcp/servers/{serverName}/unblock` [id, serverName] — Unblock DHCP server. → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprlxcs/{id}/firewall/filters/{filterName}/rule` [id, filterName, isComposeRequest?] — Create firewall filter rule. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/firewall/filters/{filterName}/rule` [id, filterName, isComposeRequest?] — Updates firewall filter rule. → 200/400/401/404/500
|
||||
- POST `/devices/uisprs/{id}/firewall/mangles/{mangleName}/rule` [id, mangleName] — Create firewall mangles rule. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprs/{id}/firewall/mangles/{mangleName}/rule` [id, mangleName] — Updates firewall mangle rule. → 200/400/401/404/500
|
||||
- POST `/devices/uisprs/{id}/dhcp/servers/{serverName}/block` [id, serverName] — Block DHCP server. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/dhcp/servers/{serverName}/unblock` [id, serverName] — Unblock DHCP server. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- POST `/devices/uisprs/{id}/firewall/filters/{filterName}/rule` [id, filterName, isComposeRequest?] — Create firewall filter rule. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprs/{id}/firewall/filters/{filterName}/rule` [id, filterName, isComposeRequest?] — Updates firewall filter rule. → 200/400/401/404/500
|
||||
- PUT `/devices/onus/{id}/system` [id] — Update an Onu's system settings. [Deprecated in favor of route "/nms/api/v2.1/devices/update-configuration"] → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/system/users` [id] — Update device system users. → 200/400/401/403/404/500
|
||||
- PUT `/devices/{id}/compose/confirm` [id, composeId] — Confirm compose request → 200/400/401/403/404/500
|
||||
- PUT `/devices/airmaxes/{id}/system/users` [id] — Update AirMax system users. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- PUT `/devices/eswitches/{id}/system/users` [id] — Update EdgeSwitch system users. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- PUT `/devices/toughswitches/{id}/system/users` [id] — Update ToughSwitch system users. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uispss/{id}/system/users` [id] — Update UISP Switch system users. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/system] → 200/400/401/403/404/500
|
||||
- PUT `/devices/{deviceId}/interfaces/{interfaceName}/ospf` [deviceId, interfaceName] — Set interface OSPF config. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/{deviceId}/interfaces/{interfaceName}/ospf` [deviceId, interfaceName] — Unset interface OSPF config. → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/firewall/filter/chain` [id, isComposeRequest?] — Update firewall filter chain. Useful for changing rules order. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/firewall/nats/rules` [id] — Updates firewall NAT rules. Useful for changing rules order. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprs/{id}/firewall/filter/chain` [id, isComposeRequest?] — Update firewall filter chain. Useful for changing rules order. → 200/400/401/404/500
|
||||
- PUT `/devices/uisprs/{id}/firewall/nats/rules` [id, isComposeRequest?] — Updates firewall NAT rules. Useful for changing rules order. → 200/400/401/404/500
|
||||
- PUT `/devices/{id}/router/ospf/areas/{areaId}` [id, areaId] — Update OSPF area. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/{id}/router/ospf/areas/{areaId}` [id, areaId] — Delete OSPF area. → 200/400/401/403/404/500
|
||||
- PUT `/devices/erouters/{id}/dhcp/leases/{serverName}/{leaseId}` [id, serverName, leaseId] — Update DHCP IP lease. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/erouters/{id}/dhcp/leases/{serverName}/{leaseId}` [id, serverName, leaseId] — Update DHCP IP lease. → 200/400/401/403/404/500
|
||||
- PUT `/devices/erouters/{id}/router/ospf/areas/{areaId}` [id, areaId] — Update OSPF area. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/router/ospf/areas/{areaId}] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/erouters/{id}/router/ospf/areas/{areaId}` [id, areaId] — Delete OSPF area. [Deprecated in favor of route DELETE /nms/api/v2.1/devices/{id}/router/ospf/areas/{areaId}] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/router/ospf/areas/{areaId}` [id, areaId] — Update OSPF area. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/router/ospf/areas/{areaId}] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/router/ospf/areas/{areaId}` [id, areaId] — Delete OSPF area. [Deprecated in favor of route DELETE /nms/api/v2.1/devices/{id}/router/ospf/areas/{areaId}] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprlxcs/{id}/dhcp/leases/{serverName}/{leaseId}` [id, serverName, leaseId] — Update DHCP IP lease. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/dhcp/leases/{serverName}/{leaseId}` [id, serverName, leaseId] — Delete DHCP IP lease. → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprs/{id}/router/ospf/areas/{areaId}` [id, areaId] — Update OSPF area. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/router/ospf/areas/{areaId}] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprs/{id}/router/ospf/areas/{areaId}` [id, areaId] — Delete OSPF area. [Deprecated in favor of route DELETE /nms/api/v2.1/devices/{id}/router/ospf/areas/{areaId}] → 200/400/401/403/404/500
|
||||
- PUT `/devices/uisprs/{id}/dhcp/leases/{serverName}/{leaseId}` [id, serverName, leaseId] — Update DHCP IP lease. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprs/{id}/dhcp/leases/{serverName}/{leaseId}` [id, serverName, leaseId] — Delete DHCP IP lease. [Deprecated in favor of route PUT /nms/api/v2.1/devices/{id}/configuration] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/unknown/{ipAddress}` [ipAddress] — Delete unknown client devices (based on detected network traffic). → 200/400/401/403/500
|
||||
- DELETE `/devices/{id}/stations` [id] — Delete station from this device. → 200/400/401/403/404/500
|
||||
- DELETE `/devices/{id}/vlans/{vlanId}` [id, vlanId, isComposeRequest?] — Delete a device's VLAN. → 200/400/401/403/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/firewall/nats/rule/{ruleId}` [id, ruleId] — Deletes firewall NAT rule. → 200/400/401/404/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/router/ospf/interfaces/{interfaceName}` [id, interfaceName] — Delete OSPF Interface. [Deprecated in favor of route DELETE /nms/api/v2.1/devices/{deviceId}/interfaces/{interfaceName}/ospf] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprs/{id}/firewall/nats/rule/{ruleId}` [id, ruleId, isComposeRequest?] — Deletes firewall NAT rule. → 200/400/401/404/500
|
||||
- DELETE `/devices/uisprs/{id}/router/ospf/interfaces/{interfaceName}` [id, interfaceName] — Delete OSPF Interface. [Deprecated in favor of route DELETE /nms/api/v2.1/devices/{deviceId}/interfaces/{interfaceName}/ospf] → 200/400/401/403/404/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/firewall/filters/{filterName}/rule/{ruleId}` [id, filterName, ruleId, isComposeRequest?] — Deletes firewall filter rule. → 200/400/401/404/500
|
||||
- DELETE `/devices/uisprlxcs/{id}/firewall/mangles/{mangleName}/rule/{ruleId}` [id, mangleName, ruleId] — Deletes firewall mangle rule. → 200/400/401/404/500
|
||||
- DELETE `/devices/uisprs/{id}/firewall/filters/{filterName}/rule/{ruleId}` [id, filterName, ruleId, isComposeRequest?] — Deletes firewall filter rule. → 200/400/401/404/500
|
||||
- DELETE `/devices/uisprs/{id}/firewall/mangles/{mangleName}/rule/{ruleId}` [id, mangleName, ruleId] — Deletes firewall mangle rule. → 200/400/401/404/500
|
||||
|
||||
## Discovery — _Scan for devices and connect them to UISP._
|
||||
|
||||
- GET `/discovery/scan-status` — Get current scan status (payload is WIP). → 200/400/401/500
|
||||
- GET `/discovery/status/{deviceId}` — Get device's discovery status. → 200/400/401/404/500
|
||||
- POST `/discovery/import` — Import devices to discovery. → 200/400/401/500
|
||||
- POST `/discovery/rescan` — Restart scanning process. → 200/400/401/500
|
||||
- POST `/discovery/connect/other` — Start connect process for discovered other devices. → 200/400/401/500
|
||||
- POST `/discovery/connect/ubnt` — Start connect process for discovered UBNT devices. → 200/400/401/500
|
||||
|
||||
## Export — _Client data export._
|
||||
|
||||
- GET `/gdpr/clients/{id}` [id] — Download GDPR Client Report. → 400/401/403/404/500
|
||||
|
||||
## Firmware — _Manage firmware files in UISP._
|
||||
|
||||
- GET `/firmwares` — Fetch available firmware. → 200/401/403/500
|
||||
- POST `/firmwares` — Upload new firmware image. → 200/401/403/500
|
||||
- GET `/firmwares-per-devices` — Fetches current firmware statuses and available firmwares for all devices. → 200/401/403/500
|
||||
- POST `/firmwares/delete` — Batch firmware delete. → 200/401/403/500
|
||||
- POST `/firmwares/download` — Batch firmware download. → 200/401/403/500
|
||||
|
||||
## Gateways — _Setup network device gateways._
|
||||
|
||||
- GET `/gateways` — Get all gateways. → 200/401/403/500
|
||||
- POST `/gateways` — Create a new gateway. → 200/400/401/403/500
|
||||
- GET `/gateways/{id}` — Get gateway detail → 200/401/403/404/500
|
||||
- PUT `/gateways/{id}` [id] — Update a gateway. → 200/400/401/403/404/500
|
||||
- DELETE `/gateways/{id}` [id] — Delete a gateway. → 200/401/403/404/500
|
||||
- GET `/gateways/{id}/speed` [id] — Get current and last 24-hour peak of up/down speed of a gateway → 200/401/403/404/500
|
||||
|
||||
## Logs — _View devices log lines._
|
||||
|
||||
- GET `/logs` [count, page, siteId?, deviceId?, start?, level?, tag?, period?, query?] — List of all log items. → 200/400/401/403/500
|
||||
- POST `/logs/end-alerts` [ids?] — End alerts for log items by log ids. → 200/400/401/403/500
|
||||
|
||||
## Outages — _View devices outages._
|
||||
|
||||
- GET `/outages` [count, page, deviceId?, siteId?, start?, period?, query?, type?, inProgress?, haveEnded?, grouped?] — List of all network outages for last month. → 200/400/401/403/404/500
|
||||
|
||||
## Server — _UISP settings with SMTP configuration, SSL and backups configuration._
|
||||
|
||||
- GET `/nms/address` — Get IP address of the UISP server, resolve hostname if necessary. → 200/401/403/500
|
||||
- GET `/nms/changed` [since, ucrm?] — List of items in UISP that changed since given timestamp. → 200/401/403/500
|
||||
- GET `/nms/cloud-config` — Supply UISP instance cloud config, i.e. notifications, current tier and list of tiers. → 200/401/403/406/500
|
||||
- GET `/nms/connection` — Return UISP Key for UISP server. → 200/401/403/500
|
||||
- GET `/nms/enums` — Return UISP enumerations. → 200/401/403/500
|
||||
- GET `/nms/heartbeat` — Liveness check. → 200/500
|
||||
- GET `/nms/info` [ip?] — Return UISP info. → 200/400/403/405/500/503
|
||||
- GET `/nms/log-verbosity` — Get current server log verbosity. → 200/401/403/500
|
||||
- POST `/nms/log-verbosity` — Change current server log verbosity and generate profile. → 200/401/403/500
|
||||
- GET `/nms/mailserver` — Fetch mail server settings. → 200/401/403/500
|
||||
- PUT `/nms/mailserver` — Update mail server settings. → 200/401/403/500
|
||||
- GET `/nms/notifications` — Get UISP instance notifications. → 200/401/403/412/422/500
|
||||
- PUT `/nms/notifications` — Supply UISP instance notifications. → 200/401/403/412/422/500
|
||||
- GET `/nms/search` [query?, count, page] — Search UISP. → 200/401/403/500
|
||||
- GET `/nms/server-config` — Return UISP server configuration. → 200/500
|
||||
- GET `/nms/server-time` — Get server time of the UISP server. → 200/401/403/500
|
||||
- GET `/nms/settings` — Get UISP settings. → 200/401/403/500
|
||||
- PUT `/nms/settings` — Update UISP settings. → 200/401/403/412/422/500
|
||||
- GET `/nms/setup` — Return status of UISP setup. → 200/500
|
||||
- POST `/nms/setup` — Setup UISP instance. → 200/403/500
|
||||
- GET `/nms/statistics` [interval?, start?, period?] — Get UISP Network statistics. → 200/401/403/406/500
|
||||
- GET `/nms/subscriber` — Get subscriber detail with QoS and device list → 200/400/401/403/404/500
|
||||
- GET `/nms/summary` [outagesTimestamp?, logsTimestamp?, logsLevel, firmwaresTimestamp?] — Various badge-count like values, e.g. unread logs count. → 200/401/403/500
|
||||
- GET `/nms/traffic` — Return netflow traffic status. → 200/401/403/500
|
||||
- GET `/nms/update` — Get UISP update status. → 200/401/403/500/503
|
||||
- PUT `/nms/update` — Request UISP update. → 200/401/403/500/503
|
||||
- GET `/nms/version` — Get UISP version. → 200/500
|
||||
- GET `/nms/dashboard/data` — Various data for dashboard. → 200/401/403/500
|
||||
- GET `/nms/maintenance/backup` — Download data backup. → 401/403/500
|
||||
- PUT `/nms/maintenance/backup` — Upload data backup. → 200/401/403/500
|
||||
- GET `/nms/maintenance/supportinfo` [period] — Download UISP support file. → 401/403/500
|
||||
- GET `/nms/questionnaires/active` — Get currently active questionnaire. → 200/400/401/403/500
|
||||
- GET `/nms/speed/report` [siteId?, sortBy?, sortDesc?, count?, page?] — Results of speed report. → 200/405/500
|
||||
- POST `/nms/speed/report` — Endpoint for reporting speed. Mobile application WiFiman can share speed result for example. → 200/400/401/403/404/405/500
|
||||
- GET `/nms/traffic/blacklist` — Get subnets that are ignored by traffic collection. → 200/401/403/500
|
||||
- POST `/nms/traffic/blacklist` — Set subnets that are ignored by traffic collection. → 200/401/403/500
|
||||
- GET `/nms/traffic/subnets` — Get subnets that are relevant for traffic collection. → 200/401/403/500
|
||||
- POST `/nms/traffic/subnets` — Set subnets that are relevant for traffic collection. → 200/401/403/500
|
||||
- GET `/nms/update/log` — Get latest update log. → 200/401/403/500/503
|
||||
- GET `/nms/version/latest` — Get latest UISP version. → 200/403/500
|
||||
- GET `/nms/maintenance/backup/restore` — Restores uploaded UISP backup file. → 200/401/403/500
|
||||
- DELETE `/nms/maintenance/backup/restore` — Clears uploaded UISP backup file. → 200/401/403/500
|
||||
- GET `/nms/statistics/throughput/{type}/{deviceId}` [deviceId?, type, interval?, start?, period?] — Get Gateway(s) and Olt(s) network activity data for dashboard. → 200/401/403/500
|
||||
- POST `/nms/migrate` — Start migration to enable transfer. → 200/401/403/500
|
||||
- POST `/nms/mailserver/test` — Test mail server settings by sending email. → 200/401/403/500
|
||||
- POST `/nms/setup/finish` — Set UISP setup finished. → 200/500
|
||||
- POST `/nms/setup/start` — Set UISP setup started. → 200/500
|
||||
- POST `/nms/setup/survey` — Submit setup survey. → 200/500
|
||||
- POST `/nms/questionnaires/{id}/touch` [id] — Mark the questionnaire as visited. It will pop up later if it is not submitted. → 200/400/401/403/500
|
||||
- POST `/nms/questionnaires/{id}/submit` [id] — Submit answers to questionnaire. → 200/400/401/403/500
|
||||
- POST `/nms/speed/report/bulkdelete` — Bulk delete speed reports. → 200/400/401/500
|
||||
- POST `/nms/subscriber/device/{deviceId}` [deviceId] — Set device information possessed by subscriber → 200/400/401/403/404/500
|
||||
- POST `/nms/subscriber/station/{mac}` [mac] — Set station information possessed by subscriber → 200/400/401/403/404/500
|
||||
- PUT `/nms/refresh-certificate` — Refresh UISP certificate. → 200/401/403/500
|
||||
- PUT `/nms/settings/allow-unms-beta` — Update UISP allowUnmsBetaVersion settings. → 200/401/403/412/422/500
|
||||
- DELETE `/nms/speed/report/{id}` [id] — Delete speed report. → 200/400/401/403/404/500
|
||||
|
||||
## Sites — _Manage sites and clients (former endpoints), their pictures and network structure._
|
||||
|
||||
- GET `/sites` [id?, ip?, deviceId?, type?, ucrm?, ucrmDetails?] — List of sites in UISP. Only some combinations of query parameters are valid. → 200/400/401/403/500
|
||||
- POST `/sites` — Create new site. → 200/400/401/403/500
|
||||
- GET `/sites-status` — List of sites status. → 200/400/401/403/500
|
||||
- GET `/sites/search` [query?, count, page, type?, ucrm?, latitude?, longitude?] — Search sites based on name, address, MAC address or IP address. → 200/400/401/403/404/500
|
||||
- GET `/sites/traffic` [from, to, granularity] — Get traffic from each site between two points in time. → 200/400/401/403/500
|
||||
- GET `/sites/{id}` [id, ucrmDetails?] — Return a site's detail. → 200/400/401/403/404/500
|
||||
- PUT `/sites/{id}` [id] — Update site. → 200/400/401/403/404/500
|
||||
- DELETE `/sites/{id}` [id] — Delete site. It's possible to call only on empty site. → 200/400/401/403/404/500
|
||||
- GET `/sites/nominatim/proxy` [apiurl?] — Search map position nominatim API proxy. → 200/400/401/403/404/500
|
||||
- GET `/sites/subscribers/statistics` — Get all subscribers statistics → 200/400/401/403/404/500
|
||||
- GET `/sites/{siteId}/historicalStatistics` [siteId, interval, start?, period?, tableLimit?, tablePage?, sortBy?, sortOrder?, type?] — Get historical stats for site graphs. → 200/400/401/403/404/500
|
||||
- GET `/sites/{siteId}/statistics` [siteId, interval] — Get upload and download between site and its parent site → 200/400/401/403/404/500
|
||||
- GET `/sites/{siteId}/traffic` [siteId, from, to, granularity] — Get site traffic between two points in time. → 200/400/401/403/500
|
||||
- GET `/sites/{id}/clients` [id, recursive?] — List of all client site ids belonging to the given site or to the given site's subtree. → 200/400/401/403/404/500
|
||||
- GET `/sites/{id}/uplink-devices` [id, withStatistics?, interval] — Return a site's uplink devices with scores. → 200/400/401/403/404/500
|
||||
- GET `/sites/{id}/images` [id] — Return all site images sorted by image order. → 200/400/401/403/500
|
||||
- POST `/sites/{id}/images` [id] — Upload new image and create image thumbnail. → 200/400/401/403/500
|
||||
- GET `/sites/{siteId}/qos` [siteId] — Get client Traffic Shaping. → 200/400/401/403/404/500
|
||||
- PUT `/sites/{siteId}/qos` [siteId] — Update client Traffic Shaping. → 200/400/401/403/404/500
|
||||
- GET `/sites/{siteId}/traffic/summary` [siteId, interval] — Get site total upload and download for specified interval up to now. → 200/400/401/403/500
|
||||
- GET `/sites/{siteId}/images/{imageId}` [siteId, imageId] — Return image file. → 400/401/403/500
|
||||
- DELETE `/sites/{siteId}/images/{imageId}` [siteId, imageId] — Delete image. → 200/400/401/403/500
|
||||
- PATCH `/sites/{siteId}/images/{imageId}` [siteId, imageId] — Update image. → 200/400/401/403/500
|
||||
- GET `/sites/{siteId}/traffic/interval` [siteId, interval, granularity] — Get site traffic for specified interval up to now. → 200/400/401/403/500
|
||||
- POST `/sites/authorize-onu-bulk` — Create subscriber for every ONU and authorize. → 200/400/401/403/500
|
||||
- POST `/sites/bulkdelete` — Delete sites. It's possible to call only on empty sites. → 200/400/401/403/500
|
||||
- POST `/sites/devicesiterelations` [forceCreate?] — Import sites and their devices relations. → 200/400/401/403/500
|
||||
- DELETE `/sites/devicesiterelations` — Import sites/endpoints and their devices relations. → 200/400/401/403/500
|
||||
- POST `/sites/devicesubscriberrelations` [forceCreate?] — Import subscribers and their devices relations. → 200/400/401/403/500
|
||||
- POST `/sites/bulkupdate/notifications` — Update sites notifications. → 200/400/401/403/500
|
||||
- POST `/sites/ucrm/bindings` — Import UISP sites to UCRM services relations description. → 200/400/401/403/500
|
||||
- POST `/sites/{siteId}/unsuspend` [siteId] — Unsuspend client. → 200/400/401/403/404/500
|
||||
- POST `/sites/{subscriberId}/preregister` [subscriberId] — Set subscriber to MAC/IP address pairing for auto-authorize functionality. → 200/400/401/403/500
|
||||
- POST `/sites/{siteId}/suspend` [siteId] — Suspend client. → 200/400/401/403/404/500
|
||||
- POST `/sites/{siteId}/ucrm/unbind` [siteId] — Unbind UCRM services and UISP site. → 200/400/401/403/500
|
||||
- POST `/sites/{siteId}/ucrm/bind` [siteId] — Bind UCRM services and UISP site. → 200/400/401/403/500
|
||||
- POST `/sites/{siteId}/images/{imageId}/reorder` [siteId, imageId] — Change image order. → 200/400/401/403/500
|
||||
- POST `/sites/{siteId}/images/{imageId}/rotateleft` [siteId, imageId] — Rotate the image 90 degrees to left. → 200/400/401/403/500
|
||||
- POST `/sites/{siteId}/images/{imageId}/rotateright` [siteId, imageId] — Rotate the image 90 degrees to right. → 200/400/401/403/500
|
||||
|
||||
## Speed Test — _Speedtest between device to device or device to internet._
|
||||
|
||||
- GET `/speed-tests` — Return detail about running speedtests. → 200/401/403/500
|
||||
- GET `/devices/{deviceId}/speed-tests/{testId}` [deviceId, testId] — Return detail about running speedtest. → 200/401/403/422/500
|
||||
- DELETE `/devices/{deviceId}/speed-tests/{testId}` [deviceId, testId] — Stop speed test. → 200/400/401/403/404/422/500
|
||||
- POST `/speed-tests/start` — Execute speed test between two devices. → 200/400/401/403/500
|
||||
- POST `/devices/{deviceId}/speed-tests/start` [deviceId] — Execute speed test on device [device to internet, device to device]. → 200/400/401/403/422/500
|
||||
- DELETE `/speed-tests/{id}` [id] — Stop speed test between two devices. → 200/400/401/403/404/500
|
||||
|
||||
## Tasks — _View, start or cancel UISP background tasks, for example firmware upgrade._
|
||||
|
||||
- GET `/tasks` [count, page, status?, period?] — List all tasks. → 200/401/403/500
|
||||
- POST `/tasks` — Start FW upgrade task for a group of devices. → 200/400/401/403/404/500
|
||||
- GET `/tasks/device-status-numbers-by-roles` — Return number of devices firmware upgrade status by roles. → 200/401/403/500
|
||||
- GET `/tasks/in-progress` — Return number of tasks in progress state. → 200/401/403/500
|
||||
- GET `/tasks/prepare-auto-updates` — Construct tasks for auto updates. → 200/401/403/500
|
||||
- GET `/tasks/{batchId}` [batchId] — Returns a mass upgrade task inner task items. → 200/400/401/403/404/500
|
||||
- POST `/tasks/cancel-for-device/{deviceId}` [deviceId] — Cancel a task for device. → 200/400/401/403/404/500
|
||||
- POST `/tasks/{batchId}/cancel` [batchId] — Cancel a task. → 200/400/401/403/404/500
|
||||
|
||||
## Token — _Manage access tokens._
|
||||
|
||||
- GET `/token` — Get all API tokens. → 200/401/403/409/500
|
||||
- POST `/token` — Create new API token. → 200/400/401/403/500
|
||||
- GET `/token/{tokenId}` [tokenId] — Get API token by ID. → 200/401/403/404/409/500
|
||||
- DELETE `/token/{tokenId}` [tokenId] — Delete API token. → 200/400/401/403/409/500
|
||||
|
||||
## Traffic — _Devices and clients traffic statistics generated thanks to NetFlow._
|
||||
|
||||
- GET `/devices/unknown` [minTraffic?] — Get unknown client devices (based on detected network traffic) ordered by total traffic. → 200/400/401/403/500
|
||||
- GET `/nms/traffic` — Return netflow traffic status. → 200/401/403/500
|
||||
- GET `/sites/traffic` [from, to, granularity] — Get traffic from each site between two points in time. → 200/400/401/403/500
|
||||
- GET `/nms/traffic/blacklist` — Get subnets that are ignored by traffic collection. → 200/401/403/500
|
||||
- POST `/nms/traffic/blacklist` — Set subnets that are ignored by traffic collection. → 200/401/403/500
|
||||
- GET `/nms/traffic/subnets` — Get subnets that are relevant for traffic collection. → 200/401/403/500
|
||||
- POST `/nms/traffic/subnets` — Set subnets that are relevant for traffic collection. → 200/401/403/500
|
||||
- GET `/sites/{siteId}/traffic` [siteId, from, to, granularity] — Get site traffic between two points in time. → 200/400/401/403/500
|
||||
- GET `/sites/{siteId}/traffic/summary` [siteId, interval] — Get site total upload and download for specified interval up to now. → 200/400/401/403/500
|
||||
- GET `/sites/{siteId}/traffic/interval` [siteId, interval, granularity] — Get site traffic for specified interval up to now. → 200/400/401/403/500
|
||||
- POST `/nms/migrate` — Start migration to enable transfer. → 200/401/403/500
|
||||
- DELETE `/devices/unknown/{ipAddress}` [ipAddress] — Delete unknown client devices (based on detected network traffic). → 200/400/401/403/500
|
||||
|
||||
## Users — _Manage UISP users and their profiles._
|
||||
|
||||
- GET `/user` — Get the authenticated user. → 200/401/403/500
|
||||
- PUT `/user` — Updates authenticated user. → 200/400/401/403/500
|
||||
- GET `/users` — Return list of all users. → 200/401/403/500
|
||||
- GET `/nms/keep-alive` — Extend validity of user token. → 200/500
|
||||
- GET `/user/notifications/push` — Read user push notifications settings route. → 200/401/403/500
|
||||
- PATCH `/user/notifications/push` — Save user push notifications settings route. → 200/400/401/403/500
|
||||
- GET `/user/offline-passwords/requested` — Show status if offline passwords request is present for user. → 200/401/403/404/500
|
||||
- GET `/user/password/is-token-valid` [token] — Checks if token for password reset is valid. → 200/400/403/500
|
||||
- POST `/user/check-credentials` — Check user credentials. → 200/400/401/403/500
|
||||
- POST `/user/check-session` — Check that the session token and cookie are valid. → 200/401/403/500
|
||||
- POST `/user/location` — Updates user's location. → 200/400/401/403/500
|
||||
- POST `/user/offline-passwords` — Generates random offline passwords for user. → 200/401/403/500
|
||||
- POST `/users/invite` — Invite new user. → 200/400/401/403/500
|
||||
- POST `/user/offline-passwords/confirm` — Confirms request to generate random offline passwords for authenticated user. → 200/401/403/404/500
|
||||
- POST `/user/password/requestreset` — Request password reset. → 200/400/403/500
|
||||
- POST `/user/password/reset` — Reset user password. → 200/400/401/500
|
||||
- POST `/users/{id}/reinvite` [id] — Reinvites user by email. → 200/400/401/403/500
|
||||
- POST `/users/{id}/reinvite/link` [id] — Generates new invitation link. → 200/400/401/403/500
|
||||
- PUT `/user/preferences` — Updates authenticated user's preferences. → 200/400/401/403/500
|
||||
- PUT `/users/{id}` [id] — Update user. → 200/400/401/403/500
|
||||
- DELETE `/users/{id}` [id, notifyUser?] — Delete user. → 200/400/401/403/404/500
|
||||
|
||||
## Vault — _Device credentials vault._
|
||||
|
||||
- GET `/vault/credentials` — Get credentials vault status. → 200/401/403/500
|
||||
- POST `/vault/credentials` — Update credentials vault status. → 200/400/401/403/404/500
|
||||
- GET `/vault/{deviceId}/credentials` [deviceId] — Get device credentials. → 200/400/401/403/500
|
||||
- POST `/vault/credentials/bulk-regenerate` — Bulk set new passwords for devices. → 200/400/401/403/404/412/500
|
||||
- POST `/vault/credentials/devices` — Get devices' credentials → 200/400/401/403/500
|
||||
- POST `/vault/credentials/regenerate` — Generate new password for vault. Returns status. → 200/400/401/403/404/412/500
|
||||
- POST `/vault/credentials/unlock` — Unlock private or cloud pgp key. → 200/400/401/403/404/500
|
||||
- POST `/vault/{deviceId}/credentials/change` [deviceId] — Changing vault device password for mobile app. → 200/400/401/403/404/412/500
|
||||
- POST `/vault/{deviceId}/credentials/regenerate` [deviceId] — Generate or set new password for device. → 200/400/401/403/404/412/500
|
||||
93
INDEX_api_used.md
Executable file
93
INDEX_api_used.md
Executable file
@ -0,0 +1,93 @@
|
||||
# INDEX_api_used.md — Llamadas API que el plugin ya hace
|
||||
> Referencia rápida de todas las llamadas API usadas en `public.php`, `Plugin.php` y sus dependencias.
|
||||
> Para bugs o modificaciones en llamadas existentes, este archivo evita leer los archivos de referencia API completos.
|
||||
|
||||
---
|
||||
|
||||
## API UCRM (CRM v1.0)
|
||||
**Base URL:** `https://{ipserver}/crm/api/v1.0/`
|
||||
**Auth:** Header `X-Auth-App-Key: {apitoken}` (SDK) o `X-Auth-Token: {apitoken}` (Guzzle directo)
|
||||
|
||||
| Endpoint | Método | Usado en | Parámetros / Body |
|
||||
|---|---|---|---|
|
||||
| `clients` | GET | public.php, PaymentIntentService | `query, limit, offset` |
|
||||
| `clients/{id}` | GET | Plugin.php, Facades, PaymentIntentService | — |
|
||||
| `clients/{id}` | PATCH | AbstractMessageNotifierFacade | `{attributes: [{value, customAttributeId}]}` |
|
||||
| `clients/services` | GET | AbstractMessageNotifierFacade | `?clientId={id}` |
|
||||
| `users/admins` | GET | public.php | — |
|
||||
| `users/admins/{id}` | GET | AbstractMessageNotifierFacade | — |
|
||||
| `payments` | GET | public.php | `clientId, limit, order, direction` |
|
||||
| `payments/{id}` | GET | public.php (resend_payment) | — |
|
||||
| `payments` | POST | AbstractStripeOperationsFacade | `{clientId, amount, currencyCode, methodId, note}` |
|
||||
| `payment-methods` | GET | public.php | — |
|
||||
| `scheduling/jobs` | GET | public.php | `assignedUserId, statuses[], limit` |
|
||||
| `scheduling/jobs/{id}` | GET | public.php (resend_job) | — |
|
||||
| `scheduling/jobs/{id}` | PATCH | Plugin.php, AbstractMessageNotifierFacade | `{title}` |
|
||||
| `client-attributes` | GET | AbstractStripeOperationsFacade | — (busca IDs de atributos custom) |
|
||||
| `invoices/{id}` | GET | AbstractStripeOperationsFacade | — |
|
||||
|
||||
---
|
||||
|
||||
## API NMS/UNMS (v2.1)
|
||||
**Base URL:** `https://{ipserver}/nms/api/v2.1/`
|
||||
**Auth:** Header `X-Auth-Token: {unmsApiToken}`
|
||||
|
||||
| Endpoint | Método | Usado en | Parámetros / Body |
|
||||
|---|---|---|---|
|
||||
| `user/login` | POST | public.php (nms_login) | `{username, password}` |
|
||||
| `user/login/totpauth` | POST | public.php (nms_login_totp) | `{twoFactorToken, totpCode}` |
|
||||
| `user` | GET | public.php (nms_verify_session) | Header `x-auth-token` |
|
||||
| `devices` | GET | AbstractMessageNotifierFacade | `?siteId={id}` |
|
||||
| `vault/{deviceId}/credentials` | GET | AbstractMessageNotifierFacade | Header `X-Auth-Token` |
|
||||
| `vault/{deviceId}/credentials/regenerate` | POST | AbstractMessageNotifierFacade | `[{username, password, readOnly}]` |
|
||||
|
||||
---
|
||||
|
||||
## API Stripe
|
||||
**Lib:** `stripe/stripe-php` SDK
|
||||
**Auth:** `stripeApiKey` (del config)
|
||||
|
||||
| Recurso Stripe | Operación | Usado en | Parámetros relevantes |
|
||||
|---|---|---|---|
|
||||
| `paymentIntents.create` | POST | PaymentIntentService, AbstractStripeOperationsFacade | `amount, currency, customer, payment_method_types, metadata` |
|
||||
| `paymentIntents.all` | GET | PaymentIntentService | `customer, limit, expand:[data.charges]` |
|
||||
| `paymentIntents.retrieve` | GET | AbstractStripeOperationsFacade | `id` |
|
||||
| `customers.create` | POST | AbstractStripeOperationsFacade | `name, email, metadata:{clientId}` |
|
||||
| `customers.retrieve` | GET | AbstractStripeOperationsFacade | `id` |
|
||||
| `customers.update` | POST | AbstractStripeOperationsFacade | `name, email` |
|
||||
| `customers.retrieveCashBalance` | GET | PaymentIntentService | `stripeCustomerId` |
|
||||
| `customers.createFundingInstructions` | POST | AbstractStripeOperationsFacade | `currency, funding_type, bank_transfer:{type:mx_bank_transfer}` |
|
||||
|
||||
---
|
||||
|
||||
## API Callbell
|
||||
**Base URL:** `https://api.callbell.eu/v1/`
|
||||
**Auth:** Header `Authorization: Bearer {tokencallbell}`
|
||||
**Usado en:** `ClientCallBellAPI.php`
|
||||
|
||||
| Endpoint | Método | Descripción |
|
||||
|---|---|---|
|
||||
| `contacts` | GET | Busca contacto por teléfono |
|
||||
| `contacts/{uuid}` | PATCH | Actualiza datos del contacto (nombre, custom fields) |
|
||||
| `messages/send` | POST | Envía mensaje / plantilla WhatsApp |
|
||||
|
||||
---
|
||||
|
||||
## Llamadas HTTP directas (cURL/Guzzle sin SDK)
|
||||
|
||||
| Destino | Método | Usado en | Descripción |
|
||||
|---|---|---|---|
|
||||
| `self URL` (loopback) | POST | public.php `resend_payment`, `resend_job_notification` | Simula webhook reenviando JSON al mismo `public.php` |
|
||||
| `http://{ipPuppeteer}:{portPuppeteer}/generate` | POST | AbstractOxxoOperationsFacade | Genera imagen de voucher OXXO via Puppeteer |
|
||||
| MinIO presigned URL | PUT | MinioStorageService | Sube imagen del voucher generado |
|
||||
|
||||
---
|
||||
|
||||
## Atributos Custom de Cliente en UCRM que el plugin usa
|
||||
| Clave (`key`) | Nombre en UI | Usado para |
|
||||
|---|---|---|
|
||||
| `stripeCustomerId` | Stripe Customer ID | ID del customer en Stripe |
|
||||
| `clabeInterbancaria` | Clabe Interbancaria | CLABE para transferencias MX |
|
||||
| `passwordAntenaCliente` | Password Antena Cliente | Contraseña de la antena del cliente (desde UNMS vault) |
|
||||
| `antenaSectorial` | Antena Sectorial | Tipo de antena (usado en audit de passwords) |
|
||||
| `site` | Site | ID del sitio UNMS asociado al servicio |
|
||||
230
INDEX_deps.md
Executable file
230
INDEX_deps.md
Executable file
@ -0,0 +1,230 @@
|
||||
# INDEX_deps.md — Facades y Services
|
||||
> Firmas y propósito de todas las dependencias usadas por `public.php` y `Plugin.php`
|
||||
|
||||
---
|
||||
|
||||
## src/Facade/
|
||||
|
||||
### AbstractMessageNotifierFacade (abstract) — L.1–537
|
||||
`src/Facade/AbstractMessageNotifierFacade.php`
|
||||
Base de todas las facades. Maneja lógica de notificación via Callbell/WhatsApp.
|
||||
|
||||
**Constantes:**
|
||||
- `SUBJECT_OF_INSTALLER_CHANGE[]` — textos para notificar cambio/desasignación de técnico
|
||||
- `ADDITIONAL_CHANGE_DATA[]` — datos adicionales del cambio de técnico
|
||||
|
||||
**Constructor:** `(Logger, MessageTextFactory, SmsNumberProvider)` + carga `$ucrmApi` desde config
|
||||
|
||||
**Métodos públicos:**
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `verifyPaymentActionToDo(NotificationData): void` | Itera teléfonos por tipo (whatsapp/whatsnotifica/whatsactualiza) y llama notify/update |
|
||||
| `verifyClientActionToDo(NotificationData): void` | Solo `onlyUpdate()` para tipos whatsapp/whatsactualiza |
|
||||
| `verifyServiceActionToDo(NotificationData): void` | `onlyUpdateService()` para tipos whatsapp/whatsactualiza |
|
||||
| `verifyJobActionToDo($jsonData, $reprog, $changeInstaller): void` | Notifica instalador anterior, cliente y técnico nuevo; gestiona título del job |
|
||||
| `verifyInvoiceActionToDo(NotificationData): void` | `onlyUpdate()` sin mostrar balance |
|
||||
| `notify(NotificationData, $phone): void` | Envía notificación de pago vía Callbell (texto o plantilla) |
|
||||
| `notifyAndUpdate(NotificationData, $phone): void` | Notifica + hace PATCH en Callbell |
|
||||
| `notifyOverDue(NotificationData): void` | Notifica factura vencida/por vencer |
|
||||
| `onlyUpdate(NotificationData, $phone): void` | Solo actualiza contacto en Callbell (sin enviar mensaje) |
|
||||
| `onlyUpdateService(NotificationData, $phone): void` | Actualiza status de servicio en Callbell |
|
||||
|
||||
**Métodos protegidos:**
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `getVaultCredentialsByClientId($clientId): string` | Obtiene pass de antena desde UNMS vault; sincroniza en CRM. Usa `GET /nms/api/v2.1/devices?siteId=` y `GET /nms/api/v2.1/vault/{deviceId}/credentials` |
|
||||
| `syncPasswordWithCrm(int $clientId, string $pass): void` | PATCH atributo `passwordAntenaCliente` en CRM si difiere |
|
||||
| `generateStrongPassword(int $length=16): string` | Genera contraseña segura (lower+upper+digits+@#) |
|
||||
| `patchClientCustomAttribute(int $clientId, int $attributeId, string $value): bool` | PATCH `clients/{id}` con atributo custom |
|
||||
| `comparePasswords(?string $crm, ?string $vault): string` | Prioriza vault, luego crm, luego mensaje de advertencia |
|
||||
| `validarNumeroTelefono($n): string` | Normaliza teléfono a formato internacional 521XXXXXXXXXX |
|
||||
|
||||
**Abstracto:** `sendWhatsApp(NotificationData, string $phone): void`
|
||||
|
||||
---
|
||||
|
||||
### PluginNotifierFacade extends AbstractStripeOperationsFacade
|
||||
`src/Facade/PluginNotifierFacade.php` — L.1–152
|
||||
|
||||
**Herencia:** `PluginNotifierFacade → AbstractStripeOperationsFacade → AbstractOxxoOperationsFacade → AbstractMessageNotifierFacade`
|
||||
|
||||
**Constructor:** `(Logger, MessageTextFactory, SmsNumberProvider, OptionsManager)` — carga `$pluginData`
|
||||
|
||||
**Métodos propios:**
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `updatePasswordAntenaIfNeeded(int $clientId, array $jsonData): void` | Llama `getVaultCredentialsByClientId()` — sincroniza pass antena |
|
||||
| `processClientPasswordAntenna(int $clientId, array $clientEntity): void` | Procesa tag "OBTENER PASSWORD ANTENA": incluye `scripts-uisp/audit_client_passwords.php`, llama `fixClientData()`, remueve tag |
|
||||
| `sendWhatsApp(NotificationData, string $phone): void` | Implementación mock (solo log) |
|
||||
|
||||
**Métodos heredados de AbstractStripeOperationsFacade** (ver abajo):
|
||||
- `createPaymentIntent()`, `registerPaymentFromWebhook()`, `registerPaymentFromIntent()`
|
||||
- `ensureStripePaymentAttribute()`, `createStripeClient()`, `syncStripeCustomerData()`
|
||||
- `createClabeForClient()`, `removeTagFromClient()`
|
||||
|
||||
---
|
||||
|
||||
### AbstractStripeOperationsFacade (abstract)
|
||||
`src/Facade/AbstractStripeOperationsFacade.php` — ~45KB
|
||||
|
||||
Hereda de `AbstractOxxoOperationsFacade`. Maneja toda la integración con Stripe y registro de pagos en UCRM.
|
||||
|
||||
**Métodos clave** (firmas aproximadas):
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `createPaymentIntent(array $jsonData): void` | Crea PaymentIntent en Stripe desde webhook `customer_cash_balance_transaction.funded` |
|
||||
| `registerPaymentFromWebhook(array $jsonData): void` | Registra pago en UCRM desde webhook `applied_to_payment` |
|
||||
| `registerPaymentFromIntent(array $piData): void` | Registra pago en UCRM desde `payment_intent.succeeded` |
|
||||
| `ensureStripePaymentAttribute(NotificationData $n): void` | Determina si el pago Stripe es OXXO, Transferencia o Tarjeta revisando metadata |
|
||||
| `createStripeClient(NotificationData $n, string $tag, bool $createClabe): void` | Crea cliente en Stripe + opcionalmente genera CLABE |
|
||||
| `syncStripeCustomerData(int $clientId, string $name, ?string $email): void` | Actualiza nombre/email del customer en Stripe |
|
||||
| `removeTagFromClient(int $clientId, string $tagName): void` | Elimina tag del cliente en UCRM |
|
||||
|
||||
---
|
||||
|
||||
### AbstractOxxoOperationsFacade (abstract)
|
||||
`src/Facade/AbstractOxxoOperationsFacade.php` — ~31KB
|
||||
|
||||
Maneja la integración OXXO Pay con Stripe y generación de vouchers.
|
||||
|
||||
**Métodos clave:**
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `createStripeReference(array $jsonData, ?float $amount): array` | Crea PaymentIntent OXXO en Stripe. Retorna `{hasError, data:{oxxo_reference, url, clientID, clientFullName, amount}}` |
|
||||
| `createOxxoOrder(array $oxxoData): array` | Guarda orden OXXO en storage local. Retorna `{order_id, oxxo_reference, url, ...}` |
|
||||
| `generateOxxoVoucher(array $responseOxxo, bool $background): void` | Llama a Puppeteer para generar imagen del voucher y subirla a MinIO |
|
||||
| `getOxxoOrderStatus(string $orderId): array` | Retorna estado actual de la orden OXXO |
|
||||
| `createOxxoPaymentIntent(array $clientData, float $amount, bool $notify): array` | Flujo completo: crea PI Stripe OXXO + orden + voucher |
|
||||
|
||||
---
|
||||
|
||||
### PluginOxxoNotifierFacade extends AbstractOxxoOperationsFacade
|
||||
`src/Facade/PluginOxxoNotifierFacade.php` — pequeño
|
||||
|
||||
Solo implementa los métodos abstractos. No agrega lógica propia relevante.
|
||||
|
||||
---
|
||||
|
||||
### TwilioNotifierFacade extends AbstractMessageNotifierFacade
|
||||
`src/Facade/TwilioNotifierFacade.php` — ~2KB
|
||||
|
||||
Implementa `sendWhatsApp()` usando Twilio (legacy, en desuso). Métodos notificación estándar delegados a la clase base.
|
||||
|
||||
---
|
||||
|
||||
### ClientCallBellAPI
|
||||
`src/Facade/ClientCallBellAPI.php` — ~81KB
|
||||
|
||||
Cliente completo de la API de Callbell. Usado internamente por las facades vía `new ClientCallBellAPI($apitoken, $ipserver, $tokencallbell)`.
|
||||
|
||||
**Métodos clave relevantes para el plugin:**
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `sendPaymentNotificationWhatsApp($phone, NotificationData): bool` | Envía plantilla de pago con template de Callbell |
|
||||
| `sendTextPaymentNotificationWhatsApp($phone, NotificationData): bool` | Envía texto plano de pago |
|
||||
| `sendOverdueNotificationWhatsApp($phone, NotificationData): bool` | Notificación de factura vencida |
|
||||
| `sendJobNotificationWhatsAppToClient($phone, array $data, $reprog, $changeInst): bool` | Notificación de job al cliente |
|
||||
| `sendJobNotificationWhatsAppToInstaller($phone, array $data, $reprog, $changeInst): void` | Notificación de job al técnico |
|
||||
| `getContactWhatsapp($phone): string` | GET contacto en Callbell por teléfono → JSON |
|
||||
| `patchWhatsapp(array $contact, NotificationData): void` | PATCH datos del contacto en Callbell |
|
||||
| `patchServiceStatusWhatsApp(array $contact, NotificationData): void` | PATCH status de servicio en Callbell |
|
||||
|
||||
---
|
||||
|
||||
## src/Service/
|
||||
|
||||
### PaymentIntentService
|
||||
`src/Service/PaymentIntentService.php` — 282 líneas
|
||||
|
||||
Usado directamente en `public.php` (no via DI). Constructor: `($ucrmApi, $stripeApiKey, $logger=null)`
|
||||
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `searchClients($query): array` | UCRM `GET clients?query=q&limit=5` — retorna array simplificado |
|
||||
| `getClientDetails($clientId): array` | UCRM `GET clients/{id}` — retorna datos + stripeCustomerId + clabeInterbancaria |
|
||||
| `createPaymentIntent($clientId, $amount, $stripeCustomerId, $adminId): array` | Crea PI Stripe tipo `customer_balance/bank_transfer/mx_bank_transfer` |
|
||||
| `getLastPayments($stripeCustomerId, $limit=10): array` | Stripe `paymentIntents.all` filtrado a bank_transfer — últimos N |
|
||||
| `getLastOxxoPayments($stripeCustomerId, $limit=5): array` | Stripe `paymentIntents.all` filtrado a OXXO — últimos N |
|
||||
| `getCustomerCashBalance($stripeCustomerId): float` | Stripe `customers.retrieveCashBalance` → MXN |
|
||||
|
||||
---
|
||||
|
||||
### SmsNumberProvider
|
||||
`src/Service/SmsNumberProvider.php` — 160 líneas
|
||||
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `getUcrmClientNumber(NotificationData): ?string` | Primer contacto con tipo aplicable al evento |
|
||||
| `getUcrmClientNumbers(NotificationData?, array?): array` | Retorna `['whatsapp'=>[phones], 'whatsnotifica'=>[phones], 'whatsactualiza'=>[phones]]` |
|
||||
| `getAllUcrmClientNumbers(array): array` | Todos los teléfonos del cliente sin filtrar por tipo |
|
||||
|
||||
**Tipos de contacto reconocidos:**
|
||||
- `whatsapp` — recibe notificación + actualización en Callbell
|
||||
- `whatsnotifica` — solo recibe notificación (no actualiza Callbell)
|
||||
- `whatsactualiza` — solo actualiza Callbell (no notifica)
|
||||
|
||||
---
|
||||
|
||||
### Logger (`src/Service/Logger.php`)
|
||||
Wrapper de PSR-3. Métodos: `debug()`, `info()`, `warning()`, `error()`. Nivel controlado por config `logging_level` / `debugMode`.
|
||||
|
||||
### OptionsManager (`src/Service/OptionsManager.php`)
|
||||
Carga config del plugin. Método: `load(): PluginData`
|
||||
|
||||
### PluginDataValidator (`src/Service/PluginDataValidator.php`)
|
||||
Valida que la configuración esté completa. Método: `validate(): bool`
|
||||
|
||||
### MinioStorageService (`src/Service/MinioStorageService.php`)
|
||||
Sube vouchers OXXO a MinIO. Usado por `AbstractOxxoOperationsFacade`.
|
||||
|
||||
### CurlExecutor (`src/Service/CurlExecutor.php`)
|
||||
Wrapper de cURL para la `UcrmApi` interna del plugin.
|
||||
|
||||
---
|
||||
|
||||
## src/Data/
|
||||
|
||||
### PluginData (extends UcrmData)
|
||||
`src/Data/PluginData.php` — Propiedades de configuración del plugin:
|
||||
|
||||
| Propiedad | Tipo | Descripción |
|
||||
|---|---|---|
|
||||
| `$ipserver` | string | IP/hostname del servidor UISP |
|
||||
| `$apitoken` | string | Token API UCRM |
|
||||
| `$unmsApiToken` | string | Token API UNMS/NMS |
|
||||
| `$tokencallbell` | string | Token API Callbell |
|
||||
| `$tokenstripe` | string | API Key Stripe |
|
||||
| `$ipPuppeteer` | string | IP del microservicio Puppeteer |
|
||||
| `$portPuppeteer` | string | Puerto del microservicio Puppeteer |
|
||||
| `$idPaymentAdminCRM` | string | ID admin para registrar pagos |
|
||||
| `$cashPaymentMethodId` | bool | Habilitar notif. efectivo |
|
||||
| `$courtesyPaymentMethodId` | bool | Habilitar notif. cortesía |
|
||||
| `$bankTransferPaymentMethodId` | bool | Habilitar notif. transferencia |
|
||||
| `$oxxoPayPaymentMethodId` | bool | Habilitar notif. OXXO |
|
||||
| `$creditCardStripePaymentMethodId` | bool | Habilitar notif. tarjeta Stripe |
|
||||
| `$stripeSubscriptionCreditCardPaymentMethodId` | bool | Habilitar notif. suscripción Stripe |
|
||||
| `$notificationTypeText` | bool | true=texto plano, false=plantilla Callbell |
|
||||
| `$installersDataWhatsApp` | string | JSON de instaladores |
|
||||
| `$debugMode` | bool | Activa logging DEBUG |
|
||||
| `$logging_level` | bool | Nivel verbose de log |
|
||||
| `$twilioAccountSid` | string\|null | Legacy Twilio |
|
||||
| `$twilioAuthToken` | string\|null | Legacy Twilio |
|
||||
| `$twilioSmsNumber` | string\|null | Legacy Twilio |
|
||||
|
||||
### NotificationData
|
||||
`src/Data/NotificationData.php`
|
||||
DTO con propiedades: `$uuid`, `$changeType`, `$entity`, `$entityId`, `$eventName`, `$clientId`, `$clientData[]`, `$paymentData[]`, `$invoiceData[]`
|
||||
|
||||
---
|
||||
|
||||
## src/Factory/
|
||||
|
||||
### NotificationDataFactory
|
||||
`src/Factory/NotificationDataFactory.php` — 5.8KB
|
||||
| Método | Descripción |
|
||||
|---|---|
|
||||
| `getObject(array $jsonData): NotificationData` | Construye `NotificationData` desde el payload del webhook UCRM, cargando datos adicionales de la API |
|
||||
|
||||
### MessageTextFactory
|
||||
`src/Factory/MessageTextFactory.php` — 3.7KB
|
||||
Genera textos de mensajes WhatsApp. Usado por las facades para construir el contenido de notificaciones.
|
||||
112
INDEX_plugin.md
Executable file
112
INDEX_plugin.md
Executable file
@ -0,0 +1,112 @@
|
||||
# INDEX_plugin.md — Plugin.php
|
||||
> **Archivo:** `src/Plugin.php` | 672 líneas | Entry point del plugin UCRM
|
||||
|
||||
---
|
||||
|
||||
## Propósito
|
||||
Clase orquestadora principal. UCRM la invoca en cada evento (hook). Detecta si viene de HTTP o CLI y delega al handler correspondiente.
|
||||
|
||||
## Namespace & Dependencias
|
||||
```
|
||||
namespace SmsNotifier
|
||||
Depende de: PluginNotifierFacade, PluginOxxoNotifierFacade, TwilioNotifierFacade,
|
||||
NotificationDataFactory, Logger, OptionsManager, PluginDataValidator, UcrmApi
|
||||
```
|
||||
|
||||
## Propiedades
|
||||
| Propiedad | Tipo | Descripción |
|
||||
|---|---|---|
|
||||
| `$logger` | Logger | Logger del plugin |
|
||||
| `$optionsManager` | OptionsManager | Carga config del plugin |
|
||||
| `$pluginDataValidator` | PluginDataValidator | Valida configuración |
|
||||
| `$pluginNotifierFacade` | PluginNotifierFacade | Fachada Stripe + Callbell |
|
||||
| `$pluginOxxoNotifierFacade` | PluginOxxoNotifierFacade | Fachada OXXO |
|
||||
| `$notifierFacade` | TwilioNotifierFacade | Fachada notificaciones base |
|
||||
| `$notificationDataFactory` | NotificationDataFactory | Crea objetos NotificationData |
|
||||
| `$ucrmApi` | UcrmApi (SDK) | Cliente API UCRM |
|
||||
|
||||
## Métodos
|
||||
| Método | Línea | Descripción |
|
||||
|---|---|---|
|
||||
| `__construct(...)` | 58 | Inicializa dependencias + UcrmApi con config |
|
||||
| `run(): void` | 84 | Entry point: detecta SAPI (fpm/cli) y delega |
|
||||
| `processCli(): void` | 96 | CLI: solo valida config |
|
||||
| `processHttpRequest(): void` | 104 | HTTP: procesa payload JSON del webhook |
|
||||
|
||||
---
|
||||
|
||||
## Flujo de `processHttpRequest()` (L.104–670)
|
||||
|
||||
### Paso 1 — Detectar tipo de payload (L.111–268)
|
||||
Lee `php://input`. Si **no tiene `uuid`** → es webhook externo (Stripe/OXXO):
|
||||
|
||||
| `type` en JSON | Línea | Acción |
|
||||
|---|---|---|
|
||||
| `customer_cash_balance_transaction.created` → `funded` | 133 | `pluginNotifierFacade->createPaymentIntent()` |
|
||||
| `customer_cash_balance_transaction.created` → `applied_to_payment` | 139 | `pluginNotifierFacade->registerPaymentFromWebhook()` |
|
||||
| `customer_cash_balance_transaction.created` → `unapplied_from_payment` | 142 | Log de transferencia cancelada |
|
||||
| `payment_intent.succeeded` | 166 | `pluginNotifierFacade->registerPaymentFromIntent()` |
|
||||
| `oxxo.retrieve` | 172 | `pluginOxxoNotifierFacade->getOxxoOrderStatus()` → responde JSON |
|
||||
| `oxxo.request` | 188 | Flujo async: `createStripeReference()` → `createOxxoOrder()` → responde → `generateOxxoVoucher()` en background |
|
||||
| `payout.failed` | 150 | Solo log |
|
||||
| `payment_intent.partially_funded` | 155 | Solo log |
|
||||
| `cash_balance.funds_available` | 163 | Solo log |
|
||||
|
||||
### Paso 2 — Payload con `uuid` → Webhook UCRM (L.270–670)
|
||||
Construye `NotificationData` via factory, luego switch por `eventName`:
|
||||
|
||||
| `eventName` | Línea | Acción |
|
||||
|---|---|---|
|
||||
| `payment.add` | 297 | Switch por `methodId` UUID → `notifierFacade->verifyPaymentActionToDo()` |
|
||||
| `payment.delete/unmatch/edit` | 418 | `notifierFacade->verifyClientActionToDo()` |
|
||||
| `client.edit` | 423 | Detecta cambios de tags/datos → `updatePasswordAntenaIfNeeded()`, `createStripeClient()`, `syncStripeCustomerData()`, `verifyClientActionToDo()` |
|
||||
| `client.add` | 539 | Solo log |
|
||||
| `service.edit` | 542 | `notifierFacade->verifyServiceActionToDo()` + `updatePasswordAntenaIfNeeded()` |
|
||||
| `service.suspend` | 552 | `notifierFacade->verifyServiceActionToDo()` |
|
||||
| `service.suspend_cancel` | 555 | `notifierFacade->verifyServiceActionToDo()` |
|
||||
| `service.postpone` | 558 | `notifierFacade->verifyServiceActionToDo()` |
|
||||
| `invoice.near_due` | 561 | `notifierFacade->notifyOverDue()` |
|
||||
| `invoice.overdue` | 564 | `notifierFacade->notifyOverDue()` |
|
||||
| `invoice.add` | 569 | `notifierFacade->verifyInvoiceActionToDo()` |
|
||||
| `invoice.edit` | 579 | `notifierFacade->verifyInvoiceActionToDo()` |
|
||||
| `invoice.add_draft` | 582 | `notifierFacade->verifyInvoiceActionToDo()` |
|
||||
| `invoice.draft_approved` | 585 | `notifierFacade->verifyInvoiceActionToDo()` |
|
||||
| `invoice.delete` | 588 | `notifierFacade->verifyInvoiceActionToDo()` |
|
||||
| `job.add` | 591 | PATCH title en UCRM con prefijo `[NOTIFICACION-PENDIENTE]` |
|
||||
| `job.edit` | 599 | Detecta cambios status/técnico/fecha → `notifierFacade->verifyJobActionToDo()` |
|
||||
|
||||
---
|
||||
|
||||
## UUIDs de Métodos de Pago (L.309–416)
|
||||
| UUID | Método |
|
||||
|---|---|
|
||||
| `11721cdf-...` | Cheque |
|
||||
| `6efe0fa8-...` | Efectivo |
|
||||
| `4145b5f5-...` | Transferencia bancaria |
|
||||
| `78e84000-...` | PayPal |
|
||||
| `6da98bb9-...` | Tarjeta de crédito PayPal |
|
||||
| `1dd098fa-...` | Stripe Credit Card (detecta sub-tipo por metadata) |
|
||||
| `b9e1e9d1-...` | Suscripción Stripe (tarjeta) |
|
||||
| `939f7701-...` | Suscripción PayPal |
|
||||
| `1c963e35-...` | MercadoPago |
|
||||
| `d8c1eae9-...` | Personalizado |
|
||||
| `72271b72-...` | Cortesía |
|
||||
| `b01c0b35-...` | OXXO Pay |
|
||||
| `93814765-...` | Tarjeta de crédito/débito |
|
||||
|
||||
---
|
||||
|
||||
## Tags de Cliente que disparan acciones (L.460–492)
|
||||
| Tag | Acción |
|
||||
|---|---|
|
||||
| `CREAR CLABE STRIPE` | `pluginNotifierFacade->createStripeClient(..., true)` |
|
||||
| `CREAR CLIENTE STRIPE` | `pluginNotifierFacade->createStripeClient(..., false)` |
|
||||
| `OBTENER PASSWORD ANTENA` | `pluginNotifierFacade->processClientPasswordAntenna()` |
|
||||
| `isLead: true→false` | Auto-crea cliente en Stripe |
|
||||
|
||||
---
|
||||
|
||||
## Notas de implementación
|
||||
- `job.edit` con `statusAfter==1` y `$isPending` en título dispara notificación
|
||||
- El flujo `oxxo.request` usa `fastcgi_finish_request()` para responder antes de generar el voucher
|
||||
- `client.edit` refresca datos del cliente vía UCRM API antes de sincronizar con CallBell
|
||||
153
INDEX_public.md
Executable file
153
INDEX_public.md
Executable file
@ -0,0 +1,153 @@
|
||||
# INDEX_public.md — public.php
|
||||
> **Archivo:** `public.php` | 2978 líneas | Portal web administrativo + endpoint público HTTP
|
||||
|
||||
---
|
||||
|
||||
## Propósito
|
||||
Archivo dual:
|
||||
1. **Endpoint REST/webhook** — recibe webhooks de Stripe, solicitudes OXXO, y llamadas AJAX del portal
|
||||
2. **SPA PHP** — interfaz web administrativa con 4 módulos (Instaladores, Notificaciones, Stripe, OXXO)
|
||||
|
||||
---
|
||||
|
||||
## Variables globales inicializadas (L.1–65)
|
||||
| Variable | Descripción |
|
||||
|---|---|
|
||||
| `$config` | Config del plugin cargada de `data/config.json` |
|
||||
| `$ucrmApi` | `UcrmApi` (SDK) con base_uri UCRM y apitoken |
|
||||
| `$paymentIntentService` | `PaymentIntentService` con stripeApiKey |
|
||||
| `$admins` | Array de admins UCRM `[id, nombre]` |
|
||||
| `$defaultStripeAdminId` | ID del admin "stripe" o primero de la lista |
|
||||
| `$nmsBaseUrl` | `https://{ipserver}/nms/api/v2.1` |
|
||||
| `$installersData` | JSON de instaladores desde config |
|
||||
|
||||
---
|
||||
|
||||
## Mapa de Endpoints / Actions
|
||||
|
||||
### POST con JSON body (L.22–35) — Webhook delegado a Plugin::run()
|
||||
| Condición | Acción |
|
||||
|---|---|
|
||||
| `isset($jsonData['uuid']) || isset($jsonData['eventName']) || isset($jsonData['type'])` | Instancia `Plugin` via DI container y llama `run()` |
|
||||
|
||||
---
|
||||
|
||||
### POST `?action=nms_login` (L.80–117) — Login NMS
|
||||
- **Body:** `{username, password}`
|
||||
- **Respuesta 200:** `{success, token, user}`
|
||||
- **Respuesta 201:** `{requires2FA, twoFactorToken}` → sigue con `nms_login_totp`
|
||||
- **Guarda token en:** `sessionStorage('nms_auth_token')` (frontend)
|
||||
|
||||
### POST `?action=nms_login_totp` (L.120–146) — Login TOTP 2FA
|
||||
- **Body:** `{twoFactorToken, ..., totpCode}`
|
||||
- **Respuesta 200:** `{success, token, user}`
|
||||
|
||||
### GET `?action=nms_verify_session` (L.148–173) — Verificar sesión NMS
|
||||
- **Header:** `x-auth-token`
|
||||
- **Llama:** `GET {nmsBaseUrl}/user`
|
||||
|
||||
---
|
||||
|
||||
### POST `$_POST['action']` (L.176–291)
|
||||
|
||||
| action | Línea | Params | Descripción |
|
||||
|---|---|---|---|
|
||||
| `save_installers` | 177 | `installers_data` (JSON) | Guarda JSON en config.json |
|
||||
| `resend_payment` | 191 | `paymentId` | Obtiene pago+cliente UCRM, hace loopback POST a sí mismo simulando webhook |
|
||||
| `resend_job_notification` | 238 | `jobId` | Simula webhook `job.edit` con status 0→1 y prefijo `[NOTIFICACION-PENDIENTE]` |
|
||||
| `create_intent` | 443 | `clientId, amount, stripeCustomerId, adminId` | `PaymentIntentService->createPaymentIntent()` |
|
||||
| `create_oxxo_intent` | 463 | `clientId, amount` | `PluginOxxoNotifierFacade->createOxxoPaymentIntent()` via DI |
|
||||
|
||||
---
|
||||
|
||||
### GET `?action=...` (L.293–438)
|
||||
|
||||
| action | Línea | Params GET | Descripción |
|
||||
|---|---|---|---|
|
||||
| `search_clients` | 296 | `q` | UCRM `GET clients?query=q&limit=6` |
|
||||
| `get_payments` | 307 | `clientId` | UCRM `GET payments?clientId=...&limit=20` + names de métodos |
|
||||
| `search_stripe` | 323 | `q` | `PaymentIntentService->searchClients(q)` |
|
||||
| `get_stripe_details` | 329 | `id` (clientId UCRM) | `PaymentIntentService->getClientDetails(id)` |
|
||||
| `get_stripe_history` | 339 | `stripeCustomerId` | Balance cash + últimos 10 PIs de transferencia |
|
||||
| `get_oxxo_history` | 355 | `stripeCustomerId` | Últimos 5 PIs OXXO de Stripe |
|
||||
| `get_installer_jobs` | 365 | `installerId` | UCRM `GET scheduling/jobs?assignedUserId=...&statuses=[0,1]&limit=50` |
|
||||
| `image` / `get_image` | 413 | `file` o `name` | Sirve imágenes de `/img/`, `/img/webp/`, `/vouchers_oxxo/` |
|
||||
|
||||
---
|
||||
|
||||
## HTML/UI — Estructura (L.481–2064)
|
||||
|
||||
### Variables PHP→JS inyectadas (L.2066–2075)
|
||||
```javascript
|
||||
NEEDS_LOGIN = true/false // Si sesión UCRM activa
|
||||
SYSTEM_USER_ID = null|int // ID usuario actual
|
||||
store.installers = [...] // Array de instaladores desde config
|
||||
store.crmUrl = 'https://...' // URL base API UCRM
|
||||
store.publicUrl = 'https://...'// URL pública CRM
|
||||
store.defaultStripeAdminId = '' // ID admin Stripe por defecto
|
||||
```
|
||||
|
||||
### Módulos de la UI (tabs)
|
||||
| Tab ID | Sección HTML | Descripción |
|
||||
|---|---|---|
|
||||
| `instaladores` | `#section-instaladores` | Tabla instaladores + jobs activos del instalador |
|
||||
| `notificaciones` | `#section-notificaciones` | Búsqueda cliente + tabla pagos + botón reenviar |
|
||||
| `pagos-spei` | `#section-pagos-spei` (en `views/stripe.php`) | Generador Stripe + historial |
|
||||
| `pagos-oxxo` | `#section-pagos-oxxo` (en `views/oxxo.php`) | Generador OXXO + historial |
|
||||
|
||||
### Funciones JS principales (L.2065+)
|
||||
| Función | Descripción |
|
||||
|---|---|
|
||||
| `toggleTheme()` | Cambia light/dark en `data-theme` + localStorage |
|
||||
| `showDashboard()` | Muestra menú principal, oculta módulos |
|
||||
| `showModule(id)` | Activa módulo por ID (mapea 'stripe'→'pagos-spei', etc.) |
|
||||
| `switchTab(tabName)` | Cambia tab activa y sección visible |
|
||||
| `showToast(msg, err)` | Toast temporal 3s |
|
||||
| `setupSearch(inputId, resultId, action, onSelect)` | Search debounced 300ms → fetch `?action=...` |
|
||||
| `renderRichSearchResults(container, data, query, cb)` | Renderiza resultados de búsqueda con highlight |
|
||||
| `highlightMatch(text, query)` | Resalta coincidencias en texto |
|
||||
| `loadPayments(id)` | Fetch `?action=get_payments&clientId=id` → tabla |
|
||||
| `refreshClientPayments()` | Recarga pagos del cliente actual |
|
||||
| `resendPayment(id)` | POST `action=resend_payment&paymentId=id` |
|
||||
| `loadOxxoHistory(stripeCustomerId)` | Fetch `?action=get_oxxo_history` → tabla |
|
||||
| `handleLogin()` | POST `?action=nms_login` → guarda token en sessionStorage |
|
||||
| `handleTotpLogin()` | POST `?action=nms_login_totp` |
|
||||
| `handleLogout()` | Limpia sessionStorage + reload |
|
||||
|
||||
### Funciones JS Instaladores
|
||||
| Función | Descripción |
|
||||
|---|---|
|
||||
| `openInstallerModal()` | Abre modal de nuevo instalador |
|
||||
| `fillInstallerData(select)` | Rellena campos desde select de admin |
|
||||
| `loadInstallerJobs(installerId)` | Fetch `?action=get_installer_jobs&installerId=...` |
|
||||
| `resendJobNotification(jobId)` | POST `action=resend_job_notification` |
|
||||
| `renderInstallersTable()` | Renderiza tabla desde `store.installers` |
|
||||
|
||||
---
|
||||
|
||||
## Includes externos (L.1992–1993)
|
||||
```php
|
||||
include __DIR__ . '/views/stripe.php'; // Sección y JS del módulo Stripe
|
||||
include __DIR__ . '/views/oxxo.php'; // Sección y JS del módulo OXXO
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Archivos que sirve el endpoint `image`
|
||||
- `/img/*.{png,webp,jpg}`
|
||||
- `/img/webp/*.{webp}`
|
||||
- `/vouchers_oxxo/*.{png,webp,jpg}`
|
||||
|
||||
## Login overlay (L.1711–1750)
|
||||
- Se muestra si `NEEDS_LOGIN = true` (usuario sin sesión UCRM activa)
|
||||
- Pide credenciales NMS (UISP), no UCRM
|
||||
- Soporta 2FA TOTP
|
||||
- Token guardado en `sessionStorage('nms_auth_token')`
|
||||
|
||||
---
|
||||
|
||||
## Notas de implementación
|
||||
- `resend_payment` hace un **loopback cURL** a sí mismo simulando webhook UCRM
|
||||
- `create_intent` usa `PaymentIntentService` que llama a Stripe SDK directo
|
||||
- El CSS (~1200 líneas) está inline en el archivo, soporta dark/light mode vía `data-theme`
|
||||
- Las vistas `stripe.php` y `oxxo.php` tienen su propio JS, leer esos archivos para modificar esos módulos
|
||||
160
PLUGIN_INDEX.md
Executable file
160
PLUGIN_INDEX.md
Executable file
@ -0,0 +1,160 @@
|
||||
# PLUGIN_INDEX.md — Índice Maestro
|
||||
> Plugin: **siip-whatsapp-notifications** | Versión actual: ver `manifest.json`
|
||||
> Este archivo es el punto de entrada para cualquier agente IA que trabaje en este plugin.
|
||||
|
||||
---
|
||||
|
||||
## 🧭 Guía rápida: ¿Qué índice leer?
|
||||
|
||||
| Tarea | Índices a leer | Tokens est. |
|
||||
|---|---|---|
|
||||
| Modificar lógica de hooks UCRM | `INDEX_plugin.md` + `INDEX_deps.md` | ~4,500 |
|
||||
| Agregar/modificar endpoint en el portal web | `INDEX_public.md` | ~3,000 |
|
||||
| Modificar módulo Stripe del portal | `INDEX_public.md` + leer `views/stripe.php` directamente | ~3,500 |
|
||||
| Modificar módulo OXXO del portal | `INDEX_public.md` + leer `views/oxxo.php` directamente | ~3,500 |
|
||||
| Nueva feature que usa API UCRM | `INDEX_api_used.md` + `INDEX_api_ucrm.md` | ~3,500 |
|
||||
| Nueva feature que usa API UNMS | `INDEX_api_used.md` + `INDEX_api_unms.md` | ~5,000 |
|
||||
| Bug en llamada API existente | `INDEX_api_used.md` solo | ~1,500 |
|
||||
| Modificar notificaciones WhatsApp | `INDEX_deps.md` (sección Facades) | ~2,000 |
|
||||
| Cambiar flujo de pago OXXO/Stripe | `INDEX_plugin.md` + `INDEX_deps.md` | ~4,500 |
|
||||
|
||||
> ⚡ **Tip:** Después de leer el índice correspondiente, usa `StartLine/EndLine` para leer solo el fragmento del archivo fuente que necesitas modificar, en lugar de leer el archivo completo.
|
||||
|
||||
---
|
||||
|
||||
## 📁 Estructura del Plugin
|
||||
|
||||
```
|
||||
siip-whatsapp-notifications/
|
||||
│
|
||||
├── 📄 public.php (2978 líneas) — Portal web + endpoint HTTP público
|
||||
├── 📄 src/Plugin.php (672 líneas) — Entry point de hooks UCRM
|
||||
│
|
||||
├── src/
|
||||
│ ├── Facade/
|
||||
│ │ ├── AbstractMessageNotifierFacade.php — Base notificaciones WhatsApp
|
||||
│ │ ├── AbstractStripeOperationsFacade.php — Lógica Stripe + registro pagos UCRM
|
||||
│ │ ├── AbstractOxxoOperationsFacade.php — Lógica OXXO Pay + vouchers
|
||||
│ │ ├── PluginNotifierFacade.php — Facade principal (extiende Stripe)
|
||||
│ │ ├── PluginOxxoNotifierFacade.php — Facade OXXO
|
||||
│ │ ├── TwilioNotifierFacade.php — Legacy Twilio
|
||||
│ │ └── ClientCallBellAPI.php — Cliente Callbell (81KB)
|
||||
│ │
|
||||
│ ├── Service/
|
||||
│ │ ├── PaymentIntentService.php — Stripe SDK: PIs, historial, balance
|
||||
│ │ ├── SmsNumberProvider.php — Extrae teléfonos WhatsApp de contactos UCRM
|
||||
│ │ ├── Logger.php — PSR-3 logger
|
||||
│ │ ├── OptionsManager.php — Carga config → PluginData
|
||||
│ │ ├── MinioStorageService.php — Sube vouchers a MinIO
|
||||
│ │ ├── CurlExecutor.php — Wrapper cURL
|
||||
│ │ └── PluginDataValidator.php — Valida configuración
|
||||
│ │
|
||||
│ ├── Data/
|
||||
│ │ ├── PluginData.php — DTO config del plugin
|
||||
│ │ ├── NotificationData.php — DTO payload webhook UCRM
|
||||
│ │ └── UcrmData.php — Base DTO
|
||||
│ │
|
||||
│ └── Factory/
|
||||
│ ├── NotificationDataFactory.php — Construye NotificationData desde JSON
|
||||
│ └── MessageTextFactory.php — Genera textos de mensajes WhatsApp
|
||||
│
|
||||
├── views/
|
||||
│ ├── stripe.php (17KB) — HTML+JS del módulo Stripe del portal
|
||||
│ └── oxxo.php (12KB) — HTML+JS del módulo OXXO del portal
|
||||
│
|
||||
├── scripts-uisp/ — Scripts auxiliares UNMS (audit_client_passwords.php, etc.)
|
||||
├── data/ — config.json, logs, ordenes OXXO
|
||||
├── vendor/ — Dependencias Composer
|
||||
├── img/ — Imágenes del portal (logos, íconos)
|
||||
└── vouchers_oxxo/ — Vouchers OXXO generados
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Archivos de Índice
|
||||
|
||||
| Archivo | Describe | Tamaño |
|
||||
|---|---|---|
|
||||
| [`INDEX_plugin.md`](INDEX_plugin.md) | `src/Plugin.php`: flujos, eventos UCRM, UUIDs de métodos de pago | ~5KB |
|
||||
| [`INDEX_public.md`](INDEX_public.md) | `public.php`: todos los endpoints, acciones JS, módulos UI | ~6KB |
|
||||
| [`INDEX_deps.md`](INDEX_deps.md) | Facades y Services: firmas de métodos y propósito | ~7KB |
|
||||
| [`INDEX_api_used.md`](INDEX_api_used.md) | Todas las llamadas API reales que hace el plugin | ~4KB |
|
||||
| [`INDEX_api_ucrm.md`](INDEX_api_ucrm.md) | Catálogo de 162 endpoints UCRM CRM v1.0 | ~10KB |
|
||||
| [`INDEX_api_unms.md`](INDEX_api_unms.md) | Catálogo de 560 endpoints UNMS NMS v2.1 | ~74KB |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Scripts de utilidad
|
||||
|
||||
| Script | Uso |
|
||||
|---|---|
|
||||
| `.agent/scripts/gen_index_unms_swagger.php` | `php gen_index_unms_swagger.php` — regenera `INDEX_api_unms.md` desde `unms-swagger.json` |
|
||||
| `.agent/scripts/gen_index_ucrm_apib.php` | `php gen_index_ucrm_apib.php` — regenera `INDEX_api_ucrm.md` desde `unmscrm.apib` |
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuración del Plugin (claves en `data/config.json`)
|
||||
|
||||
| Clave | Tipo | Descripción |
|
||||
|---|---|---|
|
||||
| `ipserver` | string | IP/hostname del servidor UISP |
|
||||
| `apitoken` | string | Token API UCRM |
|
||||
| `unmsApiToken` | string | Token API UNMS/NMS |
|
||||
| `tokencallbell` | string | Token API Callbell |
|
||||
| `tokenstripe` | string | API Key Stripe |
|
||||
| `ipPuppeteer` | string | IP microservicio Puppeteer para vouchers |
|
||||
| `portPuppeteer` | string | Puerto microservicio Puppeteer |
|
||||
| `idPaymentAdminCRM` | string | ID admin UCRM para registrar pagos |
|
||||
| `installersDataWhatsApp` | JSON string | `{"instaladores":[{id, nombre, whatsapp}]}` |
|
||||
| `notificationTypeText` | bool | `true`=texto plano, `false`=plantilla Callbell |
|
||||
| `debugMode` | bool | Activa logging DEBUG |
|
||||
| `cashPaymentMethodId` | bool | Habilitar notif. efectivo |
|
||||
| `bankTransferPaymentMethodId` | bool | Habilitar notif. transferencia bancaria |
|
||||
| `oxxoPayPaymentMethodId` | bool | Habilitar notif. OXXO |
|
||||
| `creditCardStripePaymentMethodId` | bool | Habilitar notif. tarjeta Stripe |
|
||||
| *(más métodos de pago)* | bool | Ver `PluginData.php` para lista completa |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Flujos principales resumidos
|
||||
|
||||
### Flujo: Pago registrado en UCRM
|
||||
```
|
||||
UCRM → POST public.php (uuid presente) → Plugin::run() → processHttpRequest()
|
||||
→ NotificationDataFactory::getObject() → event: payment.add
|
||||
→ switch(methodId) → notifierFacade::verifyPaymentActionToDo()
|
||||
→ SmsNumberProvider::getUcrmClientNumbers() → ClientCallBellAPI::sendPaymentNotificationWhatsApp()
|
||||
```
|
||||
|
||||
### Flujo: Transferencia bancaria Stripe (SPEI)
|
||||
```
|
||||
Stripe → POST public.php (type: customer_cash_balance_transaction.created → funded)
|
||||
→ Plugin::run() → pluginNotifierFacade::createPaymentIntent()
|
||||
→ AbstractStripeOperationsFacade: crea PI en Stripe + registra pago en UCRM
|
||||
```
|
||||
|
||||
### Flujo: Solicitud OXXO Pay
|
||||
```
|
||||
Bot/Cliente → POST public.php (type: oxxo.request)
|
||||
→ Plugin::run() → pluginOxxoNotifierFacade::createStripeReference()
|
||||
→ createOxxoOrder() → responde inmediatamente → fastcgi_finish_request()
|
||||
→ [background] generateOxxoVoucher() → Puppeteer → MinIO
|
||||
```
|
||||
|
||||
### Flujo: Tarea de Instalador
|
||||
```
|
||||
UCRM → POST public.php (uuid, event: job.edit, status 0→1)
|
||||
→ notifierFacade::verifyJobActionToDo()
|
||||
→ ClientCallBellAPI: notifica cliente + técnico vía WhatsApp
|
||||
→ PATCH job title en UCRM (remueve/actualiza prefijo [NOTIFICACION-PENDIENTE])
|
||||
```
|
||||
|
||||
### Flujo: Obtener Password Antena
|
||||
```
|
||||
UCRM client.edit + tag "OBTENER PASSWORD ANTENA" agregado
|
||||
→ Plugin::processHttpRequest() → pluginNotifierFacade::processClientPasswordAntenna()
|
||||
→ AbstractMessageNotifierFacade::getVaultCredentialsByClientId()
|
||||
→ UNMS GET devices?siteId → GET vault/{id}/credentials
|
||||
→ UCRM PATCH clients/{id} (atributo passwordAntenaCliente)
|
||||
→ removeTagFromClient("OBTENER PASSWORD ANTENA")
|
||||
```
|
||||
30
README.md
30
README.md
@ -1,12 +1,40 @@
|
||||
# SIIP - WhatsApp Notifications & Integrated Payment Portal
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Este plugin es una solución integral que transforma tu UCRM en un **Portal Administrativo de Última Generación**. No solo automatiza la comunicación por WhatsApp, sino que integra un Dashboard completo para la gestión de pagos online (Stripe/OXXO), visualización de comprobantes y coordinación de equipos técnicos.
|
||||
|
||||
## ✨ Novedades v4.7.5 (Embedded Mode Integration)
|
||||
|
||||
- **🖼️ Modo Embebido en Portal Unificado**: Soporte completo para renderizado transparente dentro del iframe de `siip-cashier-tools-ui` (`embedded=1`), ocultando la cabecera y el menú principal para evitar duplicidades visuales.
|
||||
|
||||
## ✨ Novedades v4.7.4 (Offline CPE Password Preservation Hotfix)
|
||||
|
||||
- **🔑 Preservación de Clave de Antena Desconectada**: Se añadió una capa de validación que detecta si el CPE del cliente en UISP está fuera de línea. Si ya existía una contraseña válida en UCRM, el script evita sobrescribirla con la leyenda de error por desconexión. Esto preserva la credencial de acceso para que los instaladores puedan conectarse físicamente al equipo y alinearlo o repararlo en sitio.
|
||||
|
||||
## ✨ Novedades v4.7.3 (Antenna Password Tag Fixes, Overwrite Protections & Sync)
|
||||
|
||||
- **🔄 Sincronización CallBell Inmediata**: Los datos del cliente se refrescan de forma activa en el objeto de notificación antes de llamar a la sincronización, garantizando que el nuevo Site y contraseña de la antena se actualicen en CallBell inmediatamente en el mismo evento del webhook.
|
||||
- **🏷️ Robustez en Detección de Etiquetas**: Modificada la detección de tags en `client.edit` (`OBTENER PASSWORD ANTENA`, etc.) para leer directamente del objeto `$notification->clientData['tags']`, logrando compatibilidad con los webhooks estándar de producción en UCRM (los cuales no proveen la clave `extraData`).
|
||||
- **🛡️ Protección contra Sobrescrituras de Red**: Agregadas validaciones inteligentes en los actualizadores (`audit_client_passwords.php` y `ejemplo_script_actualizador.php`) para evitar que caídas de red o errores de asociación de la API de UISP reemplacen atributos del CRM con valores genéricos destructivos (como `"Sin SITE"` o `"REPETIDOR"`) si ya existían datos de sector válidos.
|
||||
- **📝 Integración de Log de Errores**: Integrado el logger principal del plugin con el script de auditoría de UISP para mostrar de forma amigable cualquier error de conexión con la API en el panel del plugin de UCRM (`data/plugin.log`).
|
||||
- **📡 Resolución Inteligente de Site (AP Fallback)**: En redes de UISP sin jerarquías Padre-Hijo estructuradas (donde no hay parent site directo en el dispositivo del cliente), el plugin ahora recupera y asocia dinámicamente el Site de la Torre a partir del AP (Access Point) al que está conectada la antena.
|
||||
|
||||
|
||||
## ✨ Novedades v4.7.1 (Robustness & Scheduling Filters)
|
||||
|
||||
- **🛡️ Robustez de UI de Pestañas**: Se eliminó la búsqueda y dependencias en los eventos `onclick` inline (que se bloqueaban por CSP o eran alterados por minificadores de HTML de UCRM en producción) y se implementó un sistema basado en atributos nativos `data-tab` para la navegación segura entre los módulos del portal.
|
||||
- **👷 Filtro de Tareas de Técnicos**: Se actualizó la llamada de API de scheduling para traer únicamente los tickets que se encuentran en estatus **Abierto** (`0`) o **En curso/Pendiente** (`1`), ocultando correctamente aquellos tickets ya solucionados o cerrados (`2`).
|
||||
|
||||
## ✨ Novedades v4.7.0 (Payment Events & Size Optimization)
|
||||
|
||||
- **🔄 Sincronización Completa de Eventos de Pagos**: Soporte para la sincronización automática en Callbell ante eventos de edición (`payment.edit`), desvinculación (`payment.unmatch`) y eliminación (`payment.delete`) de pagos en UCRM, manteniendo los saldos de clientes siempre actualizados.
|
||||
- **🛡️ Estabilidad Anti-404**: Mapeo directo de la entidad desde el payload del webhook para evitar errores HTTP 404 al consultar pagos que ya fueron eliminados.
|
||||
- **⚡ Reducción Masiva de Tamaño**: Eliminación del SDK de Google no utilizado y optimización de exclusiones en el script de empaquetado, reduciendo la carpeta `vendor` a 28 MB y el tamaño del archivo ZIP empaquetado a solo **8.1 MB** (anteriormente ~45 MB).
|
||||
|
||||
## ✨ Novedades v4.6.0 (Stripe & OXXO Stability)
|
||||
|
||||
- **🛡️ Estabilidad en Pagos (Stripe CashBalance)**: Nuevo sistema para comprobar fondos y validación contra intención de pagos para evitar las intenciones huérfanas o duplicadas tras recibir transferencias SPEI.
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
"katzgrau/klogger": "^1.2",
|
||||
"stripe/stripe-php": "^13.11",
|
||||
"ext-imagick": "*",
|
||||
"google/apiclient": "^2.0",
|
||||
"aws/aws-sdk-php": "^3.0"
|
||||
}
|
||||
}
|
||||
|
||||
890
composer.lock
generated
890
composer.lock
generated
File diff suppressed because it is too large
Load Diff
1
comprobantes/.gitkeep
Executable file
1
comprobantes/.gitkeep
Executable file
@ -0,0 +1 @@
|
||||
# Keep directory in git
|
||||
12437
data/plugin.log
12437
data/plugin.log
File diff suppressed because one or more lines are too long
BIN
img/installer-jobs.webp
Normal file
BIN
img/installer-jobs.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
BIN
img/payments-notifications.webp
Normal file
BIN
img/payments-notifications.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
img/webp/installer-jobs.webp
Normal file
BIN
img/webp/installer-jobs.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
BIN
img/webp/payments-notifications.webp
Normal file
BIN
img/webp/payments-notifications.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
@ -5,13 +5,48 @@
|
||||
"displayName": "SIIP - Procesador de Pagos en línea con Stripe, Oxxo y Transferencia, Sincronizador de CallBell y Envío de Notificaciones y comprobantes vía WhatsApp",
|
||||
"description": "Este plugin sincroniza los clientes del sistema UISP CRM con los contactos de WhatsApp en CallBell, además procesa pagos de Stripe como las trasferencias bancarias y genera referencias de pago vía OXXO, además envía comprobantes de pago en formato imagen PNG o texto vía Whatsapp a los clientes",
|
||||
"url": "https://siip.mx/",
|
||||
"version": "4.6.0",
|
||||
"version": "4.7.5",
|
||||
"unmsVersionCompliancy": {
|
||||
"min": "2.1.0",
|
||||
"max": null
|
||||
},
|
||||
"author": "SIIP INTERNET",
|
||||
"changelog": [
|
||||
{
|
||||
"version": "4.7.5",
|
||||
"date": "2026-08-02",
|
||||
"changes": "Actualización: Soporte para Modo Embebido (embedded=1 / is-embedded) en el Portal Unificado de Cajas (siip-cashier-tools-ui) con ocultamiento de cabecera y barra de menú al cargarse dentro del iframe."
|
||||
},
|
||||
{
|
||||
"version": "4.7.4",
|
||||
"date": "2026-07-15",
|
||||
"changes": "Corrección: Preservación de contraseñas de antena válidas en UCRM cuando el dispositivo se encuentra desconectado (offline) en UISP, evitando que la clave se sobrescriba por un mensaje de error y garantizando que el instalador pueda recibirla en las notificaciones."
|
||||
},
|
||||
{
|
||||
"version": "4.7.3",
|
||||
"date": "2026-07-13",
|
||||
"changes": "Actualización y Corrección: Detección robusta de etiquetas (OBTENER PASSWORD ANTENA) utilizando clientData en producción para compatibilidad con webhooks estándar. Integración del logger del plugin con el script de auditoría de UISP. Prevención de sobrescritura de datos de Site/Antena por genéricos ante caídas de la API. Refresco en tiempo real del objeto de notificación previo a la sincronización con CallBell. Resolución de Site (Torre) UISP mediante fallback del Punto de Acceso (AP) para redes sin jerarquías Padre-Hijo."
|
||||
},
|
||||
{
|
||||
"version": "4.7.2",
|
||||
"date": "2026-06-10",
|
||||
"changes": "Actualización: Ordenamiento descendente (de más reciente a más antiguo) de la fecha en la tabla de tareas/tickets del instalador."
|
||||
},
|
||||
{
|
||||
"version": "4.7.1",
|
||||
"date": "2026-06-05",
|
||||
"changes": "Hotfix: Resolución de error en navegación de pestañas (tabs) en producción mediante atributos data-tab (evitando bloqueos por CSP o alteración de comillas en onclick por minificadores de UCRM) y corrección del filtro en tareas de instaladores para retornar únicamente tickets con estatus Abierto (0) o En curso (1)."
|
||||
},
|
||||
{
|
||||
"version": "4.7.0",
|
||||
"date": "2026-06-04",
|
||||
"changes": "Actualización: Soporte para sincronización de clientes en Callbell ante la eliminación (payment.delete), desvinculación (payment.unmatch) y edición (payment.edit) de pagos. Optimización masiva del tamaño del plugin (reducción del 82% en ZIP) eliminando dependencias no utilizadas (google/apiclient) y excluyendo archivos temporales y locales."
|
||||
},
|
||||
{
|
||||
"version": "4.6.1",
|
||||
"date": "2026-05-11",
|
||||
"changes": "Hotfix: Resolución de errores de sintaxis (backticks) en el portal de pagos que bloqueaban la navegación del menú administrativo y mejora de mensajes de estado de pago."
|
||||
},
|
||||
{
|
||||
"version": "4.6.0",
|
||||
"date": "2026-04-11",
|
||||
|
||||
492
public.php
492
public.php
@ -1,5 +1,62 @@
|
||||
<?php
|
||||
|
||||
// Endpoint de Autodetección de Módulos (para portal unificado siip-cashier-tools-ui)
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'get_modules_list') {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'plugin' => 'whatsapp',
|
||||
'modules' => [
|
||||
[
|
||||
'key' => 'notifications',
|
||||
'title' => '💬 Notificaciones WhatsApp',
|
||||
'sub' => 'SIIP WhatsApp Notifications',
|
||||
'description' => 'Re-envía notificaciones de pago e instaladores vía WhatsApp.',
|
||||
'image' => '?action=get_image&name=payments-notifications.webp',
|
||||
'tab' => 'notifications',
|
||||
'plugin' => 'whatsapp'
|
||||
],
|
||||
[
|
||||
'key' => 'installers',
|
||||
'title' => '🔧 Administrar Instaladores',
|
||||
'sub' => 'SIIP WhatsApp Notifications',
|
||||
'description' => 'Alta, edición y baja del catálogo de técnicos instaladores (CRUD).',
|
||||
'image' => '?action=get_image&name=installers-management.png',
|
||||
'tab' => 'installers',
|
||||
'plugin' => 'whatsapp'
|
||||
],
|
||||
[
|
||||
'key' => 'installer-jobs',
|
||||
'title' => '📋 Reenviar Notificaciones de Asignación de Servicios en Curso',
|
||||
'sub' => 'SIIP WhatsApp Notifications',
|
||||
'description' => 'Consulta de servicios en curso por técnico y reenvío de notificaciones de asignación de servicio por WhatsApp.',
|
||||
'image' => '?action=get_image&name=installer-jobs.webp',
|
||||
'tab' => 'installer-jobs',
|
||||
'plugin' => 'whatsapp'
|
||||
],
|
||||
[
|
||||
'key' => 'stripe',
|
||||
'title' => '💳 Generador de Intenciones de Pago Stripe',
|
||||
'sub' => 'SIIP WhatsApp Notifications',
|
||||
'description' => 'Genera intenciones de pago en línea personalizadas para liberar saldo no reflejado en la cuenta del cliente en UISP CRM.',
|
||||
'image' => '?action=get_image&name=stripe-logo.png',
|
||||
'tab' => 'stripe',
|
||||
'plugin' => 'whatsapp'
|
||||
],
|
||||
[
|
||||
'key' => 'oxxo',
|
||||
'title' => '🏪 Pagos OXXO',
|
||||
'sub' => 'SIIP WhatsApp Notifications',
|
||||
'description' => 'Genera fichas de pago OXXO Pay para cobranza en caja.',
|
||||
'image' => '?action=get_image&name=oxxo-logo.png',
|
||||
'tab' => 'oxxo',
|
||||
'plugin' => 'whatsapp'
|
||||
]
|
||||
]
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
chdir(__DIR__);
|
||||
@ -339,9 +396,12 @@ if (isset($_GET['action'])) {
|
||||
if ($_GET['action'] === 'get_stripe_history') {
|
||||
$stripeCustomerId = $_GET['stripeCustomerId'] ?? $_GET['customerId'] ?? null;
|
||||
if ($stripeCustomerId) {
|
||||
// getCustomerCashBalance returns MXN (already /100), multiply by 100 so JS can do /100
|
||||
$cashBalanceMxn = $paymentIntentService->getCustomerCashBalance($stripeCustomerId);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'payments' => $paymentIntentService->getLastPayments($stripeCustomerId, 10)
|
||||
'success' => true,
|
||||
'payments' => $paymentIntentService->getLastPayments($stripeCustomerId, 10),
|
||||
'cashBalance' => $cashBalanceMxn * 100 // centavos, JS divides by 100
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing customer id']);
|
||||
@ -368,7 +428,7 @@ if (isset($_GET['action'])) {
|
||||
try {
|
||||
$jobs = $ucrmApi->get('scheduling/jobs', [
|
||||
'assignedUserId' => $installerId,
|
||||
'statuses[]' => 1,
|
||||
'statuses' => [0, 1],
|
||||
'limit' => 50
|
||||
]);
|
||||
$result = [];
|
||||
@ -391,6 +451,15 @@ if (isset($_GET['action'])) {
|
||||
'description' => mb_substr($job['description'] ?? '', 0, 80)
|
||||
];
|
||||
}
|
||||
// Ordenar por fecha descendente (de más reciente a más antiguo)
|
||||
usort($result, function ($a, $b) {
|
||||
$dateA = $a['date'] ?? '';
|
||||
$dateB = $b['date'] ?? '';
|
||||
if ($dateA === $dateB) {
|
||||
return 0;
|
||||
}
|
||||
return ($dateA > $dateB) ? -1 : 1;
|
||||
});
|
||||
echo json_encode($result);
|
||||
} catch (\Exception $e) {
|
||||
echo json_encode([]);
|
||||
@ -474,7 +543,70 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SIIP - Notificaciones y Pagos</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap" rel="stylesheet">
|
||||
<script>
|
||||
// Embedded mode & Session Inheritance
|
||||
(function() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.get('embedded') === '1') {
|
||||
document.documentElement.classList.add('is-embedded');
|
||||
if (window.parent && window.parent !== window) {
|
||||
try {
|
||||
const parentToken = window.parent.sessionStorage.getItem('nms_auth_token');
|
||||
const parentUser = window.parent.sessionStorage.getItem('nms_user');
|
||||
if (parentToken && !sessionStorage.getItem('nms_auth_token')) {
|
||||
sessionStorage.setItem('nms_auth_token', parentToken);
|
||||
}
|
||||
if (parentUser && !sessionStorage.getItem('nms_user')) {
|
||||
sessionStorage.setItem('nms_user', parentUser);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const loginOverlay = document.getElementById('loginOverlay');
|
||||
if (loginOverlay) {
|
||||
loginOverlay.style.display = 'none';
|
||||
loginOverlay.classList.add('hidden');
|
||||
}
|
||||
const tabParam = urlParams.get('tab') || urlParams.get('module');
|
||||
if (tabParam && typeof showModule === 'function') {
|
||||
showModule(tabParam);
|
||||
}
|
||||
if (urlParams.get('hide_nav') === '1') {
|
||||
const tabsNav = document.querySelector('.tabs');
|
||||
if (tabsNav) tabsNav.style.display = 'none';
|
||||
}
|
||||
const themeParam = urlParams.get('theme');
|
||||
if (themeParam) {
|
||||
document.documentElement.setAttribute('data-theme', themeParam);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
/* Embedded Mode Support */
|
||||
html.is-embedded #loginOverlay,
|
||||
html.is-embedded .header,
|
||||
html.is-embedded #mainDashboard {
|
||||
display: none !important;
|
||||
}
|
||||
html.is-embedded body {
|
||||
padding: 0.5rem !important;
|
||||
background-color: var(--bg-body);
|
||||
}
|
||||
html.is-embedded .container {
|
||||
padding: 0 !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
html.is-embedded #moduleView {
|
||||
display: block !important;
|
||||
}
|
||||
html.is-embedded .tabs {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-hover: #1d4ed8;
|
||||
@ -1792,22 +1924,27 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
<div class="menu-container" id="mainDashboard">
|
||||
<div class="menu-item" onclick="showModule('installers')">
|
||||
<img src="?action=image&file=instalador.png">
|
||||
<h3>Instaladores</h3>
|
||||
<p>Administra los técnicos e instaladores registrados en el sistema.</p>
|
||||
<h3>Administrar Instaladores</h3>
|
||||
<p>Administra los datos de contacto y registro de los instaladores técnicos (CRUD).</p>
|
||||
</div>
|
||||
<div class="menu-item" onclick="showModule('installer-jobs')">
|
||||
<img src="?action=image&file=installer-jobs.webp">
|
||||
<h3>Reenviar Notificaciones de Asignación de Servicios en Curso</h3>
|
||||
<p>Consulta y re-envía manualmente notificaciones de servicios en curso por WhatsApp a los instaladores</p>
|
||||
</div>
|
||||
<div class="menu-item" onclick="showModule('notifications')">
|
||||
<img src="?action=image&file=whatsapp-logo.png">
|
||||
<h3>Notificaciones</h3>
|
||||
<p>Re-envía manualmente notificaciones de pago vía WhatsApp.</p>
|
||||
<img src="?action=image&file=payments-notifications.webp">
|
||||
<h3>Reenviar Notificaciones de pago vía WhatsApp</h3>
|
||||
<p>Re-envía manualmente notificaciones de pago a clientes vía WhatsApp.</p>
|
||||
</div>
|
||||
<div class="menu-item" onclick="showModule('stripe')">
|
||||
<img src="?action=image&file=stripe-logo.png">
|
||||
<h3>Pagos SPEI</h3>
|
||||
<p>Genera referencias de Transferencia Bancaria personalizadas.</p>
|
||||
<h3>Generador de Intenciones de Pago Stripe</h3>
|
||||
<p>Genera intenciones de pago vía Stripe personalizadas para liberar saldo no reflejado en la cuenta del cliente en UISP CRM.</p>
|
||||
</div>
|
||||
<div class="menu-item" onclick="showModule('oxxo')">
|
||||
<img src="?action=image&file=oxxo-logo.png">
|
||||
<h3>Pagos OXXO</h3>
|
||||
<h3>Generador de fichas OXXO</h3>
|
||||
<p>Genera fichas y códigos de barras para pago en tiendas OXXO.</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -1816,166 +1953,26 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
<div id="moduleView" class="hidden">
|
||||
<!-- Tabs Navigation -->
|
||||
<nav class="tabs">
|
||||
<a href="#" class="tab active" onclick="switchTab('instaladores'); return false;">
|
||||
<img src="?action=get_image&name=installers-management.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Instaladores
|
||||
<a href="#" class="tab active" data-tab="instaladores" onclick="switchTab('instaladores'); return false;">
|
||||
<img src="?action=get_image&name=installers-management.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Administrar Instaladores
|
||||
</a>
|
||||
<a href="#" class="tab" onclick="switchTab('notificaciones'); return false;">
|
||||
<img src="?action=get_image&name=whatsapp-notification.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Notificaciones
|
||||
<a href="#" class="tab" data-tab="installer-jobs" onclick="switchTab('installer-jobs'); return false;">
|
||||
<img src="?action=get_image&name=installers-management.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Reenviar Notificaciones de Asignación de Servicios en Curso
|
||||
</a>
|
||||
<a href="#" class="tab" onclick="switchTab('pagos-spei'); return false;">
|
||||
<img src="?action=get_image&name=online-payments-stripe.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Pagos SPEI
|
||||
<a href="#" class="tab" data-tab="notificaciones" onclick="switchTab('notificaciones'); return false;">
|
||||
<img src="?action=get_image&name=payments-notifications.webp" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Reenviar Notificaciones de pago vía WhatsApp
|
||||
</a>
|
||||
<a href="#" class="tab" onclick="switchTab('pagos-oxxo'); return false;">
|
||||
<img src="?action=get_image&name=oxxo-payments.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Pagos OXXO
|
||||
<a href="#" class="tab" data-tab="pagos-spei" onclick="switchTab('pagos-spei'); return false;">
|
||||
<img src="?action=get_image&name=online-payments-stripe.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Generador de Intenciones de Pago Stripe
|
||||
</a>
|
||||
<a href="#" class="tab" data-tab="pagos-oxxo" onclick="switchTab('pagos-oxxo'); return false;">
|
||||
<img src="?action=get_image&name=oxxo-payments.png" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;"> Generador de fichas OXXO
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<!-- MODULE 1: INSTALLERS -->
|
||||
<section id="section-instaladores" class="section-view active">
|
||||
<div class="card">
|
||||
<div style="margin-bottom: 2rem;">
|
||||
<h2 style="margin: 0; display: flex; align-items: center; gap: 10px;">
|
||||
<img src="?action=get_image&name=installers-management.png" style="width:64px;height:64px;"> Gestión de Instaladores
|
||||
</h2>
|
||||
<p style="color: var(--text-muted); margin: 5px 0 0 0;">👷 Administra tu equipo de técnicos y asigna instalaciones de manera eficiente</p>
|
||||
</div>
|
||||
|
||||
<!-- CONFIG CONTAINER -->
|
||||
<!-- CONFIG CONTAINER -->
|
||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 2rem;">
|
||||
<button class="btn btn-primary" onclick="openInstallerModal()" style="height: 42px;">
|
||||
+ Nuevo Instalador
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="overflow-x: auto;">
|
||||
<table id="installersTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Nombre</th>
|
||||
<th>WhatsApp</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="installersEmptyState" class="placeholder-state" style="display: none;">
|
||||
<span class="placeholder-icon">🔍</span>
|
||||
<p>No se encontraron instaladores</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JOBS ACTIVOS DEL INSTALADOR -->
|
||||
<div class="card" style="margin-top: 2rem;">
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<h2 id="jobsSectionTitle" style="margin: 0; display: flex; align-items: center; gap: 10px;">
|
||||
📋 Tareas Activas del Instalador
|
||||
</h2>
|
||||
<p style="color: var(--text-muted); margin: 5px 0 0 0;">Selecciona un instalador con el botón 📋 para ver sus tareas "En curso" y reenviar notificaciones</p>
|
||||
</div>
|
||||
|
||||
<div id="jobsEmptyState" class="placeholder-state">
|
||||
<span class="placeholder-icon">👷</span>
|
||||
<p>Selecciona un instalador para ver sus tareas activas</p>
|
||||
</div>
|
||||
|
||||
<div id="jobsLoading" style="display: none; text-align: center; padding: 2rem; color: var(--text-muted);">
|
||||
<div class="loader" style="margin: 0 auto 1rem;"></div>
|
||||
<p>Cargando tareas...</p>
|
||||
</div>
|
||||
|
||||
<div id="jobsTableWrapper" style="display: none; overflow-x: auto;">
|
||||
<table id="installerJobsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Folio</th>
|
||||
<th>Cliente</th>
|
||||
<th>Fecha</th>
|
||||
<th>Descripción</th>
|
||||
<th>Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="jobsNoResults" class="placeholder-state" style="display: none;">
|
||||
<span class="placeholder-icon">✅</span>
|
||||
<p>Este instalador no tiene tareas "En curso"</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- MODULE 2: NOTIFICATIONS -->
|
||||
<section id="section-notificaciones" class="section-view">
|
||||
<div class="card">
|
||||
<div style="margin-bottom: 2rem;">
|
||||
<h2 style="margin: 0; display: flex; align-items: center; gap: 10px;">
|
||||
<img src="?action=get_image&name=whatsapp-notification.png" style="width:64px;height:64px;"> Notificaciones WhatsApp
|
||||
</h2>
|
||||
<p style="color: var(--text-muted); margin: 5px 0 0 0;">📱 Busca clientes y envía comprobantes de pago directamente a su WhatsApp</p>
|
||||
</div>
|
||||
|
||||
<!-- CONFIG CONTAINER -->
|
||||
<div class="config-container">
|
||||
<div class="form-group" style="position: relative;">
|
||||
<label>Buscar Cliente (Nombre, Email o ID)</label>
|
||||
<div class="search-wrapper">
|
||||
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||
</svg>
|
||||
<input type="text" id="clientSearch" class="form-control search-input-padded" placeholder="Escribe para buscar (ej. Juan Perez)..." autocomplete="off">
|
||||
</div>
|
||||
<div id="searchResults" class="search-results"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PLACEHOLDER STATE (Initially Visible) -->
|
||||
<div id="notificationsPlaceholder" class="placeholder-state">
|
||||
<span class="placeholder-icon">👋</span>
|
||||
<p>Busca un cliente arriba para ver sus pagos y enviar notificaciones</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="paymentsContainer" class="card" style="display: none;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 10px;">
|
||||
<h3 style="margin: 0; display: flex; align-items: center;">
|
||||
<img src="?action=image&file=client.webp" class="client-header-icon"> <span id="selectedClientName">Pagos del Cliente</span>
|
||||
</h3>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<a id="btnNotifCrm" href="#" target="_blank" class="btn btn-uniform" style="height: 38px;">
|
||||
<img src="?action=image&file=crm.webp" class="icon-crm"> Ver en CRM
|
||||
</a>
|
||||
<button class="btn btn-secondary" onclick="refreshClientPayments()" style="height: 38px;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M23 4v6h-6" />
|
||||
<path d="M1 20v-6h6" />
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
||||
</svg>
|
||||
Refrescar Pagos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x: auto;">
|
||||
<table id="paymentsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Fecha</th>
|
||||
<th>Monto</th>
|
||||
<th>Método</th>
|
||||
<th>Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php include __DIR__ . '/views/installers.php'; ?>
|
||||
<?php include __DIR__ . '/views/installer_jobs.php'; ?>
|
||||
<?php include __DIR__ . '/views/notifications.php'; ?>
|
||||
|
||||
<?php include __DIR__ . '/views/stripe.php'; ?>
|
||||
<?php include __DIR__ . '/views/oxxo.php'; ?>
|
||||
@ -1994,11 +1991,21 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px 12px 0 0;
|
||||
">
|
||||
<?php
|
||||
$manifestPath = __DIR__ . '/manifest.json';
|
||||
$pluginVersion = 'Desconocida';
|
||||
if (file_exists($manifestPath)) {
|
||||
$manifestData = json_decode(file_get_contents($manifestPath), true);
|
||||
if (isset($manifestData['information']['version'])) {
|
||||
$pluginVersion = $manifestData['information']['version'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
<p style="margin: 0; font-size: 0.9rem; color: var(--text-main);">
|
||||
Plugin Desarrollado por <strong>SIIP INTERNET</strong> - Todos los derechos reservados.
|
||||
</p>
|
||||
<p style="margin: 5px 0 0 0; font-size: 0.85rem; color: var(--text-muted);">
|
||||
© <?php echo date('Y'); ?> SIIP Internet. Versión 4.6.0
|
||||
© <?php echo date('Y'); ?> SIIP Internet. Versión <?php echo htmlspecialchars($pluginVersion); ?>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@ -2045,9 +2052,8 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
let SYSTEM_USER_ID = <?php echo $currentUser ? $currentUser->userId : 'null'; ?>;
|
||||
|
||||
const store = {
|
||||
installers: <?php echo json_encode($installersData['instaladores']); ?>,
|
||||
theme: localStorage.getItem('theme') || 'light',
|
||||
installers: <?php echo json_encode($installersData['instaladores']); ?>,
|
||||
installers: <?php echo json_encode($installersData['instaladores'] ?? []); ?>,
|
||||
admins: <?php echo json_encode($admins); ?>,
|
||||
theme: localStorage.getItem('theme') || 'light',
|
||||
crmUrl: '<?php echo $ucrmApiUrl; ?>',
|
||||
publicUrl: '<?php echo $ucrmPublicUrl; ?>',
|
||||
@ -2114,6 +2120,8 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
const moduleMap = {
|
||||
// Spanish IDs
|
||||
'instaladores': 'instaladores',
|
||||
'installer-jobs': 'installer-jobs',
|
||||
'tareas-instalador': 'installer-jobs',
|
||||
'notificaciones': 'notificaciones',
|
||||
'stripe': 'pagos-spei',
|
||||
'oxxo': 'pagos-oxxo',
|
||||
@ -2131,9 +2139,7 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
|
||||
|
||||
// Find and activate the clicked tab
|
||||
const targetTab = Array.from(document.querySelectorAll('.tab')).find(tab => {
|
||||
return tab.getAttribute('onclick').includes(`'${tabName}'`);
|
||||
});
|
||||
const targetTab = document.querySelector(`.tab[data-tab="${tabName}"]`);
|
||||
if (targetTab) {
|
||||
targetTab.classList.add('active');
|
||||
}
|
||||
@ -2145,6 +2151,10 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
targetSection.classList.add('active');
|
||||
}
|
||||
|
||||
if (tabName === 'installer-jobs') {
|
||||
populateInstallerJobsSelect();
|
||||
}
|
||||
|
||||
// Initialize module if needed
|
||||
if (tabName === 'notificaciones' && !window.notificacionesInitialized) {
|
||||
// Load clients for notifications module
|
||||
@ -2465,18 +2475,82 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
function renderTable() {
|
||||
const tbody = document.querySelector('#installersTable tbody');
|
||||
tbody.innerHTML = store.installers.map((inst, i) => `<tr>
|
||||
<td>#${inst.id}</td>
|
||||
<td>${inst.nombre}</td>
|
||||
<td>${inst.id}</td>
|
||||
<td><strong>${inst.nombre}</strong></td>
|
||||
<td>${inst.whatsapp}</td>
|
||||
<td>
|
||||
<span style="cursor:pointer; font-size:1.3em; margin-right:8px;" onclick="loadInstallerJobs('${inst.id}', '${inst.nombre}')" title="Ver tareas activas">📋</span>
|
||||
<span style="cursor:pointer; font-size:1.3em; margin-right:8px;" onclick="openInstallerJobsFromCrud('${inst.id}', '${inst.nombre}')" title="Ver tareas activas">📋</span>
|
||||
<img src="?action=image&file=edit.webp" class="icon-action" onclick="editInstaller(${i})" title="Editar">
|
||||
<img src="?action=image&file=delete.webp" class="icon-action" style="margin-left:10px" onclick="deleteInstaller(${i})" title="Borrar">
|
||||
</td>
|
||||
</tr>`).join('');
|
||||
populateInstallerJobsSelect();
|
||||
}
|
||||
renderTable();
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function populateInstallerJobsSelect() {
|
||||
const select = document.getElementById('installerJobsSelect');
|
||||
if (!select) return;
|
||||
|
||||
let installerList = Array.isArray(store.installers) ? [...store.installers] : [];
|
||||
const installerIds = installerList.map(i => String(i.id));
|
||||
|
||||
if (Array.isArray(store.admins)) {
|
||||
store.admins.forEach(admin => {
|
||||
if (!installerIds.includes(String(admin.id))) {
|
||||
installerList.push({
|
||||
id: admin.id,
|
||||
nombre: admin.nombre,
|
||||
whatsapp: ''
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const currentVal = select.value;
|
||||
select.innerHTML = '<option value="">-- Selecciona un instalador --</option>' +
|
||||
installerList.map(inst => {
|
||||
const phoneInfo = inst.whatsapp ? ` (${inst.whatsapp})` : '';
|
||||
return `<option value="${inst.id}">${escapeHtml(inst.nombre)}${phoneInfo}</option>`;
|
||||
}).join('');
|
||||
|
||||
if (currentVal) select.value = currentVal;
|
||||
}
|
||||
|
||||
window.onInstallerSelectChange = function(installerId) {
|
||||
if (!installerId) {
|
||||
document.getElementById('jobsEmptyState').style.display = 'block';
|
||||
document.getElementById('jobsLoading').style.display = 'none';
|
||||
document.getElementById('jobsTableWrapper').style.display = 'none';
|
||||
document.getElementById('jobsNoResults').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
let installer = (store.installers || []).find(i => String(i.id) === String(installerId));
|
||||
if (!installer && Array.isArray(store.admins)) {
|
||||
const adminObj = store.admins.find(a => String(a.id) === String(installerId));
|
||||
if (adminObj) installer = { id: adminObj.id, nombre: adminObj.nombre };
|
||||
}
|
||||
const installerName = installer ? installer.nombre : `ID ${installerId}`;
|
||||
loadInstallerJobs(installerId, installerName);
|
||||
};
|
||||
|
||||
window.openInstallerJobsFromCrud = function(installerId, installerName) {
|
||||
switchTab('installer-jobs');
|
||||
const select = document.getElementById('installerJobsSelect');
|
||||
if (select) select.value = installerId;
|
||||
loadInstallerJobs(installerId, installerName);
|
||||
};
|
||||
|
||||
|
||||
window.editInstaller = function(index) {
|
||||
const installer = store.installers[index];
|
||||
@ -2522,52 +2596,48 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
const tableWrapper = document.getElementById('jobsTableWrapper');
|
||||
const noResults = document.getElementById('jobsNoResults');
|
||||
|
||||
title.innerHTML = `📋 Tareas Activas: <strong>${installerName}</strong>`;
|
||||
emptyState.style.display = 'none';
|
||||
loading.style.display = 'block';
|
||||
tableWrapper.style.display = 'none';
|
||||
noResults.style.display = 'none';
|
||||
|
||||
// Scroll suave a la sección de jobs
|
||||
title.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
if (title) title.innerHTML = `📋 Tareas Activas: <strong>${escapeHtml(installerName)}</strong>`;
|
||||
if (emptyState) emptyState.style.display = 'none';
|
||||
if (loading) loading.style.display = 'block';
|
||||
if (tableWrapper) tableWrapper.style.display = 'none';
|
||||
if (noResults) noResults.style.display = 'none';
|
||||
|
||||
try {
|
||||
const resp = await fetch(`?action=get_installer_jobs&installerId=${installerId}`);
|
||||
const jobs = await resp.json();
|
||||
loading.style.display = 'none';
|
||||
if (loading) loading.style.display = 'none';
|
||||
|
||||
if (!jobs.length) {
|
||||
noResults.style.display = 'block';
|
||||
if (!Array.isArray(jobs) || !jobs.length) {
|
||||
if (noResults) noResults.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
const tbody = document.querySelector('#installerJobsTable tbody');
|
||||
tbody.innerHTML = jobs.map(job => {
|
||||
const dateFormatted = job.date ? new Date(job.date).toLocaleDateString('es-MX', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
}) : 'S/F';
|
||||
const titleClean = job.title.replace('[NOTIFICACION-PENDIENTE]', '').replace('[CLIENTE-SIN-WHATSAPP]', '').trim();
|
||||
return `<tr>
|
||||
<td><strong>#${job.id}</strong></td>
|
||||
<td>[${job.clientId}] ${job.clientName}</td>
|
||||
<td>${dateFormatted}</td>
|
||||
<td style="max-width:200px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${job.description}">${titleClean || job.description || 'Sin descripción'}</td>
|
||||
<td>
|
||||
<button class="btn btn-primary" style="padding:6px 14px; font-size:0.85em;" onclick="resendJobNotification(${job.id}, this)">
|
||||
📨 Reenviar
|
||||
</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
tableWrapper.style.display = 'block';
|
||||
if (tbody) {
|
||||
tbody.innerHTML = jobs.map(job => {
|
||||
const dateFormatted = job.date ? new Date(job.date).toLocaleDateString('es-MX', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
}) : 'S/F';
|
||||
const titleClean = String(job.title || '').replace('[NOTIFICACION-PENDIENTE]', '').replace('[CLIENTE-SIN-WHATSAPP]', '').trim();
|
||||
return `<tr>
|
||||
<td><strong><a href="${store.publicUrl}/scheduling/job/${job.id}" target="_blank" style="color: var(--primary); text-decoration: underline;">#${job.id}</a></strong></td>
|
||||
<td>[${job.clientId || '-'}] ${escapeHtml(job.clientName)}</td>
|
||||
<td>${dateFormatted}</td>
|
||||
<td style="max-width:200px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${escapeHtml(job.description)}">${escapeHtml(titleClean || job.description || 'Sin descripción')}</td>
|
||||
<td>
|
||||
<button class="btn btn-whatsapp" onclick="resendJobNotification(${job.id}, this)">
|
||||
<img src="?action=image&file=whatsapp-logo-button.png" class="icon-btn" style="margin-right:5px;filter: brightness(0) invert(1);"> Re-enviar Notificación
|
||||
</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
if (tableWrapper) tableWrapper.style.display = 'block';
|
||||
} catch (e) {
|
||||
loading.style.display = 'none';
|
||||
noResults.style.display = 'block';
|
||||
if (loading) loading.style.display = 'none';
|
||||
if (noResults) noResults.style.display = 'block';
|
||||
console.error('Error loading installer jobs:', e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ function generateStrongPassword($length = 16)
|
||||
* - Service status Ended(2) or Obsolete(5) → password = 'Servicio Finalizado'
|
||||
* - Device disconnected → password = 'Antena desconectada al momento de obtener la contraseña'
|
||||
*/
|
||||
function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config, $currentPassword)
|
||||
function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config, $currentPassword, $logger = null)
|
||||
{
|
||||
$passwordAttributeId = $attributeIds['password'];
|
||||
$siteAttributeId = $attributeIds['site'] ?? null;
|
||||
@ -153,34 +153,61 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
'deviceInfo' => null,
|
||||
];
|
||||
|
||||
// Local helper for unified logging
|
||||
$logMsg = function ($msg, $level = 'info') use ($logger, $clientId) {
|
||||
$fullMsg = "[Client $clientId] " . $msg;
|
||||
logMessage($fullMsg);
|
||||
if ($logger) {
|
||||
$logger->log($level, $fullMsg);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
$isTest = isTestEnvironment($config);
|
||||
|
||||
// ── Edge Case 1: Check client tags for "NS REPETIDOR" ──
|
||||
// Fetch client attributes from CRM to check current site & antenna details
|
||||
$currentSite = null;
|
||||
$currentAntenna = null;
|
||||
try {
|
||||
$clientData = $ucrmApi->get("clients/$clientId");
|
||||
$clientTags = $clientData['tags'] ?? [];
|
||||
|
||||
foreach ($clientData['attributes'] ?? [] as $attr) {
|
||||
if ($attr['key'] === 'site' || $attr['customAttributeId'] == $siteAttributeId) {
|
||||
$currentSite = trim((string)$attr['value']);
|
||||
}
|
||||
if ($attr['key'] === 'antenaSectorial' || $attr['customAttributeId'] == $antenaSectorialAttributeId) {
|
||||
$currentAntenna = trim((string)$attr['value']);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Edge Case 1: Check client tags for "NS REPETIDOR" ──
|
||||
foreach ($clientTags as $tag) {
|
||||
if (stripos($tag['name'] ?? '', 'NS REPETIDOR') !== false) {
|
||||
$msg = 'Este cliente funciona como Repetidor';
|
||||
$result['password'] = $msg;
|
||||
$result['deviceInfo'] = 'REPETIDOR';
|
||||
|
||||
$attrUpdates = [];
|
||||
if ($currentPassword !== $msg) {
|
||||
$attrUpdates = [$passwordAttributeId => $msg];
|
||||
if ($antenaSectorialAttributeId) {
|
||||
$attrUpdates[$antenaSectorialAttributeId] = 'REPETIDOR';
|
||||
}
|
||||
$attrUpdates[$passwordAttributeId] = $msg;
|
||||
}
|
||||
if ($antenaSectorialAttributeId && $currentAntenna !== 'REPETIDOR') {
|
||||
$attrUpdates[$antenaSectorialAttributeId] = 'REPETIDOR';
|
||||
}
|
||||
|
||||
if (!empty($attrUpdates)) {
|
||||
patchClientAttributes($ucrmApi, $clientId, $attrUpdates);
|
||||
$result['changed'] = true;
|
||||
} else {
|
||||
$result['changed'] = false;
|
||||
}
|
||||
logMessage("Client $clientId: Tag NS REPETIDOR detected → skipping");
|
||||
$logMsg("Tag NS REPETIDOR detected → skipping");
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
logMessage("Warning: Could not fetch client tags for $clientId: " . $e->getMessage());
|
||||
$logMsg("Warning: Could not fetch client details/tags: " . $e->getMessage(), 'warning');
|
||||
}
|
||||
|
||||
// ── Get Services ──
|
||||
@ -197,6 +224,7 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
} else {
|
||||
$result['changed'] = false;
|
||||
}
|
||||
$logMsg($msg, 'warning');
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -214,13 +242,14 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
// ── Edge Case 2: Service Ended (2) or Obsolete (5) ──
|
||||
if ($serviceStatus === 2 || $serviceStatus === 5) {
|
||||
$passwordValue = 'Servicio Finalizado';
|
||||
logMessage("Client $clientId: Service " . ($index + 1) . " has status $serviceStatus (Ended/Obsolete)");
|
||||
$logMsg("Service " . ($index + 1) . " has status $serviceStatus (Ended/Obsolete)");
|
||||
$allServicePasswords[] = trim("$label $passwordValue");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$siteId) {
|
||||
$passwordValue = "⚠️ Sin sitio";
|
||||
$logMsg("Service " . ($index + 1) . " has no unmsClientSiteId", 'warning');
|
||||
} else {
|
||||
if ($isTest) {
|
||||
// Test Env Logic: Preserve existing if valid
|
||||
@ -244,22 +273,11 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
// Static Test Data
|
||||
$siteName = 'VENUS';
|
||||
$deviceInfo = 'Sectorial de pruebas 172.16.5.134';
|
||||
logMessage("Test Env: Generated new password for Client $clientId (Service $index) and set static network data.");
|
||||
$logMsg("Test Env: Generated new password (Service $index) and set static network data.");
|
||||
} else {
|
||||
// Even if password exists, we might want to ensure static data is set if missing?
|
||||
// User request implies "when generating that password... it should also fill".
|
||||
// Let's safe-set it if we are touching the client.
|
||||
// Actually, if we found it in CRM, we preserve existing password.
|
||||
// But maybe we should update the network data anyway?
|
||||
// The user said "when generating". So only on new generation seems safer/stricter to request,
|
||||
// but usually test env data should be consistent.
|
||||
// Let's set it always in Test Env if we are in this block?
|
||||
// No, let's stick to "when generating" or if meaningful to update.
|
||||
// If we are strictly "Testing", we might want to overwrite "Real" data with "Test" data to avoid confusion?
|
||||
// But let's stick to the generation block for now as requested.
|
||||
$siteName = 'VENUS';
|
||||
$deviceInfo = 'Sectorial de pruebas 172.16.5.134';
|
||||
logMessage("Test Env: Preserved existing password for Client $clientId but ensured static network data.");
|
||||
$logMsg("Test Env: Preserved existing password but ensured static network data.");
|
||||
}
|
||||
} else {
|
||||
// Production Logic
|
||||
@ -269,66 +287,97 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
|
||||
if (empty($devs)) {
|
||||
$passwordValue = "⚠️ Sin antena";
|
||||
$logMsg("No devices found for siteId: $siteId", 'warning');
|
||||
} else {
|
||||
$passVault = null;
|
||||
$passVault = null;
|
||||
|
||||
// Find first client-like device or fallback to first device
|
||||
$firstDev = null;
|
||||
foreach ($devs as $d) {
|
||||
$role = $d['identification']['role'] ?? '';
|
||||
if ($role === 'station' || $role === 'cpe' || $role === 'client') {
|
||||
$firstDev = $d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($firstDev === null) {
|
||||
$firstDev = $devs[0];
|
||||
}
|
||||
|
||||
// Extract network data (always execute for first device if index is 0)
|
||||
if ($firstDev && $index === 0) {
|
||||
$siteName = $firstDev['identification']['site']['parent']['name'] ?? null;
|
||||
$apDeviceName = $firstDev['attributes']['apDevice']['name'] ?? null;
|
||||
$apDeviceId = $firstDev['attributes']['apDevice']['id'] ?? null;
|
||||
|
||||
if ($apDeviceName && $apDeviceId) {
|
||||
try {
|
||||
$respApDev = $unmsClient->get("devices/$apDeviceId");
|
||||
$apDevData = json_decode($respApDev->getBody()->getContents(), true);
|
||||
$apDeviceIP = $apDevData['ipAddress'] ?? '';
|
||||
$deviceInfo = trim("$apDeviceName $apDeviceIP");
|
||||
|
||||
// AP Device Site Name Fallback:
|
||||
if (empty($siteName)) {
|
||||
$siteName = $apDevData['identification']['site']['name'] ?? null;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$deviceInfo = $apDeviceName;
|
||||
$logMsg("Warning: Could not fetch AP device IP for $apDeviceId: " . $e->getMessage(), 'warning');
|
||||
}
|
||||
} elseif (!$apDeviceName) {
|
||||
$deviceInfo = 'REPETIDOR';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Extraer contraseña previa de este servicio en el CRM si existe ──
|
||||
$prevServicePassword = null;
|
||||
$hasValidPrevPassword = false;
|
||||
if (!empty($currentPassword)) {
|
||||
if ($numServices > 1) {
|
||||
if (preg_match('/Servicio ' . ($index + 1) . ':\s*([^⚠️\s]+)/', $currentPassword, $matches)) {
|
||||
$tempPass = trim($matches[1]);
|
||||
// Validar que no sea una leyenda de error conocida o un emoji
|
||||
if (stripos($tempPass, 'desconectada') === false &&
|
||||
stripos($tempPass, 'Finalizado') === false &&
|
||||
stripos($tempPass, 'Error') === false &&
|
||||
stripos($tempPass, 'Repetidor') === false &&
|
||||
stripos($tempPass, 'sin') === false) {
|
||||
$prevServicePassword = $tempPass;
|
||||
$hasValidPrevPassword = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$tempPass = trim($currentPassword);
|
||||
if (strpos($tempPass, '⚠️') === false &&
|
||||
strpos($tempPass, 'Servicio') === false &&
|
||||
stripos($tempPass, 'desconectada') === false &&
|
||||
stripos($tempPass, 'Finalizado') === false &&
|
||||
stripos($tempPass, 'Error') === false &&
|
||||
stripos($tempPass, 'Repetidor') === false &&
|
||||
stripos($tempPass, 'sin') === false) {
|
||||
$prevServicePassword = $tempPass;
|
||||
$hasValidPrevPassword = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Edge Case 3: Check if device is disconnected ──
|
||||
$deviceStatus = $firstDev['overview']['status'] ?? 'unknown';
|
||||
if ($deviceStatus === 'disconnected') {
|
||||
if ($hasValidPrevPassword) {
|
||||
// Preservamos la contraseña real para que el instalador pueda usarla
|
||||
$passwordValue = $prevServicePassword;
|
||||
$logMsg("Device is disconnected (siteId=$siteId), but preserved existing valid password from CRM: '$passwordValue'.");
|
||||
} else {
|
||||
$passwordValue = 'Antena desconectada al momento de obtener la contraseña';
|
||||
$logMsg("Device is disconnected (siteId=$siteId) and no valid password was found in CRM.", 'warning');
|
||||
}
|
||||
$allServicePasswords[] = trim("$label $passwordValue");
|
||||
continue; // Skip vault check for this service
|
||||
}
|
||||
|
||||
$firstDeviceId = null;
|
||||
|
||||
// ── Edge Case 3: Check if device is disconnected ──
|
||||
$firstDev = $devs[0] ?? null;
|
||||
$deviceStatus = $firstDev['overview']['status'] ?? 'unknown';
|
||||
|
||||
if ($deviceStatus === 'disconnected') {
|
||||
$passwordValue = 'Antena desconectada al momento de obtener la contraseña';
|
||||
logMessage("Client $clientId: Device is disconnected (siteId=$siteId)");
|
||||
|
||||
// Still extract network data even if disconnected
|
||||
if ($firstDev && $index === 0) {
|
||||
$siteName = $firstDev['identification']['site']['parent']['name'] ?? null;
|
||||
$apDeviceName = $firstDev['attributes']['apDevice']['name'] ?? null;
|
||||
$apDeviceId = $firstDev['attributes']['apDevice']['id'] ?? null;
|
||||
if ($apDeviceName && $apDeviceId) {
|
||||
try {
|
||||
$respApDev = $unmsClient->get("devices/$apDeviceId");
|
||||
$apDevData = json_decode($respApDev->getBody()->getContents(), true);
|
||||
$apDeviceIP = $apDevData['ipAddress'] ?? '';
|
||||
$deviceInfo = trim("$apDeviceName $apDeviceIP");
|
||||
} catch (\Exception $e) {
|
||||
$deviceInfo = $apDeviceName;
|
||||
}
|
||||
} elseif (!$apDeviceName) {
|
||||
$deviceInfo = 'REPETIDOR';
|
||||
}
|
||||
}
|
||||
$allServicePasswords[] = trim("$label $passwordValue");
|
||||
continue; // Skip vault/regenerate for this service
|
||||
}
|
||||
// $firstDev already set above (line 258)
|
||||
if ($firstDev && $index === 0) {
|
||||
// Site name from parent site
|
||||
$siteName = $firstDev['identification']['site']['parent']['name'] ?? null;
|
||||
|
||||
// AP Device name and IP
|
||||
$apDeviceName = $firstDev['attributes']['apDevice']['name'] ?? null;
|
||||
$apDeviceId = $firstDev['attributes']['apDevice']['id'] ?? null;
|
||||
|
||||
if ($apDeviceName && $apDeviceId) {
|
||||
// Fetch AP device IP
|
||||
try {
|
||||
$respApDev = $unmsClient->get("devices/$apDeviceId");
|
||||
$apDevData = json_decode($respApDev->getBody()->getContents(), true);
|
||||
$apDeviceIP = $apDevData['ipAddress'] ?? '';
|
||||
$deviceInfo = trim("$apDeviceName $apDeviceIP");
|
||||
} catch (\Exception $e) {
|
||||
$deviceInfo = $apDeviceName;
|
||||
logMessage("Warning: Could not fetch AP device IP for $apDeviceId: " . $e->getMessage());
|
||||
}
|
||||
} elseif (!$apDeviceName) {
|
||||
// No apDevice = possibly a repeater
|
||||
$deviceInfo = 'REPETIDOR';
|
||||
logMessage("Client $clientId: Device is a repeater (no apDevice)");
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($devs as $dev) {
|
||||
$deviceId = $dev['identification']['id'] ?? null;
|
||||
if (!$deviceId) continue;
|
||||
@ -342,6 +391,7 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
break;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$logMsg("Could not fetch credentials from device $deviceId: " . $e->getMessage(), 'debug');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -356,16 +406,19 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
'json' => [['username' => 'ubnt', 'password' => $newPass, 'readOnly' => true]]
|
||||
]);
|
||||
$passwordValue = $newPass;
|
||||
logMessage("Prod Env: Regenerated password on UNMS Device $firstDeviceId");
|
||||
$logMsg("Regenerated password on UNMS Device $firstDeviceId");
|
||||
} catch (\Exception $e) {
|
||||
$passwordValue = "⚠️ Error Regenerating: " . $e->getMessage();
|
||||
$logMsg("Error regenerating password on device $firstDeviceId: " . $e->getMessage(), 'error');
|
||||
}
|
||||
} else {
|
||||
$passwordValue = "⚠️ Sin dispositivo válido";
|
||||
$logMsg("No valid device ID for credentials", 'warning');
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$passwordValue = "⚠️ Error API UNMS: " . $e->getMessage();
|
||||
$logMsg("Exception during UNMS API query: " . $e->getMessage(), 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -385,25 +438,60 @@ function fixClientData($clientId, $ucrmApi, $unmsClient, $attributeIds, $config,
|
||||
$attrUpdates[$passwordAttributeId] = $finalValue;
|
||||
}
|
||||
|
||||
// Site: patch if we got data and attribute ID exists
|
||||
if ($siteName !== null && $siteAttributeId) {
|
||||
$attrUpdates[$siteAttributeId] = $siteName;
|
||||
// Site: patch if we got data and it is different
|
||||
if ($siteAttributeId) {
|
||||
if ($siteName !== null) {
|
||||
if (!empty($siteName) || empty($currentSite)) {
|
||||
if ($siteName !== $currentSite) {
|
||||
$attrUpdates[$siteAttributeId] = $siteName;
|
||||
$logMsg("Site to update resolved: '$siteName' (previous was: '" . ($currentSite ?: 'empty') . "')");
|
||||
} else {
|
||||
$logMsg("Site name matches current CRM value '$currentSite'. Patch skipped.");
|
||||
}
|
||||
} else {
|
||||
$logMsg("Site name resolved from UISP is empty and CRM already has a value '$currentSite'. Prevented overwriting with empty value.");
|
||||
}
|
||||
} else {
|
||||
$logMsg("UISP did not return a parent site name (siteName is null).");
|
||||
}
|
||||
} else {
|
||||
$logMsg("Site attribute ID ('site') was not resolved.");
|
||||
}
|
||||
|
||||
// Antena/Sectorial: patch if we got data and attribute ID exists
|
||||
if ($deviceInfo !== null && $antenaSectorialAttributeId) {
|
||||
$attrUpdates[$antenaSectorialAttributeId] = $deviceInfo;
|
||||
// Antena/Sectorial: patch if we got data and it is different
|
||||
if ($antenaSectorialAttributeId) {
|
||||
if ($deviceInfo !== null) {
|
||||
$isNewValueRepeater = (strtoupper($deviceInfo) === 'REPETIDOR');
|
||||
$isCurrentValueValuable = (!empty($currentAntenna) && strtoupper($currentAntenna) !== 'REPETIDOR' && stripos($currentAntenna, '⚠️') === false);
|
||||
|
||||
// Only update if we're not overwriting a valid sector name with a generic 'REPETIDOR'
|
||||
if (!($isNewValueRepeater && $isCurrentValueValuable)) {
|
||||
if ($deviceInfo !== $currentAntenna) {
|
||||
$attrUpdates[$antenaSectorialAttributeId] = $deviceInfo;
|
||||
$logMsg("Antenna/Sectorial to update resolved: '$deviceInfo' (previous was: '" . ($currentAntenna ?: 'empty') . "')");
|
||||
} else {
|
||||
$logMsg("Antenna info matches current CRM value '$currentAntenna'. Patch skipped.");
|
||||
}
|
||||
} else {
|
||||
$logMsg("Prevented overwriting valuable CRM antenna sector '$currentAntenna' with generic 'REPETIDOR'.");
|
||||
}
|
||||
} else {
|
||||
$logMsg("UISP did not return antenna info (deviceInfo is null).");
|
||||
}
|
||||
} else {
|
||||
$logMsg("Antenna attribute ID ('antenaSectorial') was not resolved.");
|
||||
}
|
||||
|
||||
if (!empty($attrUpdates)) {
|
||||
patchClientAttributes($ucrmApi, $clientId, $attrUpdates);
|
||||
logMessage("Client $clientId: Patched " . count($attrUpdates) . " attribute(s)");
|
||||
$logMsg("Patched " . count($attrUpdates) . " attribute(s): " . json_encode($attrUpdates));
|
||||
}
|
||||
|
||||
$result['changed'] = ($finalValue !== $currentPassword) || !empty($attrUpdates);
|
||||
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
$logMsg("Fatal error in fixClientData: " . $e->getMessage(), 'error');
|
||||
$result['password'] = "Error fixing: " . $e->getMessage();
|
||||
return $result;
|
||||
}
|
||||
|
||||
0
scripts-uisp/audit_incomplete_pi.php
Normal file → Executable file
0
scripts-uisp/audit_incomplete_pi.php
Normal file → Executable file
0
scripts-uisp/clean_incomplete_pi.php
Normal file → Executable file
0
scripts-uisp/clean_incomplete_pi.php
Normal file → Executable file
@ -4,60 +4,60 @@ chdir(__DIR__ . '/../');
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
||||
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
$config = PluginConfigManager::create()->loadConfig();
|
||||
$ipServer = $config['ipserver'] ?? 'localhost';
|
||||
$apiUrl = "https://$ipServer/crm/api/v1.0/";
|
||||
$token = $config['apitoken'] ?? '';
|
||||
$unmsToken = $config['unmsApiToken'] ?? '';
|
||||
|
||||
$client = new Client([
|
||||
'base_uri' => $apiUrl,
|
||||
if (empty($unmsToken)) {
|
||||
echo "Error: UNMS API Token is missing.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$unmsClient = new Client([
|
||||
'base_uri' => "https://{$ipServer}/nms/api/v2.1/",
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'X-Auth-App-Key' => $token,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'X-Auth-Token' => $unmsToken
|
||||
]
|
||||
]);
|
||||
$ucrmApi = new UcrmApi($client, $token);
|
||||
|
||||
$ids = [18, 20];
|
||||
$customAttributeKey = 'passwordAntenaCliente';
|
||||
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$data = $ucrmApi->get("clients/$id");
|
||||
echo "Client ID: $id\n";
|
||||
echo "IsArchived: " . ($data['isArchived'] ? 'Yes' : 'No') . "\n";
|
||||
|
||||
echo "All Attributes:\n";
|
||||
foreach ($data['attributes'] as $attr) {
|
||||
echo " - {$attr['key']} (ID: {$attr['customAttributeId']}): [{$attr['value']}]\n";
|
||||
}
|
||||
flush();
|
||||
|
||||
$passVal = null;
|
||||
$foundKey = false;
|
||||
foreach ($data['attributes'] as $attr) {
|
||||
if ($attr['key'] === $customAttributeKey) {
|
||||
$passVal = $attr['value'];
|
||||
$foundKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($foundKey) {
|
||||
echo "Attribute '$customAttributeKey' FOUND.\n";
|
||||
echo "Value: [" . $passVal . "]\n";
|
||||
echo "Type: " . gettype($passVal) . "\n";
|
||||
echo "Empty? " . (empty($passVal) ? 'Yes' : 'No') . "\n";
|
||||
try {
|
||||
echo "=== FETCHING DEVICES FROM UISP ===\n";
|
||||
$response = $unmsClient->get("devices");
|
||||
$devices = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
echo "Total devices retrieved: " . count($devices) . "\n\n";
|
||||
|
||||
$count = 0;
|
||||
foreach ($devices as $idx => $dev) {
|
||||
if ($count >= 10) break;
|
||||
$count++;
|
||||
|
||||
echo "Device #$idx:\n";
|
||||
echo " - Name: " . ($dev['identification']['name'] ?? 'N/A') . "\n";
|
||||
echo " - Role: " . ($dev['identification']['role'] ?? 'N/A') . "\n";
|
||||
echo " - Site ID: " . ($dev['identification']['site']['id'] ?? 'N/A') . "\n";
|
||||
echo " - Site Name: " . ($dev['identification']['site']['name'] ?? 'N/A') . "\n";
|
||||
|
||||
// Parent Site structure
|
||||
if (isset($dev['identification']['site']['parent'])) {
|
||||
echo " - Parent Site Name: " . ($dev['identification']['site']['parent']['name'] ?? 'N/A') . "\n";
|
||||
} else {
|
||||
echo "Attribute '$customAttributeKey' NOT FOUND in attributes list.\n";
|
||||
echo " - Parent Site structure: NOT PRESENT\n";
|
||||
}
|
||||
|
||||
// apDevice structure
|
||||
if (isset($dev['attributes']['apDevice'])) {
|
||||
echo " - apDevice Name: " . ($dev['attributes']['apDevice']['name'] ?? 'N/A') . "\n";
|
||||
echo " - apDevice ID: " . ($dev['attributes']['apDevice']['id'] ?? 'N/A') . "\n";
|
||||
} else {
|
||||
echo " - apDevice structure: NOT PRESENT\n";
|
||||
}
|
||||
echo "--------------------------------------\n";
|
||||
} catch (\Exception $e) {
|
||||
echo "Error fetching $id: " . $e->getMessage() . "\n";
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
echo "Error calling UISP API: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
@ -961,6 +961,34 @@ function getClientDataNetwork($clientId, $authToken): array
|
||||
]);
|
||||
|
||||
|
||||
$currentSite = 'Sin SITE';
|
||||
$currentDevice = 'REPETIDOR';
|
||||
|
||||
try {
|
||||
// Fetch current attributes from CRM to avoid destructive overwrites
|
||||
$responseClient = $clientUcrm->get('clients/' . $clientId, [
|
||||
'headers' => [
|
||||
'X-Auth-Token' => $authToken,
|
||||
'Content-Type: application/json'
|
||||
]
|
||||
]);
|
||||
if ($responseClient->getStatusCode() === 200) {
|
||||
$clientInfo = json_decode($responseClient->getBody()->getContents(), true);
|
||||
foreach ($clientInfo['attributes'] ?? [] as $attr) {
|
||||
if ($attr['key'] === 'site') {
|
||||
$val = trim((string)$attr['value']);
|
||||
if (!empty($val)) $currentSite = $val;
|
||||
}
|
||||
if ($attr['key'] === 'antenaSectorial') {
|
||||
$val = trim((string)$attr['value']);
|
||||
if (!empty($val)) $currentDevice = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Ignore fallback fetch error
|
||||
}
|
||||
|
||||
try {
|
||||
//Obtener id del sitio por medio del servicio
|
||||
$responseServices = $clientUcrm->get('clients/services?clientId=' . $clientId, [
|
||||
@ -980,7 +1008,11 @@ function getClientDataNetwork($clientId, $authToken): array
|
||||
echo "No se encontró la clave 'unmsClientSiteId' en la respuesta." . PHP_EOL;
|
||||
$logMessage = "No se encontró la clave 'unmsClientSiteId' en la respuesta." . PHP_EOL;
|
||||
log_message($logMessage);
|
||||
return $networkData;
|
||||
return array(
|
||||
"siteName" => $currentSite,
|
||||
"device" => $currentDevice,
|
||||
"ipDevice" => 'Sin IP de cliente'
|
||||
);
|
||||
}
|
||||
//print_r('responseServices: ' . json_encode($dataServices) . PHP_EOL);
|
||||
|
||||
@ -988,7 +1020,11 @@ function getClientDataNetwork($clientId, $authToken): array
|
||||
echo "Error en la solicitud. Código de estado HTTP: " . $responseServices->getStatusCode() . PHP_EOL;
|
||||
$logMessage = "Error en la solicitud. Código de estado HTTP: " . $responseServices->getStatusCode() . PHP_EOL;
|
||||
log_message($logMessage);
|
||||
return $networkData;
|
||||
return array(
|
||||
"siteName" => $currentSite,
|
||||
"device" => $currentDevice,
|
||||
"ipDevice" => 'Sin IP de cliente'
|
||||
);
|
||||
}
|
||||
|
||||
$responseDevicesBySite = $clientUnms->request('GET', 'devices?siteId=' . $unmsSiteID, [
|
||||
@ -1013,8 +1049,8 @@ function getClientDataNetwork($clientId, $authToken): array
|
||||
log_message($logMessage);
|
||||
|
||||
$networkData = array(
|
||||
"siteName" => 'Sin SITE',
|
||||
"device" => 'REPETIDOR',
|
||||
"siteName" => $currentSite,
|
||||
"device" => $currentDevice,
|
||||
"ipDevice" => 'Sin IP de cliente'
|
||||
);
|
||||
|
||||
|
||||
@ -247,12 +247,12 @@ abstract class AbstractMessageNotifierFacade
|
||||
if ($config['notificationTypeText'] ?? false) {
|
||||
if ($api->sendTextPaymentNotificationWhatsApp($phone, $notificationData)) {
|
||||
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
||||
if ($contact) $api->patchWhatsapp($contact, $notificationData);
|
||||
if (isset($contact['contact']['uuid'])) $api->patchWhatsapp($contact, $notificationData);
|
||||
}
|
||||
} else {
|
||||
if ($api->sendPaymentNotificationWhatsApp($phone, $notificationData)) {
|
||||
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
||||
if ($contact) $api->patchWhatsapp($contact, $notificationData);
|
||||
if (isset($contact['contact']['uuid'])) $api->patchWhatsapp($contact, $notificationData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -274,7 +274,7 @@ abstract class AbstractMessageNotifierFacade
|
||||
$this->logger->debug("onlyUpdate: Teléfono validado: $phone");
|
||||
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
||||
$this->logger->debug("onlyUpdate: Contacto obtenido de CallBell: " . json_encode($contact));
|
||||
if ($contact) {
|
||||
if (isset($contact['contact']['uuid'])) {
|
||||
$this->logger->info("onlyUpdate: Ejecutando patchWhatsapp para teléfono: $phone");
|
||||
$api->patchWhatsapp($contact, $notificationData);
|
||||
} else {
|
||||
@ -288,7 +288,7 @@ abstract class AbstractMessageNotifierFacade
|
||||
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
||||
$phone = $this->validarNumeroTelefono($phoneToUpdate);
|
||||
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
||||
if ($contact) $api->patchServiceStatusWhatsApp($contact, $notificationData);
|
||||
if (isset($contact['contact']['uuid'])) $api->patchServiceStatusWhatsApp($contact, $notificationData);
|
||||
}
|
||||
|
||||
protected function getVaultCredentialsByClientId($clientId): string
|
||||
@ -524,7 +524,13 @@ abstract class AbstractMessageNotifierFacade
|
||||
{
|
||||
if (!$n) return '';
|
||||
$n = preg_replace('/\D/', '', (string)$n);
|
||||
return (strlen($n) === 10) ? '52' . $n : $n;
|
||||
if (strlen($n) === 10) {
|
||||
return '521' . $n;
|
||||
}
|
||||
if (strlen($n) === 12 && strpos($n, '52') === 0) {
|
||||
return '521' . substr($n, 2);
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
abstract protected function sendWhatsApp(NotificationData $notificationData, string $clientSmsNumber): void;
|
||||
|
||||
@ -525,20 +525,25 @@ abstract class AbstractStripeOperationsFacade
|
||||
$clientData = $this->ucrmApi->get("clients/$clientId");
|
||||
|
||||
$passCRM = '';
|
||||
$attributeId = 17; // ID real para 'passwordAntenaCliente'
|
||||
$attributeId = $this->resolveAttributeId('passwordAntenaCliente');
|
||||
|
||||
if (isset($clientData['attributes'])) {
|
||||
foreach ($clientData['attributes'] as $attr) {
|
||||
if ($attr['key'] === 'passwordAntenaCliente') {
|
||||
$passCRM = $attr['value'] ?? '';
|
||||
$attributeId = $attr['customAttributeId'];
|
||||
$attributeId = $attr['customAttributeId'] ?? $attributeId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($attributeId <= 0) {
|
||||
$this->logger->error("No se pudo resolver el ID de atributo para 'passwordAntenaCliente'. Omitiendo sincronización para cliente $clientId.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($passCRM) || $passCRM !== $passVault) {
|
||||
$this->logger->info("Sincronizando contraseña en CRM para cliente $clientId. [" . ($passCRM ?: 'VACIO') . "] -> [$passVault]");
|
||||
$this->logger->info("Sincronizando contraseña en CRM para cliente $clientId. [" . ($passCRM ?: 'VACIO') . "] -> [$passVault] (Atributo ID: $attributeId)");
|
||||
$this->patchClientCustomAttribute($clientId, (int)$attributeId, $passVault);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
@ -621,6 +626,21 @@ abstract class AbstractStripeOperationsFacade
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function resolvePaymentAttributeId(string $key): int
|
||||
{
|
||||
try {
|
||||
$attrs = $this->ucrmApi->get('custom-attributes', ['attributeType' => 'payment']);
|
||||
foreach ($attrs as $attr) {
|
||||
if ($attr['key'] === $key) {
|
||||
return (int)$attr['id'];
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error resolving payment custom attribute '$key': " . $e->getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function comparePasswords(?string $crm, ?string $vault): string
|
||||
{
|
||||
if ($crm && strpos($crm, 'Error') !== 0) return $crm;
|
||||
@ -679,7 +699,13 @@ abstract class AbstractStripeOperationsFacade
|
||||
{
|
||||
if (!$n) return '';
|
||||
$n = preg_replace('/\D/', '', (string)$n);
|
||||
return (strlen($n) === 10) ? '52' . $n : $n;
|
||||
if (strlen($n) === 10) {
|
||||
return '521' . $n;
|
||||
}
|
||||
if (strlen($n) === 12 && strpos($n, '52') === 0) {
|
||||
return '521' . substr($n, 2);
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
public function ensureStripePaymentAttribute($notificationObject): void
|
||||
@ -713,10 +739,87 @@ abstract class AbstractStripeOperationsFacade
|
||||
$metadataTipoPago = $data['metadata']['tipoPago'];
|
||||
$this->logger->info("Microservice found metadata: tipoPago = '$metadataTipoPago'");
|
||||
}
|
||||
// Capture Stripe ID for prefix-based heuristic detection below
|
||||
$metadataStripeId = $data['stripeId'] ?? null;
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->warning("Microservice metadata fetch failed: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// --- LOCAL FALLBACK LOGIC ---
|
||||
if (!$metadataTipoPago && isset($config['tokenstripe'])) {
|
||||
try {
|
||||
$this->logger->info("Attempting direct Stripe API fetch for Payment $paymentId");
|
||||
$paymentInfo = $this->ucrmApi->get('payments/' . $paymentId);
|
||||
$clientId = $paymentInfo['clientId'] ?? null;
|
||||
$amount = isset($paymentInfo['amount']) ? round($paymentInfo['amount'] * 100) : null;
|
||||
|
||||
if ($clientId && $amount) {
|
||||
$clientInfo = $this->ucrmApi->get('clients/' . $clientId);
|
||||
$stripeCustomerId = null;
|
||||
if (isset($clientInfo['attributes'])) {
|
||||
foreach ($clientInfo['attributes'] as $attr) {
|
||||
if (stripos($attr['name'] ?? '', 'Stripe') !== false && stripos($attr['name'] ?? '', 'Customer') !== false) {
|
||||
$stripeCustomerId = $attr['value'];
|
||||
break;
|
||||
}
|
||||
if (($attr['key'] ?? '') === 'stripeCustomerId' || ($attr['key'] ?? '') === 'customerIdStripe') {
|
||||
$stripeCustomerId = $attr['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($stripeCustomerId) {
|
||||
$stripeClient = new Client([
|
||||
'base_uri' => 'https://api.stripe.com/v1/',
|
||||
'auth' => [$config['tokenstripe'], ''],
|
||||
'timeout' => 5
|
||||
]);
|
||||
$res = $stripeClient->get("payment_intents?customer=$stripeCustomerId&limit=5");
|
||||
$intents = json_decode($res->getBody()->getContents(), true);
|
||||
|
||||
if (isset($intents['data']) && is_array($intents['data'])) {
|
||||
foreach ($intents['data'] as $intent) {
|
||||
if (isset($intent['amount']) && $intent['amount'] == $amount) {
|
||||
if (isset($intent['metadata']['tipoPago'])) {
|
||||
$metadataTipoPago = $intent['metadata']['tipoPago'];
|
||||
$this->logger->info("Direct Stripe API found metadata: tipoPago = '$metadataTipoPago'");
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($intent['payment_method_types'])) {
|
||||
if (in_array('oxxo', $intent['payment_method_types'])) {
|
||||
$metadataTipoPago = 'OXXO';
|
||||
$this->logger->info("Direct Stripe API guessed 'OXXO' from payment_method_types");
|
||||
break;
|
||||
} elseif (in_array('customer_balance', $intent['payment_method_types']) || in_array('spei', $intent['payment_method_types'])) {
|
||||
$metadataTipoPago = 'Transferencia Bancaria';
|
||||
$this->logger->info("Direct Stripe API guessed 'Transferencia Bancaria' from payment_method_types");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->warning("Direct Stripe API fallback failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 2.5. Last-resort heuristic: use Stripe ID prefix when metadata and API both fail
|
||||
// In Mexico ISP context: py_ prefix = OXXO (cash) payment, ch_ prefix = card charge
|
||||
if (!$metadataTipoPago && !empty($metadataStripeId)) {
|
||||
if (str_starts_with($metadataStripeId, 'py_')) {
|
||||
$metadataTipoPago = 'OXXO';
|
||||
$this->logger->info("Heuristic detection: stripe_id '$metadataStripeId' starts with 'py_' → assumed OXXO Pay");
|
||||
} elseif (str_starts_with($metadataStripeId, 'ch_')) {
|
||||
$this->logger->debug("Heuristic detection: stripe_id '$metadataStripeId' starts with 'ch_' → confirmed card");
|
||||
// No change: will default to Tarjeta de crédito/débito
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Update User ID if missing (Direct DB Patch via Microservice)
|
||||
// UCRM API doesn't support PATCH userId, so we use microservice
|
||||
if ($stripeUserId) {
|
||||
@ -788,13 +891,17 @@ abstract class AbstractStripeOperationsFacade
|
||||
// Actually, Metadata keys are stronger than manual edits if the flow is automatic.
|
||||
// But let's respect existing valid attributes if metadata is missing.
|
||||
|
||||
$paymentAttributeId = $this->resolvePaymentAttributeId('tipoPagoStripe');
|
||||
$payment = $this->ucrmApi->get('payments/' . $paymentId); // Re-fetch in case changed? Or Use previous result.
|
||||
$currentValue = null;
|
||||
$hasAttribute = false;
|
||||
foreach ($payment['attributes'] as $attr) {
|
||||
if ($attr['key'] === 'tipoPagoStripe' || $attr['customAttributeId'] == 20) {
|
||||
if ($attr['key'] === 'tipoPagoStripe' || ($paymentAttributeId > 0 && $attr['customAttributeId'] == $paymentAttributeId)) {
|
||||
$hasAttribute = true;
|
||||
$currentValue = $attr['value'];
|
||||
if ($paymentAttributeId <= 0) {
|
||||
$paymentAttributeId = $attr['customAttributeId'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -821,33 +928,43 @@ abstract class AbstractStripeOperationsFacade
|
||||
|
||||
if (stripos($methodName, 'OXXO') !== false) {
|
||||
$targetValue = 'OXXO Pay';
|
||||
} elseif (stripos($methodName, 'Transferencia') !== false) {
|
||||
} elseif (
|
||||
stripos($methodName, 'Transferencia') !== false ||
|
||||
stripos($methodName, 'Bank') !== false ||
|
||||
stripos($methodName, 'transfer') !== false ||
|
||||
stripos($methodName, 'ACH') !== false
|
||||
) {
|
||||
$targetValue = 'Transferencia Bancaria';
|
||||
} elseif (stripos($methodName, 'Tarjeta') !== false && stripos($methodName, 'Stripe') !== false) {
|
||||
} elseif (
|
||||
stripos($methodName, 'Tarjeta') !== false ||
|
||||
stripos($methodName, 'card') !== false ||
|
||||
stripos($methodName, 'Crédito') !== false ||
|
||||
stripos($methodName, 'débito') !== false
|
||||
) {
|
||||
$targetValue = 'Tarjeta de Crédito';
|
||||
}
|
||||
$this->logger->debug("Fallback Method Guessing '$methodName' -> '$targetValue'");
|
||||
}
|
||||
|
||||
// 5. Apply Update
|
||||
if ($targetValue) {
|
||||
if ($targetValue && $paymentAttributeId > 0) {
|
||||
// Check redundancy
|
||||
if ($hasAttribute && $currentValue === $targetValue) {
|
||||
$this->logger->debug("Attribute already matches target '$targetValue'. Skipping patch.");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->info("PATCHING Payment $paymentId: Setting tipoPagoStripe = '$targetValue'");
|
||||
$this->logger->info("PATCHING Payment $paymentId: Setting tipoPagoStripe = '$targetValue' (Atributo ID: $paymentAttributeId)");
|
||||
$this->ucrmApi->patch('payments/' . $paymentId, [
|
||||
'attributes' => [
|
||||
[
|
||||
'customAttributeId' => 20,
|
||||
'customAttributeId' => $paymentAttributeId,
|
||||
'value' => $targetValue
|
||||
]
|
||||
]
|
||||
]);
|
||||
} else {
|
||||
$this->logger->debug("No se pudo determinar el tipoPagoStripe.");
|
||||
$this->logger->debug("No se pudo determinar el tipoPagoStripe o no se resolvió el ID del atributo.");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error("Error in ensureStripePaymentAttribute: " . $e->getMessage());
|
||||
|
||||
@ -440,6 +440,11 @@ class ClientCallBellAPI
|
||||
$fileNameComprobante = 'Comprobante_' . $clean_name . '.pdf';
|
||||
$rutaArchivo = __DIR__ . '/../../comprobantes/' . $fileNameComprobante;
|
||||
|
||||
// Asegurar que exista la carpeta de comprobantes
|
||||
if (!is_dir(dirname($rutaArchivo))) {
|
||||
mkdir(dirname($rutaArchivo), 0777, true);
|
||||
}
|
||||
|
||||
// Guardar el contenido del PDF en un archivo local
|
||||
if (file_put_contents($rutaArchivo, $contenidoArchivo) !== false) {
|
||||
$log->appendLog("El archivo PDF se ha descargado y guardado correctamente en: $rutaArchivo" . PHP_EOL);
|
||||
|
||||
@ -112,8 +112,13 @@ class PluginNotifierFacade extends AbstractStripeOperationsFacade
|
||||
|
||||
// 6. Fix Data
|
||||
try {
|
||||
$fixResult = \fixClientData($clientId, $this->ucrmApi, $unmsClientInstance, $attributeIds, $ucrmConfig, $currentPassword);
|
||||
$fixResult = \fixClientData($clientId, $this->ucrmApi, $unmsClientInstance, $attributeIds, $ucrmConfig, $currentPassword, $this->logger);
|
||||
$this->logger->info("Password fix result for client $clientId: " . json_encode($fixResult));
|
||||
|
||||
$passwordVal = $fixResult['password'] ?? '';
|
||||
if (stripos($passwordVal, 'Error') !== false || stripos($passwordVal, '⚠️') !== false) {
|
||||
$this->logger->error("Fallo detectado al obtener la contraseña de la antena del cliente $clientId: $passwordVal");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error executing fixClientData: " . $e->getMessage());
|
||||
}
|
||||
|
||||
@ -37,6 +37,44 @@ class NotificationDataFactory
|
||||
$notificationData->eventName = $jsonData['eventName'];
|
||||
$notificationData->message = $jsonData['extraData']['message'] ?? null;
|
||||
|
||||
// Intentar poblar los datos de la entidad directamente desde el webhook
|
||||
if (isset($jsonData['extraData']['entity'])) {
|
||||
$entityData = $jsonData['extraData']['entity'];
|
||||
if (empty($entityData['clientId']) && isset($jsonData['extraData']['entityBeforeEdit']['clientId'])) {
|
||||
$entityData['clientId'] = $jsonData['extraData']['entityBeforeEdit']['clientId'];
|
||||
}
|
||||
switch ($notificationData->entity) {
|
||||
case 'client':
|
||||
$notificationData->clientData = $entityData;
|
||||
break;
|
||||
case 'invoice':
|
||||
$notificationData->invoiceData = $entityData;
|
||||
break;
|
||||
case 'payment':
|
||||
$notificationData->paymentData = $entityData;
|
||||
break;
|
||||
case 'service':
|
||||
$notificationData->serviceData = $entityData;
|
||||
break;
|
||||
}
|
||||
} elseif (isset($jsonData['extraData']['entityBeforeEdit'])) {
|
||||
$entityData = $jsonData['extraData']['entityBeforeEdit'];
|
||||
switch ($notificationData->entity) {
|
||||
case 'client':
|
||||
$notificationData->clientData = $entityData;
|
||||
break;
|
||||
case 'invoice':
|
||||
$notificationData->invoiceData = $entityData;
|
||||
break;
|
||||
case 'payment':
|
||||
$notificationData->paymentData = $entityData;
|
||||
break;
|
||||
case 'service':
|
||||
$notificationData->serviceData = $entityData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given webhook exists. Skip for manual triggers.
|
||||
if ($notificationData->uuid !== 'manual-trigger') {
|
||||
$this->ucrmApi->query('webhook-events/' . $notificationData->uuid);
|
||||
@ -54,13 +92,13 @@ class NotificationDataFactory
|
||||
$notificationData->clientId = $notificationData->entityId;
|
||||
break;
|
||||
case 'invoice':
|
||||
$notificationData->clientId = $this->getInvoiceData($notificationData)['clientId'] ?? null;
|
||||
$notificationData->clientId = $notificationData->invoiceData['clientId'] ?? $this->getInvoiceData($notificationData)['clientId'] ?? null;
|
||||
break;
|
||||
case 'payment':
|
||||
$notificationData->clientId = $this->getPaymentData($notificationData)['clientId'] ?? null;
|
||||
$notificationData->clientId = $notificationData->paymentData['clientId'] ?? $this->getPaymentData($notificationData)['clientId'] ?? null;
|
||||
break;
|
||||
case 'service':
|
||||
$notificationData->clientId = $this->getServiceData($notificationData)['clientId'] ?? null;
|
||||
$notificationData->clientId = $notificationData->serviceData['clientId'] ?? $this->getServiceData($notificationData)['clientId'] ?? null;
|
||||
break;
|
||||
}
|
||||
if ($notificationData->clientId) {
|
||||
|
||||
135
src/Plugin.php
135
src/Plugin.php
@ -415,6 +415,11 @@ class Plugin
|
||||
$payment_method = 'Desconocido';
|
||||
break;
|
||||
}
|
||||
} else if ($notification->eventName === 'payment.delete' || $notification->eventName === 'payment.unmatch' || $notification->eventName === 'payment.edit') {
|
||||
$this->logger->info("Procesando evento " . $notification->eventName . " para client: " . ($notification->clientId ?? 'unknown'));
|
||||
if ($notification->clientId) {
|
||||
$this->notifierFacade->verifyClientActionToDo($notification);
|
||||
}
|
||||
} else if ($notification->eventName === 'client.edit') {
|
||||
$this->logger->info('Procesando evento client.edit para entityId: ' . ($jsonData['entityId'] ?? 'unknown'));
|
||||
$this->logger->debug('Payload completo client.edit: ' . json_encode($jsonData));
|
||||
@ -435,12 +440,12 @@ class Plugin
|
||||
|
||||
//ejemplo de json_data: {"uuid":"17e043a7-03b5-4312-ab81-a7818124a77e","changeType":"edit","entity":"client","entityId":"158","eventName":"client.edit","extraData":{"entity":{"id":158,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":"2025-02-09T03:15:49-0600","companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"entityBeforeEdit":{"id":158,"userIdent":null,"previousIsp":null,"isLead":true,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false}}}
|
||||
|
||||
if (isset($jsonData['extraData']['entityBeforeEdit'], $jsonData['extraData']['entity'])) {
|
||||
$entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit'];
|
||||
$entity = $jsonData['extraData']['entity'];
|
||||
$entity = $notification->clientData ?? null;
|
||||
if ($entity) {
|
||||
$entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit'] ?? null;
|
||||
|
||||
if (isset($entityBeforeEdit['isLead'], $entity['isLead'])) {
|
||||
$isLeadBefore = $entityBeforeEdit['isLead'];
|
||||
if (isset($entity['isLead'])) {
|
||||
$isLeadBefore = $entityBeforeEdit['isLead'] ?? null;
|
||||
$isLeadAfter = $entity['isLead'];
|
||||
|
||||
if ($isLeadBefore === true && $isLeadAfter === false) {
|
||||
@ -449,75 +454,85 @@ class Plugin
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($entity['tags'], $entityBeforeEdit['tags'])) {
|
||||
$tags = $entity['tags'];
|
||||
$tagsBefore = $entityBeforeEdit['tags'];
|
||||
$tags = $entity['tags'] ?? [];
|
||||
$tagsBefore = $entityBeforeEdit['tags'] ?? [];
|
||||
|
||||
$clabeTagExistsBefore = false;
|
||||
$stripeTagExistsBefore = false;
|
||||
$passwordAntenaTagExistsBefore = false;
|
||||
$clabeTagExistsBefore = false;
|
||||
$stripeTagExistsBefore = false;
|
||||
$passwordAntenaTagExistsBefore = false;
|
||||
|
||||
$clabeTagExists = false;
|
||||
$stripeTagExists = false;
|
||||
$passwordAntenaTagExists = false;
|
||||
$clabeTagExists = false;
|
||||
$stripeTagExists = false;
|
||||
$passwordAntenaTagExists = false;
|
||||
|
||||
foreach ($tagsBefore as $tag) {
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') $clabeTagExistsBefore = true;
|
||||
if ($tag['name'] === 'CREAR CLIENTE STRIPE') $stripeTagExistsBefore = true;
|
||||
if ($tag['name'] === 'OBTENER PASSWORD ANTENA') $passwordAntenaTagExistsBefore = true;
|
||||
}
|
||||
foreach ($tagsBefore as $tag) {
|
||||
if (($tag['name'] ?? '') === 'CREAR CLABE STRIPE') $clabeTagExistsBefore = true;
|
||||
if (($tag['name'] ?? '') === 'CREAR CLIENTE STRIPE') $stripeTagExistsBefore = true;
|
||||
if (($tag['name'] ?? '') === 'OBTENER PASSWORD ANTENA') $passwordAntenaTagExistsBefore = true;
|
||||
}
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') $clabeTagExists = true;
|
||||
if ($tag['name'] === 'CREAR CLIENTE STRIPE') $stripeTagExists = true;
|
||||
if ($tag['name'] === 'OBTENER PASSWORD ANTENA') $passwordAntenaTagExists = true;
|
||||
}
|
||||
foreach ($tags as $tag) {
|
||||
if (($tag['name'] ?? '') === 'CREAR CLABE STRIPE') $clabeTagExists = true;
|
||||
if (($tag['name'] ?? '') === 'CREAR CLIENTE STRIPE') $stripeTagExists = true;
|
||||
if (($tag['name'] ?? '') === 'OBTENER PASSWORD ANTENA') $passwordAntenaTagExists = true;
|
||||
}
|
||||
|
||||
if ($clabeTagExists && !$clabeTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta CREAR CLABE STRIPE se agregó al cliente');
|
||||
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLABE STRIPE', true);
|
||||
}
|
||||
if ($clabeTagExists && !$clabeTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta CREAR CLABE STRIPE se agregó al cliente o está presente');
|
||||
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLABE STRIPE', true);
|
||||
}
|
||||
|
||||
if ($stripeTagExists && !$stripeTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta CREAR CLIENTE STRIPE se agregó al cliente');
|
||||
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLIENTE STRIPE', false);
|
||||
}
|
||||
if ($stripeTagExists && !$stripeTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta CREAR CLIENTE STRIPE se agregó al cliente o está presente');
|
||||
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLIENTE STRIPE', false);
|
||||
}
|
||||
|
||||
if ($passwordAntenaTagExists && !$passwordAntenaTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta OBTENER PASSWORD ANTENA se agregó al cliente');
|
||||
$this->pluginNotifierFacade->processClientPasswordAntenna($clientID, $entity);
|
||||
}
|
||||
if ($passwordAntenaTagExists && !$passwordAntenaTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta OBTENER PASSWORD ANTENA se agregó al cliente o está presente');
|
||||
$this->pluginNotifierFacade->processClientPasswordAntenna((int)$clientID, $entity);
|
||||
}
|
||||
|
||||
// Automatización: Sincronizar cambios de Nombre o Email con Stripe
|
||||
$nameBefore = trim(($entityBeforeEdit['firstName'] ?? '') . ' ' . ($entityBeforeEdit['lastName'] ?? ''));
|
||||
if (empty($nameBefore)) $nameBefore = $entityBeforeEdit['companyName'] ?? '';
|
||||
if ($entityBeforeEdit) {
|
||||
$nameBefore = trim(($entityBeforeEdit['firstName'] ?? '') . ' ' . ($entityBeforeEdit['lastName'] ?? ''));
|
||||
if (empty($nameBefore)) $nameBefore = $entityBeforeEdit['companyName'] ?? '';
|
||||
|
||||
$nameAfter = trim(($entity['firstName'] ?? '') . ' ' . ($entity['lastName'] ?? ''));
|
||||
if (empty($nameAfter)) $nameAfter = $entity['companyName'] ?? '';
|
||||
$nameAfter = trim(($entity['firstName'] ?? '') . ' ' . ($entity['lastName'] ?? ''));
|
||||
if (empty($nameAfter)) $nameAfter = $entity['companyName'] ?? '';
|
||||
|
||||
$emailBefore = null;
|
||||
foreach ($entityBeforeEdit['contacts'] ?? [] as $contact) {
|
||||
if ($contact['isBilling'] || $contact['isContact']) {
|
||||
$emailBefore = $contact['email'] ?? null;
|
||||
if ($emailBefore) break;
|
||||
$emailBefore = null;
|
||||
foreach ($entityBeforeEdit['contacts'] ?? [] as $contact) {
|
||||
if ($contact['isBilling'] || $contact['isContact']) {
|
||||
$emailBefore = $contact['email'] ?? null;
|
||||
if ($emailBefore) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$emailAfter = null;
|
||||
foreach ($entity['contacts'] ?? [] as $contact) {
|
||||
if ($contact['isBilling'] || $contact['isContact']) {
|
||||
$emailAfter = $contact['email'] ?? null;
|
||||
if ($emailAfter) break;
|
||||
$emailAfter = null;
|
||||
foreach ($entity['contacts'] ?? [] as $contact) {
|
||||
if ($contact['isBilling'] || $contact['isContact']) {
|
||||
$emailAfter = $contact['email'] ?? null;
|
||||
if ($emailAfter) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($nameBefore !== $nameAfter || $emailBefore !== $emailAfter) {
|
||||
$this->logger->info("Detectado cambio en datos básicos del cliente $clientID. Sincronizando con Stripe...");
|
||||
$this->pluginNotifierFacade->syncStripeCustomerData((int)$clientID, $nameAfter, $emailAfter);
|
||||
if ($nameBefore !== $nameAfter || $emailBefore !== $emailAfter) {
|
||||
$this->logger->info("Detectado cambio en datos básicos del cliente $clientID. Sincronizando con Stripe...");
|
||||
$this->pluginNotifierFacade->syncStripeCustomerData((int)$clientID, $nameAfter, $emailAfter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData');
|
||||
$this->logger->warning('Los datos del cliente no pudieron recuperarse en clientData');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->logger->info("Refrescando datos del cliente $clientID desde UCRM antes de sincronizar con CallBell...");
|
||||
$refreshedClientData = $this->ucrmApi->get("clients/$clientID");
|
||||
if ($refreshedClientData) {
|
||||
$notification->clientData = $refreshedClientData;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->warning("No se pudieron refrescar los datos del cliente $clientID para CallBell: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->notifierFacade->verifyClientActionToDo($notification);
|
||||
@ -528,8 +543,12 @@ class Plugin
|
||||
$this->logger->debug('Se editó el servicio a un cliente' . PHP_EOL);
|
||||
$this->notifierFacade->verifyServiceActionToDo($notification);
|
||||
//ejemplo de json_data: {"uuid":"06d281ca-d78e-4f0a-a282-3a6b77d25da0","changeType":"edit","entity":"service","entityId":"155","eventName":"service.edit","extraData":{"entity":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]},"entityBeforeEdit":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]}}}
|
||||
$clientID = $jsonData['extraData']['entity']['clientId'];
|
||||
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded($clientID, $jsonData);
|
||||
$clientID = $jsonData['extraData']['entity']['clientId'] ?? $notification->clientId ?? null;
|
||||
if ($clientID) {
|
||||
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded((int)$clientID, $jsonData);
|
||||
} else {
|
||||
$this->logger->warning('No se pudo resolver el ID de cliente en service.edit para updatePasswordAntenaIfNeeded');
|
||||
}
|
||||
} else if ($notification->eventName === 'service.suspend') {
|
||||
$this->logger->debug('Se suspendió el servicio a un cliente' . PHP_EOL);
|
||||
$this->notifierFacade->verifyServiceActionToDo($notification);
|
||||
|
||||
@ -67,14 +67,17 @@ class PaymentIntentService
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $client['id'],
|
||||
'fullName' => ($client['clientType'] == 1)
|
||||
'id' => $client['id'],
|
||||
'fullName' => ($client['clientType'] == 1)
|
||||
? $client['firstName'] . ' ' . $client['lastName']
|
||||
: $client['companyName'],
|
||||
'stripeCustomerId' => $stripeCustomerId,
|
||||
'stripeCustomerId' => $stripeCustomerId,
|
||||
'clabeInterbancaria' => $clabeInterbancaria,
|
||||
'email' => $this->getClientEmail($client),
|
||||
'accountOutstanding' => $client['accountOutstanding'] ?? 0
|
||||
'email' => $this->getClientEmail($client),
|
||||
'accountOutstanding' => $client['accountOutstanding'] ?? 0,
|
||||
'accountBalance' => $client['accountBalance'] ?? 0, // crédito a favor del cliente
|
||||
'accountCredit' => $client['accountCredit'] ?? 0,
|
||||
'fullAddress' => $client['fullAddress'] ?? '',
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
return ['error' => $e->getMessage()];
|
||||
@ -172,15 +175,16 @@ class PaymentIntentService
|
||||
}
|
||||
}
|
||||
|
||||
$dt = (new \DateTime('@' . $payment->created))->setTimezone(new \DateTimeZone('America/Mexico_City'));
|
||||
$result[] = [
|
||||
'id' => $payment->id,
|
||||
'amount' => $payment->amount / 100,
|
||||
'currency' => strtoupper($payment->currency),
|
||||
'status' => $payment->status,
|
||||
'created' => $payment->created,
|
||||
'date' => date('d/m/Y H:i', $payment->created),
|
||||
'id' => $payment->id,
|
||||
'amount' => $payment->amount / 100,
|
||||
'currency' => strtoupper($payment->currency),
|
||||
'status' => $payment->status,
|
||||
'created' => $payment->created,
|
||||
'date' => $dt->format('d/m/Y H:i'),
|
||||
'description' => $description,
|
||||
'reference' => $reference
|
||||
'reference' => $reference
|
||||
];
|
||||
|
||||
if (count($result) >= $limit) break;
|
||||
|
||||
21
test_patch.php
Executable file
21
test_patch.php
Executable file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
$config = json_decode(file_get_contents(__DIR__ . '/data/config.json'), true);
|
||||
$ucrmOptions = [
|
||||
'base_uri' => 'https://localhost/crm/api/v1.0/',
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'X-Auth-App-Key' => $config['apitoken'],
|
||||
'Content-Type' => 'application/json'
|
||||
]
|
||||
];
|
||||
$ucrmClient = new \GuzzleHttp\Client($ucrmOptions);
|
||||
|
||||
try {
|
||||
$res = $ucrmClient->patch('payments/1030', [
|
||||
'json' => ['methodId' => 'b01c0b35-b42c-48d9-9ad9-ea6591adfbbb']
|
||||
]);
|
||||
echo "Success: " . $res->getStatusCode() . "\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
5
test_script.php
Executable file
5
test_script.php
Executable file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
$ucrmApi = \Ubnt\UcrmPluginSdk\Service\UcrmApi::create();
|
||||
$payments = $ucrmApi->get('payments?limit=5&direction=DESC');
|
||||
print_r(array_map(function($p) { return ['id' => $p['id'], 'methodId' => $p['methodId'], 'note' => $p['note'], 'providerName' => $p['providerName']]; }, $payments));
|
||||
14
test_script2.php
Executable file
14
test_script2.php
Executable file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
$options = [
|
||||
'base_uri' => 'https://localhost/crm/api/v1.0/',
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'X-Auth-App-Key' => json_decode(file_get_contents(__DIR__ . '/data/config.json'), true)['apitoken'],
|
||||
'Content-Type' => 'application/json'
|
||||
]
|
||||
];
|
||||
$client = new \GuzzleHttp\Client($options);
|
||||
$response = $client->get('payments?limit=10&direction=DESC');
|
||||
$payments = json_decode($response->getBody()->getContents(), true);
|
||||
print_r(array_map(function($p) { return ['id' => $p['id'], 'methodId' => $p['methodId'], 'note' => $p['note']]; }, $payments));
|
||||
44
test_stripe.php
Executable file
44
test_stripe.php
Executable file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
$config = json_decode(file_get_contents(__DIR__ . '/data/config.json'), true);
|
||||
$ucrmOptions = [
|
||||
'base_uri' => 'https://localhost/crm/api/v1.0/',
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'X-Auth-App-Key' => $config['apitoken'],
|
||||
'Content-Type' => 'application/json'
|
||||
]
|
||||
];
|
||||
$ucrmClient = new \GuzzleHttp\Client($ucrmOptions);
|
||||
|
||||
// Fetch client 157
|
||||
$res = $ucrmClient->get('clients/157');
|
||||
$client = json_decode($res->getBody()->getContents(), true);
|
||||
$stripeCustomerId = null;
|
||||
foreach ($client['attributes'] as $attr) {
|
||||
if (stripos($attr['name'], 'Stripe') !== false && stripos($attr['name'], 'Customer') !== false) {
|
||||
$stripeCustomerId = $attr['value'];
|
||||
}
|
||||
}
|
||||
if (!$stripeCustomerId) {
|
||||
foreach ($client['attributes'] as $attr) {
|
||||
if ($attr['key'] === 'stripeCustomerId' || $attr['key'] === 'customerIdStripe') {
|
||||
$stripeCustomerId = $attr['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
echo "Stripe Customer ID: $stripeCustomerId\n";
|
||||
|
||||
if ($stripeCustomerId && isset($config['tokenstripe'])) {
|
||||
$stripeOptions = [
|
||||
'base_uri' => 'https://api.stripe.com/v1/',
|
||||
'auth' => [$config['tokenstripe'], ''],
|
||||
];
|
||||
$stripeClient = new \GuzzleHttp\Client($stripeOptions);
|
||||
$res = $stripeClient->get("payment_intents?customer=$stripeCustomerId&limit=3");
|
||||
$intents = json_decode($res->getBody()->getContents(), true);
|
||||
foreach ($intents['data'] as $intent) {
|
||||
echo "PaymentIntent: {$intent['id']}, Amount: {$intent['amount']}, Status: {$intent['status']}, Types: " . implode(',', $intent['payment_method_types']) . "\n";
|
||||
echo "Metadata: " . json_encode($intent['metadata']) . "\n";
|
||||
}
|
||||
}
|
||||
90014
unms-swagger.json
90014
unms-swagger.json
File diff suppressed because it is too large
Load Diff
8392
unmscrm.apib
8392
unmscrm.apib
File diff suppressed because it is too large
Load Diff
8
vendor/aws/aws-sdk-php/composer.json
vendored
8
vendor/aws/aws-sdk-php/composer.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"homepage": "http://aws.amazon.com/sdkforphp",
|
||||
"homepage": "https://aws.amazon.com/sdk-for-php",
|
||||
"description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
|
||||
"keywords": ["aws","amazon","sdk","s3","ec2","dynamodb","cloud","glacier"],
|
||||
"type": "library",
|
||||
@ -8,7 +8,7 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Amazon Web Services",
|
||||
"homepage": "http://aws.amazon.com"
|
||||
"homepage": "https://aws.amazon.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
@ -33,7 +33,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-sockets": "*",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"behat/behat": "~3.0",
|
||||
"doctrine/cache": "~1.4",
|
||||
"aws/aws-php-sns-message-validator": "~1.0",
|
||||
@ -42,7 +42,7 @@
|
||||
"psr/simple-cache": "^2.0 || ^3.0",
|
||||
"sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0",
|
||||
"yoast/phpunit-polyfills": "^2.0",
|
||||
"dms/phpunit-arraysubset-asserts": "^0.4.0"
|
||||
"dms/phpunit-arraysubset-asserts": "^v0.5.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
|
||||
|
||||
@ -21,10 +21,14 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise createAnalyzerAsync(array $args = [])
|
||||
* @method \Aws\Result createArchiveRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createArchiveRuleAsync(array $args = [])
|
||||
* @method \Aws\Result createServiceLinkedAnalyzer(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createServiceLinkedAnalyzerAsync(array $args = [])
|
||||
* @method \Aws\Result deleteAnalyzer(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteAnalyzerAsync(array $args = [])
|
||||
* @method \Aws\Result deleteArchiveRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteArchiveRuleAsync(array $args = [])
|
||||
* @method \Aws\Result deleteServiceLinkedAnalyzer(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteServiceLinkedAnalyzerAsync(array $args = [])
|
||||
* @method \Aws\Result generateFindingRecommendation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise generateFindingRecommendationAsync(array $args = [])
|
||||
* @method \Aws\Result getAccessPreview(array $args = [])
|
||||
|
||||
2
vendor/aws/aws-sdk-php/src/Acm/AcmClient.php
vendored
2
vendor/aws/aws-sdk-php/src/Acm/AcmClient.php
vendored
@ -36,6 +36,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise resendValidationEmailAsync(array $args = [])
|
||||
* @method \Aws\Result revokeCertificate(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise revokeCertificateAsync(array $args = [])
|
||||
* @method \Aws\Result searchCertificates(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise searchCertificatesAsync(array $args = [])
|
||||
* @method \Aws\Result updateCertificateOptions(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateCertificateOptionsAsync(array $args = [])
|
||||
*/
|
||||
|
||||
664
vendor/aws/aws-sdk-php/src/Api/Cbor/CborDecoder.php
vendored
Executable file
664
vendor/aws/aws-sdk-php/src/Api/Cbor/CborDecoder.php
vendored
Executable file
@ -0,0 +1,664 @@
|
||||
<?php
|
||||
namespace Aws\Api\Cbor;
|
||||
|
||||
use Aws\Api\Cbor\Exception\CborException;
|
||||
|
||||
/**
|
||||
* Decodes Concise Binary Object Representation encoded strings
|
||||
* into PHP values according to RFC 8949
|
||||
*
|
||||
* https://www.rfc-editor.org/rfc/rfc8949.html
|
||||
*
|
||||
* Supports Major types 0-7 including:
|
||||
* - Type 0: Unsigned integers
|
||||
* - Type 1: Negative integers
|
||||
* - Type 2: Byte strings
|
||||
* - Type 3: Text strings (UTF-8)
|
||||
* - Type 4: Arrays
|
||||
* - Type 5: Maps
|
||||
* - Type 6: Tagged values (timestamps)
|
||||
* - Type 7: Simple values (null, bool, float)
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class CborDecoder
|
||||
{
|
||||
private int $offset;
|
||||
private int $length;
|
||||
|
||||
/**
|
||||
* Decode CBOR binary data to PHP value
|
||||
*
|
||||
* @param string $data The CBOR-encoded binary data to decode
|
||||
*
|
||||
* @return mixed The decoded PHP value (can be any type: int, string, array, bool, null, float)
|
||||
* @throws CborException If data is empty or malformed CBOR
|
||||
*/
|
||||
public function decode(string $data): mixed
|
||||
{
|
||||
if ($data === '') {
|
||||
throw new CborException("No data to decode");
|
||||
}
|
||||
|
||||
$this->offset = 0;
|
||||
$this->length = strlen($data);
|
||||
|
||||
return $this->decodeValue($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode multiple CBOR values from sequential binary data
|
||||
*
|
||||
* @param string $data The CBOR-encoded binary data containing multiple values
|
||||
*
|
||||
* @return array Array of decoded PHP values in the order they appear in the data
|
||||
* @throws CborException If data is malformed CBOR
|
||||
*/
|
||||
public function decodeAll(string $data): array
|
||||
{
|
||||
$this->length = strlen($data);
|
||||
$this->offset = 0;
|
||||
$values = [];
|
||||
|
||||
while ($this->offset < $this->length) {
|
||||
$values[] = $this->decodeValue($data);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a single CBOR value at the current offset
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
*
|
||||
* @return mixed The decoded value
|
||||
* @throws CborException If unexpected end of data or invalid CBOR format
|
||||
*/
|
||||
private function decodeValue(string &$data): mixed
|
||||
{
|
||||
$offset = $this->offset;
|
||||
$length = $this->length;
|
||||
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
$byte = ord($data[$offset++]);
|
||||
$majorType = $byte >> 5;
|
||||
$info = $byte & 0x1F;
|
||||
|
||||
switch ($majorType) {
|
||||
case 0: // Unsigned integer
|
||||
if ($info < 24) {
|
||||
$this->offset = $offset;
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 1;
|
||||
|
||||
return ord($data[$offset]);
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 2;
|
||||
|
||||
return (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 4;
|
||||
|
||||
return unpack('N', $data, $offset)[1];
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 8;
|
||||
|
||||
return unpack('J', $data, $offset)[1];
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for integer: $info");
|
||||
}
|
||||
|
||||
case 1: // Negative integer
|
||||
if ($info < 24) {
|
||||
$this->offset = $offset;
|
||||
|
||||
return -1 - $info;
|
||||
}
|
||||
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 1;
|
||||
|
||||
return -1 - ord($data[$offset]);
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 2;
|
||||
|
||||
return -1 - ((ord($data[$offset]) << 8) | ord($data[$offset + 1]));
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 4;
|
||||
|
||||
return -1 - unpack('N', $data, $offset)[1];
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 8;
|
||||
$unsigned = unpack('J', $data, $offset)[1];
|
||||
|
||||
return ($unsigned === 9223372036854775807) ? PHP_INT_MIN : -1 - $unsigned;
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for integer: $info");
|
||||
}
|
||||
|
||||
case 2: // Byte string
|
||||
if ($info < 24) {
|
||||
$len = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteString($data, 0x40);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for byte string: $info");
|
||||
}
|
||||
}
|
||||
|
||||
if ($offset + $len > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + $len;
|
||||
|
||||
return substr($data, $offset, $len);
|
||||
|
||||
case 3: // Text string
|
||||
if ($info < 24) {
|
||||
$len = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteString($data, 0x60);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for text string: $info");
|
||||
}
|
||||
}
|
||||
|
||||
if ($offset + $len > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + $len;
|
||||
|
||||
return substr($data, $offset, $len);
|
||||
|
||||
case 4: // Array
|
||||
if ($info < 24) {
|
||||
$count = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteArray($data);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for array: $info");
|
||||
}
|
||||
}
|
||||
|
||||
$this->offset = $offset;
|
||||
$arr = [];
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$arr[] = $this->decodeValue($data);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
|
||||
case 5: // Map
|
||||
if ($info < 24) {
|
||||
$count = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteMap($data);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for map: $info");
|
||||
}
|
||||
}
|
||||
|
||||
$this->offset = $offset;
|
||||
$map = [];
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$key = $this->decodeValue($data);
|
||||
$map[$key] = $this->decodeValue($data);
|
||||
}
|
||||
|
||||
return $map;
|
||||
|
||||
case 6: // Tag
|
||||
switch ($info) {
|
||||
case 24:
|
||||
$offset++;
|
||||
break;
|
||||
|
||||
case 25:
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
$offset += 8;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeValue($data);
|
||||
|
||||
case 7: // Simple/float
|
||||
switch ($info) {
|
||||
case 20:
|
||||
$this->offset = $offset;
|
||||
|
||||
return false;
|
||||
|
||||
case 21:
|
||||
$this->offset = $offset;
|
||||
|
||||
return true;
|
||||
|
||||
case 22:
|
||||
case 23:
|
||||
$this->offset = $offset;
|
||||
|
||||
return null;
|
||||
|
||||
case 25: // Half-precision float
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 2;
|
||||
$half = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$sign = ($half >> 15) & 0x01;
|
||||
$exp = ($half >> 10) & 0x1F;
|
||||
$mant = $half & 0x3FF;
|
||||
|
||||
if ($exp === 0) {
|
||||
return $mant === 0
|
||||
? ($sign ? -0.0 : 0.0)
|
||||
: ($sign ? -1 : 1) * pow(2, -14) * ($mant / 1024);
|
||||
}
|
||||
|
||||
if ($exp === 31) {
|
||||
return $mant === 0 ? ($sign ? -INF : INF) : NAN;
|
||||
}
|
||||
|
||||
return (float) (($sign ? -1 : 1) * pow(2, $exp - 15) * (1 + $mant / 1024));
|
||||
|
||||
case 26: // Single-precision float
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 4;
|
||||
|
||||
return unpack('G', $data, $offset)[1];
|
||||
|
||||
case 27: // Double-precision float
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 8;
|
||||
|
||||
return unpack('E', $data, $offset)[1];
|
||||
|
||||
case 31:
|
||||
throw new CborException("Unexpected break");
|
||||
|
||||
default:
|
||||
throw new CborException("Unknown simple value: $info");
|
||||
}
|
||||
|
||||
default:
|
||||
throw new CborException("Unknown major type: $majorType");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode indefinite-length string (byte or text)
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
* @param int $expectedMajor Expected major type (0x40 for byte string, 0x60 for text string)
|
||||
*
|
||||
* @return string The concatenated string from all chunks
|
||||
* @throws CborException If invalid chunk format or unexpected end of data
|
||||
*/
|
||||
private function decodeIndefiniteString(string &$data, int $expectedMajor): string
|
||||
{
|
||||
$chunks = [];
|
||||
|
||||
while (true) {
|
||||
$offset = $this->offset;
|
||||
$length = $this->length;
|
||||
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
$byte = ord($data[$offset++]);
|
||||
|
||||
if ($byte === 0xFF) {
|
||||
$this->offset = $offset;
|
||||
|
||||
return implode('', $chunks);
|
||||
}
|
||||
|
||||
if (($byte & 0xE0) !== $expectedMajor) {
|
||||
throw new CborException("Invalid chunk in indefinite string");
|
||||
}
|
||||
|
||||
$info = $byte & 0x1F;
|
||||
|
||||
if ($info === 31) {
|
||||
throw new CborException("Nested indefinite string");
|
||||
}
|
||||
|
||||
if ($info < 24) {
|
||||
$len = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid chunk length info: $info");
|
||||
}
|
||||
}
|
||||
|
||||
if ($offset + $len > $length) {
|
||||
throw new CborException("Not enough data for chunk");
|
||||
}
|
||||
|
||||
$chunks[] = substr($data, $offset, $len);
|
||||
$this->offset = $offset + $len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode indefinite-length array
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
*
|
||||
* @return array The decoded array elements
|
||||
* @throws CborException If unexpected end of data
|
||||
*/
|
||||
private function decodeIndefiniteArray(string &$data): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
while (true) {
|
||||
if ($this->offset >= $this->length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
if (ord($data[$this->offset]) === 0xFF) {
|
||||
$this->offset++;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result[] = $this->decodeValue($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode indefinite-length map
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
*
|
||||
* @return array The decoded map as associative array
|
||||
* @throws CborException If unexpected end of data or odd number of items
|
||||
*/
|
||||
private function decodeIndefiniteMap(string &$data): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
while (true) {
|
||||
if ($this->offset >= $this->length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
if (ord($data[$this->offset]) === 0xFF) {
|
||||
$this->offset++;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$key = $this->decodeValue($data);
|
||||
$result[$key] = $this->decodeValue($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
345
vendor/aws/aws-sdk-php/src/Api/Cbor/CborEncoder.php
vendored
Executable file
345
vendor/aws/aws-sdk-php/src/Api/Cbor/CborEncoder.php
vendored
Executable file
@ -0,0 +1,345 @@
|
||||
<?php
|
||||
namespace Aws\Api\Cbor;
|
||||
|
||||
use Aws\Api\Cbor\Exception\CborException;
|
||||
|
||||
/**
|
||||
* Encodes PHP values to Concise Binary Object Representation according to RFC 8949
|
||||
* https://www.rfc-editor.org/rfc/rfc8949.html
|
||||
*
|
||||
* Supports Major types 0-7 including:
|
||||
* - Type 0: Unsigned integers
|
||||
* - Type 1: Negative integers
|
||||
* - Type 2: Byte strings (via ['__cbor_bytes' => $data] wrappers)
|
||||
* - Type 3: Text strings (UTF-8)
|
||||
* - Type 4: Arrays
|
||||
* - Type 5: Maps
|
||||
* - Type 6: Tagged values (timestamps)
|
||||
* - Type 7: Simple values (null, bool, float)
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class CborEncoder
|
||||
{
|
||||
/**
|
||||
* Pre-encoded integers 0-23 (single byte) and common larger values
|
||||
* CBOR major type 0 (unsigned integer)
|
||||
*/
|
||||
private const INT_CACHE = [
|
||||
0 => "\x00", 1 => "\x01", 2 => "\x02", 3 => "\x03",
|
||||
4 => "\x04", 5 => "\x05", 6 => "\x06", 7 => "\x07",
|
||||
8 => "\x08", 9 => "\x09", 10 => "\x0A", 11 => "\x0B",
|
||||
12 => "\x0C", 13 => "\x0D", 14 => "\x0E", 15 => "\x0F",
|
||||
16 => "\x10", 17 => "\x11", 18 => "\x12", 19 => "\x13",
|
||||
20 => "\x14", 21 => "\x15", 22 => "\x16", 23 => "\x17",
|
||||
24 => "\x18\x18", 25 => "\x18\x19", 26 => "\x18\x1A",
|
||||
32 => "\x18\x20", 50 => "\x18\x32", 64 => "\x18\x40",
|
||||
100 => "\x18\x64", 128 => "\x18\x80", 200 => "\x18\xC8",
|
||||
255 => "\x18\xFF", 256 => "\x19\x01\x00", 500 => "\x19\x01\xF4",
|
||||
1000 => "\x19\x03\xE8", 1023 => "\x19\x03\xFF",
|
||||
];
|
||||
|
||||
/**
|
||||
* Pre-encoded negative integers -1 to -24 and common larger values
|
||||
* CBOR major type 1 (negative integer)
|
||||
*/
|
||||
private const NEG_CACHE = [
|
||||
-1 => "\x20", -2 => "\x21", -3 => "\x22", -4 => "\x23",
|
||||
-5 => "\x24", -10 => "\x29", -20 => "\x33", -24 => "\x37",
|
||||
-25 => "\x38\x18", -50 => "\x38\x31", -100 => "\x38\x63",
|
||||
];
|
||||
|
||||
/**
|
||||
* Encode a PHP value to CBOR binary string
|
||||
*
|
||||
* @param mixed $value The value to encode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encode(mixed $value): string
|
||||
{
|
||||
return $this->encodeValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively encode a value to CBOR
|
||||
*
|
||||
* @param mixed $value Value to encode
|
||||
* @return string Encoded CBOR bytes
|
||||
*/
|
||||
private function encodeValue(mixed $value): string
|
||||
{
|
||||
switch (gettype($value)) {
|
||||
case 'string':
|
||||
$len = strlen($value);
|
||||
if ($len < 24) {
|
||||
return chr(0x60 | $len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x100) {
|
||||
return "\x78" . chr($len) . $value;
|
||||
}
|
||||
|
||||
return $this->encodeTextString($value);
|
||||
|
||||
case 'array':
|
||||
if (isset($value['__cbor_timestamp'])) {
|
||||
return "\xC1\xFB" . pack('E', $value['__cbor_timestamp']);
|
||||
}
|
||||
|
||||
// Encode a byte string (major type 2)
|
||||
if (isset($value['__cbor_bytes'])) {
|
||||
$bytes = $value['__cbor_bytes'];
|
||||
$len = strlen($bytes);
|
||||
if ($len < 24) {
|
||||
return chr(0x40 | $len) . $bytes;
|
||||
}
|
||||
|
||||
if ($len < 0x100) {
|
||||
return "\x58" . chr($len) . $bytes;
|
||||
}
|
||||
|
||||
if ($len < 0x10000) {
|
||||
return "\x59" . pack('n', $len) . $bytes;
|
||||
}
|
||||
|
||||
return "\x5A" . pack('N', $len) . $bytes;
|
||||
}
|
||||
|
||||
if (array_is_list($value)) {
|
||||
return $this->encodeArray($value);
|
||||
}
|
||||
|
||||
return $this->encodeMap($value);
|
||||
|
||||
case 'integer':
|
||||
if (isset(self::INT_CACHE[$value])) {
|
||||
return self::INT_CACHE[$value];
|
||||
}
|
||||
|
||||
if (isset(self::NEG_CACHE[$value])) {
|
||||
return self::NEG_CACHE[$value];
|
||||
}
|
||||
|
||||
// Fast path for positive integers
|
||||
// Major type 0: unsigned integer
|
||||
if ($value >= 0) {
|
||||
if ($value < 24) {
|
||||
return chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x100) {
|
||||
return "\x18" . chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x10000) {
|
||||
return "\x19" . pack('n', $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100000000) {
|
||||
return "\x1A" . pack('N', $value);
|
||||
}
|
||||
|
||||
return "\x1B" . pack('J', $value);
|
||||
}
|
||||
|
||||
return $this->encodeInteger($value);
|
||||
|
||||
case 'double':
|
||||
// Encode a float (major type 7, float 64)
|
||||
return "\xFB" . pack('E', $value);
|
||||
|
||||
case 'boolean':
|
||||
// Encode a boolean (major type 7, simple)
|
||||
return $value ? "\xF5" : "\xF4";
|
||||
|
||||
case 'NULL':
|
||||
// Encode null (major type 7, simple)
|
||||
return "\xF6";
|
||||
|
||||
case 'object':
|
||||
throw new CborException("Cannot encode object of type: " . get_class($value));
|
||||
|
||||
default:
|
||||
throw new CborException("Cannot encode value of type: " . gettype($value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an integer (major type 0 or 1)
|
||||
*
|
||||
* @param int $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeInteger(int $value): string
|
||||
{
|
||||
if (isset(self::INT_CACHE[$value])) {
|
||||
return self::INT_CACHE[$value];
|
||||
}
|
||||
|
||||
if (isset(self::NEG_CACHE[$value])) {
|
||||
return self::NEG_CACHE[$value];
|
||||
}
|
||||
|
||||
if ($value >= 0) {
|
||||
// Major type 0: unsigned integer
|
||||
if ($value < 24) {
|
||||
return chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x100) {
|
||||
return "\x18" . chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x10000) {
|
||||
return "\x19" . pack('n', $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100000000) {
|
||||
return "\x1A" . pack('N', $value);
|
||||
}
|
||||
|
||||
return "\x1B" . pack('J', $value);
|
||||
}
|
||||
|
||||
// Major type 1: negative integer (-1 - n)
|
||||
$value = -1 - $value;
|
||||
if ($value < 24) {
|
||||
return chr(0x20 | $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100) {
|
||||
return "\x38" . chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x10000) {
|
||||
return "\x39" . pack('n', $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100000000) {
|
||||
return "\x3A" . pack('N', $value);
|
||||
}
|
||||
|
||||
return "\x3B" . pack('J', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a text string (major type 3)
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeTextString(string $value): string
|
||||
{
|
||||
$len = strlen($value);
|
||||
|
||||
if ($len < 24) {
|
||||
return chr(0x60 | $len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x100) {
|
||||
return "\x78" . chr($len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x10000) {
|
||||
return "\x79" . pack('n', $len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x100000000) {
|
||||
return "\x7A" . pack('N', $len) . $value;
|
||||
}
|
||||
|
||||
return "\x7B" . pack('J', $len) . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an array (major type 4)
|
||||
*
|
||||
* @param array $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeArray(array $value): string
|
||||
{
|
||||
$count = count($value);
|
||||
|
||||
if ($count < 24) {
|
||||
$result = chr(0x80 | $count);
|
||||
} elseif ($count < 0x100) {
|
||||
$result = "\x98" . chr($count);
|
||||
} elseif ($count < 0x10000) {
|
||||
$result = "\x99" . pack('n', $count);
|
||||
} elseif ($count < 0x100000000) {
|
||||
$result = "\x9A" . pack('N', $count);
|
||||
} else {
|
||||
$result = "\x9B" . pack('J', $count);
|
||||
}
|
||||
|
||||
foreach ($value as $item) {
|
||||
$result .= $this->encodeValue($item);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a map (major type 5)
|
||||
*
|
||||
* @param array $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeMap(array $value): string
|
||||
{
|
||||
$count = count($value);
|
||||
|
||||
if ($count < 24) {
|
||||
$result = chr(0xA0 | $count);
|
||||
} elseif ($count < 0x100) {
|
||||
$result = "\xB8" . chr($count);
|
||||
} elseif ($count < 0x10000) {
|
||||
$result = "\xB9" . pack('n', $count);
|
||||
} elseif ($count < 0x100000000) {
|
||||
$result = "\xBA" . pack('N', $count);
|
||||
} else {
|
||||
$result = "\xBB" . pack('J', $count);
|
||||
}
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_int($k)) {
|
||||
$result .= $this->encodeInteger($k);
|
||||
} else {
|
||||
$len = strlen($k);
|
||||
if ($len < 24) {
|
||||
$result .= chr(0x60 | $len) . $k;
|
||||
} elseif ($len < 0x100) {
|
||||
$result .= "\x78" . chr($len) . $k;
|
||||
} else {
|
||||
$result .= "\x79" . pack('n', $len) . $k;
|
||||
}
|
||||
}
|
||||
|
||||
$result .= $this->encodeValue($v);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty map (major type 5 with 0 elements)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encodeEmptyMap(): string
|
||||
{
|
||||
return "\xA0";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty indefinite map (major type 5 indefinite length)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encodeEmptyIndefiniteMap(): string
|
||||
{
|
||||
return "\xBF\xFF";
|
||||
}
|
||||
}
|
||||
6
vendor/aws/aws-sdk-php/src/Api/Cbor/Exception/CborException.php
vendored
Executable file
6
vendor/aws/aws-sdk-php/src/Api/Cbor/Exception/CborException.php
vendored
Executable file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Aws\Api\Cbor\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class CborException extends RuntimeException {}
|
||||
@ -30,11 +30,6 @@ class DateTimeResult extends \DateTime implements \JsonSerializable
|
||||
throw new ParserException('Invalid timestamp value passed to DateTimeResult::fromEpoch');
|
||||
}
|
||||
|
||||
// PHP 5.5 does not support sub-second precision
|
||||
if (\PHP_VERSION_ID < 56000) {
|
||||
return new self(gmdate('c', $unixTimestamp));
|
||||
}
|
||||
|
||||
$decimalSeparator = isset(localeconv()['decimal_point']) ? localeconv()['decimal_point'] : ".";
|
||||
$formatString = "U" . $decimalSeparator . "u";
|
||||
$dateTime = DateTime::createFromFormat(
|
||||
|
||||
@ -31,19 +31,6 @@ abstract class AbstractErrorParser
|
||||
StructureShape $member
|
||||
);
|
||||
|
||||
protected function extractPayload(
|
||||
StructureShape $member,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
if ($member instanceof StructureShape) {
|
||||
// Structure members parse top-level data into a specific key.
|
||||
return $this->payload($response, $member);
|
||||
} else {
|
||||
// Streaming data is just the stream from the response body.
|
||||
return $response->getBody();
|
||||
}
|
||||
}
|
||||
|
||||
protected function populateShape(
|
||||
array &$data,
|
||||
ResponseInterface $response,
|
||||
@ -57,16 +44,15 @@ abstract class AbstractErrorParser
|
||||
if (!empty($data['code'])) {
|
||||
|
||||
$errors = $this->api->getOperation($command->getName())->getErrors();
|
||||
foreach ($errors as $key => $error) {
|
||||
foreach ($errors as $error) {
|
||||
|
||||
// If error code matches a known error shape, populate the body
|
||||
if ($this->errorCodeMatches($data, $error)) {
|
||||
$modeledError = $error;
|
||||
$data['body'] = $this->extractPayload(
|
||||
$modeledError,
|
||||
$response
|
||||
$data['body'] = $this->payload(
|
||||
$response,
|
||||
$error
|
||||
);
|
||||
$data['error_shape'] = $modeledError;
|
||||
$data['error_shape'] = $error;
|
||||
|
||||
foreach ($error->getMembers() as $name => $member) {
|
||||
switch ($member['location']) {
|
||||
|
||||
159
vendor/aws/aws-sdk-php/src/Api/ErrorParser/AbstractRpcV2ErrorParser.php
vendored
Executable file
159
vendor/aws/aws-sdk-php/src/Api/ErrorParser/AbstractRpcV2ErrorParser.php
vendored
Executable file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
namespace Aws\Api\ErrorParser;
|
||||
|
||||
use Aws\Api\Parser\AbstractParser;
|
||||
use Aws\Api\StructureShape;
|
||||
use Aws\CommandInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Base implementation for Smithy RPC V2 protocol error parsers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractRpcV2ErrorParser extends AbstractErrorParser
|
||||
{
|
||||
private const HEADER_QUERY_ERROR = 'x-amzn-query-error';
|
||||
private const HEADER_ERROR_TYPE = 'x-amzn-errortype';
|
||||
private const HEADER_REQUEST_ID = 'x-amzn-requestid';
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
* @param CommandInterface|null $command
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __invoke(
|
||||
ResponseInterface $response,
|
||||
?CommandInterface $command = null
|
||||
) {
|
||||
$response = AbstractParser::getResponseWithCachingStream($response);
|
||||
$data = $this->parseError($response);
|
||||
|
||||
if (isset($data['parsed']['__type'])) {
|
||||
$data['message'] = $data['parsed']['message'] ?? null;
|
||||
}
|
||||
|
||||
$this->populateShape($data, $response, $command);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
* @param StructureShape $member
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function payload(
|
||||
ResponseInterface $response,
|
||||
StructureShape $member
|
||||
): array;
|
||||
|
||||
/**
|
||||
* @param StreamInterface $body
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function parseBody(
|
||||
StreamInterface $body,
|
||||
ResponseInterface $response
|
||||
): mixed;
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function parseError(ResponseInterface $response): array
|
||||
{
|
||||
$statusCode = (string) $response->getStatusCode();
|
||||
$errorCode = null;
|
||||
$errorType = null;
|
||||
|
||||
if ($this->api?->getMetadata('awsQueryCompatible') !== null
|
||||
&& $response->hasHeader(self::HEADER_QUERY_ERROR)
|
||||
&& $awsQueryError = $this->parseQueryCompatibleHeader($response)
|
||||
) {
|
||||
$errorCode = $awsQueryError['code'];
|
||||
$errorType = $awsQueryError['type'];
|
||||
}
|
||||
|
||||
if (!$errorCode && $response->hasHeader(self::HEADER_ERROR_TYPE)) {
|
||||
$errorCode = $this->extractErrorCode(
|
||||
$response->getHeaderLine(self::HEADER_ERROR_TYPE)
|
||||
);
|
||||
}
|
||||
|
||||
$parsedBody = null;
|
||||
$body = $response->getBody();
|
||||
if ($body->getSize()) {
|
||||
//TODO handle unseekable streams with CachingStream
|
||||
$parsedBody = array_change_key_case($this->parseBody($body, $response));
|
||||
}
|
||||
|
||||
if (!$errorCode && $parsedBody) {
|
||||
$errorCode = $this->extractErrorCode(
|
||||
$parsedBody['code'] ?? $parsedBody['__type'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'request_id' => $response->getHeaderLine(self::HEADER_REQUEST_ID),
|
||||
'code' => $errorCode ?: null,
|
||||
'message' => null,
|
||||
'type' => $errorType ?? ($statusCode[0] === '4' ? 'client' : 'server'),
|
||||
'parsed' => $parsedBody,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse AWS Query Compatible error from header
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return array|null Returns ['code' => string, 'type' => string] or null
|
||||
*/
|
||||
private function parseQueryCompatibleHeader(ResponseInterface $response): ?array
|
||||
{
|
||||
$parts = explode(';', $response->getHeaderLine(self::HEADER_QUERY_ERROR));
|
||||
if (count($parts) === 2 && $parts[0] && $parts[1]) {
|
||||
return [
|
||||
'code' => $parts[0],
|
||||
'type' => $parts[1],
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract error code from raw error string containing # and/or : delimiters
|
||||
*
|
||||
* @param string $rawErrorCode
|
||||
* @return string
|
||||
*/
|
||||
private function extractErrorCode(string $rawErrorCode): string
|
||||
{
|
||||
// Handle format with both # and uri (e.g., "namespace#ErrorCode:http://foo-bar")
|
||||
if (str_contains($rawErrorCode, ':') && str_contains($rawErrorCode, '#')) {
|
||||
$start = strpos($rawErrorCode, '#') + 1;
|
||||
$end = strpos($rawErrorCode, ':', $start);
|
||||
return substr($rawErrorCode, $start, $end - $start);
|
||||
}
|
||||
|
||||
// Handle format with uri only : (e.g., "ErrorCode:http://foo-bar.com/baz")
|
||||
if (str_contains($rawErrorCode, ':')) {
|
||||
return substr($rawErrorCode, 0, strpos($rawErrorCode, ':'));
|
||||
}
|
||||
|
||||
// Handle format with only # (e.g., "namespace#ErrorCode")
|
||||
if (str_contains($rawErrorCode, '#')) {
|
||||
return substr($rawErrorCode, strpos($rawErrorCode, '#') + 1);
|
||||
}
|
||||
|
||||
return $rawErrorCode;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Aws\Api\ErrorParser;
|
||||
|
||||
use Aws\Api\Parser\AbstractParser;
|
||||
use Aws\Api\Parser\PayloadParserTrait;
|
||||
use Aws\Api\StructureShape;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
@ -38,9 +39,10 @@ trait JsonParserTrait
|
||||
}
|
||||
|
||||
$parsedBody = null;
|
||||
$body = $response->getBody();
|
||||
if (!$body->isSeekable() || $body->getSize()) {
|
||||
$parsedBody = $this->parseJson((string) $body, $response);
|
||||
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
if (!empty($rawBody)) {
|
||||
$parsedBody = $this->parseJson($rawBody, $response);
|
||||
}
|
||||
|
||||
// Parse error code from response body
|
||||
@ -132,11 +134,12 @@ trait JsonParserTrait
|
||||
ResponseInterface $response,
|
||||
StructureShape $member
|
||||
) {
|
||||
$body = $response->getBody();
|
||||
if (!$body->isSeekable() || $body->getSize()) {
|
||||
$jsonBody = $this->parseJson($body, $response);
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
|
||||
if (!empty($rawBody)) {
|
||||
$jsonBody = $this->parseJson($rawBody, $response);
|
||||
} else {
|
||||
$jsonBody = (string) $body;
|
||||
$jsonBody = $rawBody;
|
||||
}
|
||||
|
||||
return $this->parser->parse($member, $jsonBody);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Aws\Api\ErrorParser;
|
||||
|
||||
use Aws\Api\Parser\AbstractParser;
|
||||
use Aws\Api\Parser\JsonParser;
|
||||
use Aws\Api\Service;
|
||||
use Aws\CommandInterface;
|
||||
@ -25,6 +26,7 @@ class JsonRpcErrorParser extends AbstractErrorParser
|
||||
ResponseInterface $response,
|
||||
?CommandInterface $command = null
|
||||
) {
|
||||
$response = AbstractParser::getResponseWithCachingStream($response);
|
||||
$data = $this->genericHandler($response);
|
||||
|
||||
// Make the casing consistent across services.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Aws\Api\ErrorParser;
|
||||
|
||||
use Aws\Api\Parser\AbstractParser;
|
||||
use Aws\Api\Parser\JsonParser;
|
||||
use Aws\Api\Service;
|
||||
use Aws\Api\StructureShape;
|
||||
@ -26,6 +27,7 @@ class RestJsonErrorParser extends AbstractErrorParser
|
||||
ResponseInterface $response,
|
||||
?CommandInterface $command = null
|
||||
) {
|
||||
$response = AbstractParser::getResponseWithCachingStream($response);
|
||||
$data = $this->genericHandler($response);
|
||||
|
||||
// Merge in error data from the JSON body
|
||||
@ -40,7 +42,9 @@ class RestJsonErrorParser extends AbstractErrorParser
|
||||
|
||||
// Retrieve error message directly
|
||||
$data['message'] = $data['parsed']['message']
|
||||
?? ($data['parsed']['Message'] ?? null);
|
||||
?? $data['parsed']['Message']
|
||||
?? $data['parsed']['error_description']
|
||||
?? null;
|
||||
|
||||
$this->populateShape($data, $response, $command);
|
||||
|
||||
|
||||
65
vendor/aws/aws-sdk-php/src/Api/ErrorParser/RpcV2CborErrorParser.php
vendored
Executable file
65
vendor/aws/aws-sdk-php/src/Api/ErrorParser/RpcV2CborErrorParser.php
vendored
Executable file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Aws\Api\ErrorParser;
|
||||
|
||||
use Aws\Api\Cbor\CborDecoder;
|
||||
use Aws\Api\Parser\RpcV2ParserTrait;
|
||||
use Aws\Api\Service;
|
||||
use Aws\Api\StructureShape;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Parses errors according to Smithy RPC V2 CBOR protocol standards.
|
||||
*
|
||||
* https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class RpcV2CborErrorParser extends AbstractRpcV2ErrorParser
|
||||
{
|
||||
/** @var CborDecoder */
|
||||
private CborDecoder $decoder;
|
||||
|
||||
use RpcV2ParserTrait;
|
||||
|
||||
/**
|
||||
* @param Service|null $api
|
||||
*/
|
||||
public function __construct(?Service $api = null)
|
||||
{
|
||||
$this->decoder = new CborDecoder();
|
||||
parent::__construct($api);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
* @param StructureShape $member
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function payload(
|
||||
ResponseInterface $response,
|
||||
StructureShape $member
|
||||
): array
|
||||
{
|
||||
$body = $response->getBody();
|
||||
$cborBody = $this->parseCbor($body, $response);
|
||||
|
||||
return $this->resolveOutputShape($member, $cborBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StreamInterface $body
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function parseBody(
|
||||
StreamInterface $body,
|
||||
ResponseInterface $response
|
||||
): mixed
|
||||
{
|
||||
return $this->parseCbor($body, $response);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Aws\Api\ErrorParser;
|
||||
|
||||
use Aws\Api\Parser\AbstractParser;
|
||||
use Aws\Api\Parser\PayloadParserTrait;
|
||||
use Aws\Api\Parser\XmlParser;
|
||||
use Aws\Api\Service;
|
||||
@ -27,6 +28,7 @@ class XmlErrorParser extends AbstractErrorParser
|
||||
ResponseInterface $response,
|
||||
?CommandInterface $command = null
|
||||
) {
|
||||
$response = AbstractParser::getResponseWithCachingStream($response);
|
||||
$code = (string) $response->getStatusCode();
|
||||
|
||||
$data = [
|
||||
@ -37,9 +39,9 @@ class XmlErrorParser extends AbstractErrorParser
|
||||
'parsed' => null
|
||||
];
|
||||
|
||||
$body = $response->getBody();
|
||||
if ($body->getSize() > 0) {
|
||||
$this->parseBody($this->parseXml($body, $response), $data);
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
if (!empty($rawBody)) {
|
||||
$this->parseBody($this->parseXml($rawBody, $response), $data);
|
||||
} else {
|
||||
$this->parseHeaders($response, $data);
|
||||
}
|
||||
@ -100,12 +102,20 @@ class XmlErrorParser extends AbstractErrorParser
|
||||
ResponseInterface $response,
|
||||
StructureShape $member
|
||||
) {
|
||||
$xmlBody = $this->parseXml($response->getBody(), $response);
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
|
||||
if (empty($rawBody)) {
|
||||
return $rawBody;
|
||||
}
|
||||
|
||||
$xmlBody = $this->parseXml($rawBody, $response);
|
||||
$prefix = $this->registerNamespacePrefix($xmlBody);
|
||||
$errorBody = $xmlBody->xpath("//{$prefix}Error");
|
||||
|
||||
if (is_array($errorBody) && !empty($errorBody[0])) {
|
||||
return $this->parser->parse($member, $errorBody[0]);
|
||||
}
|
||||
|
||||
return $rawBody;
|
||||
}
|
||||
}
|
||||
|
||||
11
vendor/aws/aws-sdk-php/src/Api/Exception/RpcV2CborException.php
vendored
Executable file
11
vendor/aws/aws-sdk-php/src/Api/Exception/RpcV2CborException.php
vendored
Executable file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace Aws\Api\Exception;
|
||||
|
||||
use Aws\HasMonitoringEventsTrait;
|
||||
use Aws\MonitoringEventsInterface;
|
||||
|
||||
class RpcV2CborException extends \RuntimeException implements
|
||||
MonitoringEventsInterface
|
||||
{
|
||||
use HasMonitoringEventsTrait;
|
||||
}
|
||||
2
vendor/aws/aws-sdk-php/src/Api/Operation.php
vendored
2
vendor/aws/aws-sdk-php/src/Api/Operation.php
vendored
@ -89,7 +89,7 @@ class Operation extends AbstractModel
|
||||
/**
|
||||
* Get an array of operation error shapes.
|
||||
*
|
||||
* @return Shape[]
|
||||
* @return StructureShape[]
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
|
||||
@ -5,6 +5,7 @@ use Aws\Api\Service;
|
||||
use Aws\Api\StructureShape;
|
||||
use Aws\CommandInterface;
|
||||
use Aws\ResultInterface;
|
||||
use GuzzleHttp\Psr7\CachingStream;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
@ -43,4 +44,27 @@ abstract class AbstractParser
|
||||
StructureShape $member,
|
||||
$response
|
||||
);
|
||||
|
||||
public static function getBodyContents(ResponseInterface $response): string
|
||||
{
|
||||
$body = $response->getBody();
|
||||
if ($body->isSeekable()) {
|
||||
$body->rewind();
|
||||
}
|
||||
|
||||
return $body->getContents();
|
||||
}
|
||||
|
||||
public static function getResponseWithCachingStream(
|
||||
ResponseInterface $response
|
||||
): ResponseInterface
|
||||
{
|
||||
if (!$response->getBody()->isSeekable()) {
|
||||
return $response->withBody(
|
||||
new CachingStream($response->getBody())
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +39,21 @@ abstract class AbstractRestParser extends AbstractParser
|
||||
|
||||
if ($payload = $output['payload']) {
|
||||
$this->extractPayload($payload, $output, $response, $result);
|
||||
} else {
|
||||
$response = AbstractParser::getResponseWithCachingStream($response);
|
||||
|
||||
if ($response->getBody()->getSize() === null) {
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
$isEmpty = empty($rawBody);
|
||||
} else {
|
||||
$isEmpty = $response->getBody()->getSize() === 0;
|
||||
}
|
||||
|
||||
if (!$isEmpty && count($output->getMembers()) > 0
|
||||
) {
|
||||
// if no payload was found, then parse the contents of the body
|
||||
$this->payload($response, $output, $result);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($output->getMembers() as $name => $member) {
|
||||
@ -55,15 +70,6 @@ abstract class AbstractRestParser extends AbstractParser
|
||||
}
|
||||
}
|
||||
|
||||
$body = $response->getBody();
|
||||
if (!$payload
|
||||
&& (!$body->isSeekable() || $body->getSize())
|
||||
&& count($output->getMembers()) > 0
|
||||
) {
|
||||
// if no payload was found, then parse the contents of the body
|
||||
$this->payload($response, $output, $result);
|
||||
}
|
||||
|
||||
return new Result($result);
|
||||
}
|
||||
|
||||
@ -75,17 +81,29 @@ abstract class AbstractRestParser extends AbstractParser
|
||||
) {
|
||||
$member = $output->getMember($payload);
|
||||
$body = $response->getBody();
|
||||
|
||||
if (!empty($member['eventstream'])) {
|
||||
$result[$payload] = new EventParsingIterator(
|
||||
$body,
|
||||
$member,
|
||||
$this
|
||||
);
|
||||
} elseif ($member instanceof StructureShape) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$response = AbstractParser::getResponseWithCachingStream($response);
|
||||
|
||||
if ($member instanceof StructureShape) {
|
||||
//Unions must have at least one member set to a non-null value
|
||||
// If the body is empty, we can assume it is unset
|
||||
if (!empty($member['union']) && ($body->isSeekable() && !$body->getSize())) {
|
||||
if ($response->getBody()->getSize() === null) {
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
$isEmpty = empty($rawBody);
|
||||
} else {
|
||||
$isEmpty = $response->getBody()->getSize() === 0;
|
||||
}
|
||||
|
||||
if (!empty($member['union']) && $isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
83
vendor/aws/aws-sdk-php/src/Api/Parser/AbstractRpcV2Parser.php
vendored
Executable file
83
vendor/aws/aws-sdk-php/src/Api/Parser/AbstractRpcV2Parser.php
vendored
Executable file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace Aws\Api\Parser;
|
||||
|
||||
use Aws\Api\Operation;
|
||||
use Aws\Api\Parser\Exception\ParserException;
|
||||
use Aws\Result;
|
||||
use Aws\CommandInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Base implementation for Smithy RPC V2 protocol parsers.
|
||||
*
|
||||
* Implementers MUST define the following static property representing
|
||||
* the `Smithy-Protocol` header value:
|
||||
* self::HEADER_SMITHY_PROTOCOL => static::$smithyProtocol
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractRpcV2Parser extends AbstractParser
|
||||
{
|
||||
private const HEADER_SMITHY_PROTOCOL = 'Smithy-Protocol';
|
||||
|
||||
/** @var string */
|
||||
protected static string $smithyProtocol;
|
||||
|
||||
public function __invoke(
|
||||
CommandInterface $command,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$operation = $this->api->getOperation($command->getName());
|
||||
|
||||
return $this->parseResponse($response, $operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a response according to Smithy RPC V2 protocol standards.
|
||||
*
|
||||
* @param ResponseInterface $response the response to parse.
|
||||
* @param Operation $operation the operation which holds information for
|
||||
* parsing the response.
|
||||
*
|
||||
* @return Result
|
||||
*/
|
||||
private function parseResponse(
|
||||
ResponseInterface $response,
|
||||
Operation $operation
|
||||
): Result
|
||||
{
|
||||
$smithyProtocolHeader = $response->getHeaderLine(self::HEADER_SMITHY_PROTOCOL);
|
||||
if ($smithyProtocolHeader !== static::$smithyProtocol) {
|
||||
$statusCode = $response->getStatusCode();
|
||||
throw new ParserException(
|
||||
"Malformed response: Smithy-Protocol header mismatch (HTTP {$statusCode}). "
|
||||
. 'Expected ' . static::$smithyProtocol
|
||||
);
|
||||
}
|
||||
|
||||
if ($operation['output'] === null) {
|
||||
return new Result([]);
|
||||
}
|
||||
|
||||
$outputShape = $operation->getOutput();
|
||||
foreach ($outputShape->getMembers() as $memberName => $memberProps) {
|
||||
if (!empty($memberProps['eventstream'])) {
|
||||
return new Result([
|
||||
$memberName => new EventParsingIterator(
|
||||
$response->getBody(),
|
||||
$outputShape->getMember($memberName),
|
||||
$this
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->parseMemberFromStream(
|
||||
$response->getBody(),
|
||||
$outputShape,
|
||||
$response
|
||||
);
|
||||
|
||||
return new Result(is_null($result) ? [] : $result);
|
||||
}
|
||||
}
|
||||
@ -63,11 +63,16 @@ class JsonRpcParser extends AbstractParser
|
||||
}
|
||||
}
|
||||
|
||||
$body = $response->getBody();
|
||||
if ($body->isSeekable()) {
|
||||
$body->rewind();
|
||||
}
|
||||
|
||||
$result = $this->parseMemberFromStream(
|
||||
$response->getBody(),
|
||||
$operation->getOutput(),
|
||||
$response
|
||||
);
|
||||
$body,
|
||||
$operation->getOutput(),
|
||||
$response
|
||||
);
|
||||
|
||||
return new Result(is_null($result) ? [] : $result);
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
namespace Aws\Api\Parser;
|
||||
|
||||
use Aws\Api\Parser\Exception\ParserException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
trait PayloadParserTrait
|
||||
{
|
||||
|
||||
@ -40,9 +40,11 @@ class QueryParser extends AbstractParser
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$output = $this->api->getOperation($command->getName())->getOutput();
|
||||
$body = $response->getBody();
|
||||
$xml = !$body->isSeekable() || $body->getSize()
|
||||
? $this->parseXml($body, $response)
|
||||
// Read the full payload, even in non-seekable streams
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
// Just parse when the body is not empty
|
||||
$xml = !empty($rawBody)
|
||||
? $this->parseXml($rawBody, $response)
|
||||
: null;
|
||||
|
||||
// Empty request bodies should not be deserialized.
|
||||
|
||||
@ -28,15 +28,14 @@ class RestJsonParser extends AbstractRestParser
|
||||
StructureShape $member,
|
||||
array &$result
|
||||
) {
|
||||
$responseBody = (string) $response->getBody();
|
||||
$rawBody = AbstractParser::getBodyContents($response);
|
||||
|
||||
// Parse JSON if we have content
|
||||
$parsedJson = null;
|
||||
if (!empty($responseBody)) {
|
||||
$parsedJson = $this->parseJson($responseBody, $response);
|
||||
if (!empty($rawBody)) {
|
||||
$parsedJson = $this->parseJson($rawBody, $response);
|
||||
} else {
|
||||
// An empty response body should be deserialized as null
|
||||
$result = $parsedJson;
|
||||
$result = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,12 @@ class RestXmlParser extends AbstractRestParser
|
||||
StructureShape $member,
|
||||
array &$result
|
||||
) {
|
||||
$result += $this->parseMemberFromStream($response->getBody(), $member, $response);
|
||||
$body = $response->getBody();
|
||||
if ($body->isSeekable()) {
|
||||
$body->rewind();
|
||||
}
|
||||
|
||||
$result += $this->parseMemberFromStream($body, $member, $response);
|
||||
}
|
||||
|
||||
public function parseMemberFromStream(
|
||||
|
||||
50
vendor/aws/aws-sdk-php/src/Api/Parser/RpcV2CborParser.php
vendored
Executable file
50
vendor/aws/aws-sdk-php/src/Api/Parser/RpcV2CborParser.php
vendored
Executable file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace Aws\Api\Parser;
|
||||
|
||||
use Aws\Api\Cbor\CborDecoder;
|
||||
use Aws\Api\Service;
|
||||
use Aws\Api\StructureShape;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Parses responses according to Smithy RPC V2 CBOR protocol standards.
|
||||
*
|
||||
* https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class RpcV2CborParser extends AbstractRpcV2Parser
|
||||
{
|
||||
/** @var string */
|
||||
protected static string $smithyProtocol = 'rpc-v2-cbor';
|
||||
|
||||
/** @var CborDecoder */
|
||||
private CborDecoder $decoder;
|
||||
|
||||
use RpcV2ParserTrait;
|
||||
|
||||
/**
|
||||
* @param Service $api Service description
|
||||
*/
|
||||
public function __construct(Service $api)
|
||||
{
|
||||
$this->decoder = new CborDecoder();
|
||||
parent::__construct($api);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StreamInterface $stream
|
||||
* @param StructureShape $member
|
||||
* @param $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function parseMemberFromStream(
|
||||
StreamInterface $stream,
|
||||
StructureShape $member,
|
||||
$response
|
||||
): mixed
|
||||
{
|
||||
return $this->resolveOutputShape($member, $this->parseCbor($stream, $response));
|
||||
}
|
||||
}
|
||||
105
vendor/aws/aws-sdk-php/src/Api/Parser/RpcV2ParserTrait.php
vendored
Executable file
105
vendor/aws/aws-sdk-php/src/Api/Parser/RpcV2ParserTrait.php
vendored
Executable file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
namespace Aws\Api\Parser;
|
||||
|
||||
use Aws\Api\Cbor\Exception\CborException;
|
||||
use Aws\Api\DateTimeResult;
|
||||
use Aws\Api\Parser\Exception\ParserException;
|
||||
use Aws\Api\Shape;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Shared parsing logic for RPC V2 Parsers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait RpcV2ParserTrait
|
||||
{
|
||||
/**
|
||||
* Resolves output shape fields that are present in the response
|
||||
*
|
||||
* @param Shape $shape
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function resolveOutputShape(Shape $shape, mixed $value): mixed
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
switch ($shape['type']) {
|
||||
case 'structure':
|
||||
$target = [];
|
||||
foreach ($shape->getMembers() as $name => $member) {
|
||||
$locationName = $member['locationName'] ?: $name;
|
||||
if (isset($value[$locationName])) {
|
||||
$target[$name] = $this->resolveOutputShape($member, $value[$locationName]);
|
||||
}
|
||||
}
|
||||
return $target;
|
||||
|
||||
case 'list':
|
||||
$target = [];
|
||||
foreach ($value as $v) {
|
||||
$target[] = $this->resolveOutputShape($shape->getMember(), $v);
|
||||
}
|
||||
return $target;
|
||||
|
||||
case 'map':
|
||||
$target = [];
|
||||
foreach ($value as $k => $v) {
|
||||
if ($v !== null) {
|
||||
$target[$k] = $this->resolveOutputShape($shape->getValue(), $v);
|
||||
}
|
||||
}
|
||||
return $target;
|
||||
|
||||
case 'timestamp':
|
||||
try {
|
||||
$value = DateTimeResult::fromEpoch($value);
|
||||
} catch (\Exception $e) {
|
||||
trigger_error(
|
||||
'Unable to parse timestamp value for '
|
||||
. $shape->getName()
|
||||
. ': ' . $e->getMessage(),
|
||||
E_USER_WARNING
|
||||
);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses CBOR-encoded response data from RPC V2 CBOR services.
|
||||
*
|
||||
* @param StreamInterface $stream
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function parseCbor(
|
||||
StreamInterface $stream,
|
||||
ResponseInterface $response
|
||||
): mixed
|
||||
{
|
||||
try {
|
||||
$cborString = (string) $stream;
|
||||
return empty($cborString)
|
||||
? null
|
||||
: $this->decoder->decode($cborString);
|
||||
} catch (CborException $e) {
|
||||
throw new ParserException(
|
||||
"Malformed Response: error parsing CBOR: {$e->getMessage()}",
|
||||
0,
|
||||
$e,
|
||||
['response' => $response]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
220
vendor/aws/aws-sdk-php/src/Api/Serializer/AbstractRpcV2Serializer.php
vendored
Executable file
220
vendor/aws/aws-sdk-php/src/Api/Serializer/AbstractRpcV2Serializer.php
vendored
Executable file
@ -0,0 +1,220 @@
|
||||
<?php
|
||||
namespace Aws\Api\Serializer;
|
||||
|
||||
use Aws\Api\Service;
|
||||
|
||||
use Aws\Api\Shape;
|
||||
use Aws\Api\StructureShape;
|
||||
use Aws\CommandInterface;
|
||||
use Aws\EndpointV2\EndpointV2SerializerTrait;
|
||||
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
|
||||
use DateTimeInterface;
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Base implementation for Smithy RPC V2 protocol serializers.
|
||||
*
|
||||
* Implementers MUST override the defaultHeader property to represent
|
||||
* protocol-specific default header values:
|
||||
* self::HEADER_SMITHY_PROTOCOL => static::SMITHY_PROTOCOL,
|
||||
* self::HEADER_CONTENT_TYPE => static::DEFAULT_CONTENT_TYPE,
|
||||
* self::HEADER_ACCEPT => static::DEFAULT_ACCEPT
|
||||
*
|
||||
* Implementers must also implement `serialize()`, `resolveBlob()`, and `resolveTimestamp()
|
||||
* according to their respective protocol specifications.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractRpcV2Serializer
|
||||
{
|
||||
protected const HEADER_SMITHY_PROTOCOL = 'Smithy-Protocol';
|
||||
protected const HEADER_CONTENT_TYPE = 'Content-Type';
|
||||
protected const HEADER_ACCEPT = 'Accept';
|
||||
|
||||
/** @var array */
|
||||
protected static array $defaultHeaders;
|
||||
|
||||
/** @var Service */
|
||||
private Service $api;
|
||||
|
||||
/** @var string|Uri */
|
||||
private string|Uri $endpoint;
|
||||
|
||||
/** @var bool */
|
||||
private bool $isUseEndpointV2;
|
||||
|
||||
use EndpointV2SerializerTrait;
|
||||
|
||||
/**
|
||||
* @param Service $api Service API description
|
||||
* @param string $endpoint Endpoint to connect to
|
||||
*/
|
||||
public function __construct(Service $api, string|Uri $endpoint)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->endpoint = Psr7\Utils::uriFor($endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommandInterface $command Command to serialize into a request.
|
||||
* @param mixed|null $endpoint
|
||||
*
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public function __invoke(
|
||||
CommandInterface $command,
|
||||
mixed $endpoint = null
|
||||
)
|
||||
{
|
||||
$commandArgs = $command->toArray();
|
||||
$commandName = $command->getName();
|
||||
$operation = $this->api->getOperation($commandName);
|
||||
$headers = static::$defaultHeaders;
|
||||
|
||||
// Operations with no defined input type must not contain bodies
|
||||
// Content-Type must not be set
|
||||
if ($operation['input'] !== null) {
|
||||
$body = $this->serialize($operation->getInput(), $commandArgs);
|
||||
$headers['Content-Length'] = (string) strlen($body);
|
||||
} else {
|
||||
unset($headers['Content-Type']);
|
||||
}
|
||||
|
||||
if ($endpoint instanceof RulesetEndpoint) {
|
||||
$this->isUseEndpointV2 = true;
|
||||
$this->setEndpointV2RequestOptions($endpoint, $headers);
|
||||
$this->endpoint = $endpoint->getUrl();
|
||||
}
|
||||
|
||||
$requestTarget = $this->buildRequestTarget(
|
||||
$commandName,
|
||||
$operation['http']['requestUri'] ?? ''
|
||||
);
|
||||
$uri = new Uri($this->endpoint . $requestTarget);
|
||||
|
||||
return new Request(
|
||||
$operation['http']['method'],
|
||||
$uri,
|
||||
$headers,
|
||||
$body ?? null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StructureShape $inputShape
|
||||
* @param array $commandArgs
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function serialize(
|
||||
StructureShape $inputShape,
|
||||
array $commandArgs
|
||||
): string;
|
||||
|
||||
/**
|
||||
* Resolves arguments for blob shapes present in the request arguments
|
||||
* into a protocol-specific format.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function resolveBlob(mixed $value): array;
|
||||
|
||||
/**
|
||||
* Resolves arguments for timestamp shapes present in the request arguments
|
||||
* into a protocol-specific format.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function resolveTimestamp(
|
||||
int|float|string|DateTimeInterface $value
|
||||
): array;
|
||||
|
||||
/**
|
||||
* Resolves input shape fields that are present in the request arguments
|
||||
*
|
||||
* @param Shape $shape
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function resolveInputShape(Shape $shape, mixed $value): mixed
|
||||
{
|
||||
switch ($shape->getType()) {
|
||||
case 'structure':
|
||||
$data = [];
|
||||
foreach ($value as $k => $v) {
|
||||
if ($v !== null && $shape->hasMember($k)) {
|
||||
$valueShape = $shape->getMember($k);
|
||||
$data[$valueShape['locationName'] ?: $k]
|
||||
= $this->resolveInputShape($valueShape, $v);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
case 'list':
|
||||
$items = $shape->getMember();
|
||||
foreach ($value as $k => $v) {
|
||||
$value[$k] = $this->resolveInputShape($items, $v);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
case 'map':
|
||||
$values = $shape->getValue();
|
||||
foreach ($value as $k => $v) {
|
||||
$value[$k] = $this->resolveInputShape($values, $v);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
case 'timestamp':
|
||||
return $this->resolveTimestamp($value);
|
||||
|
||||
case 'string':
|
||||
return (string) $value;
|
||||
|
||||
case 'integer':
|
||||
case 'long':
|
||||
return (int) $value;
|
||||
|
||||
case 'double':
|
||||
case 'float':
|
||||
return (float) $value;
|
||||
|
||||
case 'blob':
|
||||
return $this->resolveBlob($value);
|
||||
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds request URI absolute path
|
||||
*
|
||||
* @param string $commandName
|
||||
* @param string $requestUri
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function buildRequestTarget(
|
||||
string $commandName,
|
||||
string $requestUri
|
||||
): string
|
||||
{
|
||||
$requestUri = str_ends_with($requestUri, '/')
|
||||
? $requestUri
|
||||
: $requestUri . '/';
|
||||
$targetPrefix = $this->api->getMetadata('targetPrefix');
|
||||
|
||||
return "{$requestUri}service/{$targetPrefix}/operation/{$commandName}";
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@ class JsonRpcSerializer
|
||||
$headers = [
|
||||
'X-Amz-Target' => $this->api->getMetadata('targetPrefix') . '.' . $operationName,
|
||||
'Content-Type' => $this->contentType,
|
||||
'Content-Length' => strlen($body)
|
||||
'Content-Length' => (string) strlen($body)
|
||||
];
|
||||
|
||||
if ($endpoint instanceof RulesetEndpoint) {
|
||||
|
||||
@ -61,7 +61,7 @@ class QuerySerializer
|
||||
}
|
||||
$body = http_build_query($body, '', '&', PHP_QUERY_RFC3986);
|
||||
$headers = [
|
||||
'Content-Length' => strlen($body),
|
||||
'Content-Length' => (string) strlen($body),
|
||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||
];
|
||||
$requestUri = $operation['http']['requestUri'] ?? null;
|
||||
|
||||
@ -35,7 +35,7 @@ class RestJsonSerializer extends RestSerializer
|
||||
{
|
||||
$opts['headers']['Content-Type'] = $this->contentType;
|
||||
$body = $this->jsonFormatter->build($member, $value);
|
||||
$opts['headers']['Content-Length'] = strlen($body);
|
||||
$opts['headers']['Content-Length'] = (string) strlen($body);
|
||||
$opts['body'] = $body;
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ abstract class RestSerializer
|
||||
|
||||
$body = $args[$name];
|
||||
if (!$m['streaming'] && is_string($body)) {
|
||||
$opts['headers']['Content-Length'] = strlen($body);
|
||||
$opts['headers']['Content-Length'] = (string) strlen($body);
|
||||
}
|
||||
|
||||
// Streaming bodies or payloads that are strings are
|
||||
@ -173,20 +173,36 @@ abstract class RestSerializer
|
||||
|
||||
private function applyHeader($name, Shape $member, $value, array &$opts)
|
||||
{
|
||||
// Handle lists by recursively applying header logic to each element
|
||||
if ($value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle lists by applying header logic to each element
|
||||
if ($member instanceof ListShape) {
|
||||
if (!is_array($value)) {
|
||||
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
|
||||
}
|
||||
|
||||
$listMember = $member->getMember();
|
||||
$headerValues = [];
|
||||
|
||||
foreach ($value as $listValue) {
|
||||
if ($listValue === null) {
|
||||
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
|
||||
}
|
||||
|
||||
$tempOpts = ['headers' => []];
|
||||
$this->applyHeader('temp', $listMember, $listValue, $tempOpts);
|
||||
if (!array_key_exists('temp', $tempOpts['headers'])) {
|
||||
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
|
||||
}
|
||||
|
||||
$convertedValue = $tempOpts['headers']['temp'];
|
||||
$headerValues[] = $convertedValue;
|
||||
}
|
||||
|
||||
$value = $headerValues;
|
||||
} elseif (!is_null($value)) {
|
||||
} else {
|
||||
switch ($member->getType()) {
|
||||
case 'timestamp':
|
||||
$timestampFormat = $member['timestampFormat'] ?? 'rfc822';
|
||||
@ -208,7 +224,7 @@ abstract class RestSerializer
|
||||
$value = base64_encode($value);
|
||||
}
|
||||
|
||||
$opts['headers'][$member['locationName'] ?: $name] = $value;
|
||||
$opts['headers'][$member['locationName'] ?: $name] = self::prepareHeaderValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,10 +234,42 @@ abstract class RestSerializer
|
||||
{
|
||||
$prefix = $member['locationName'];
|
||||
foreach ($value as $k => $v) {
|
||||
$opts['headers'][$prefix . $k] = $v;
|
||||
if ($v === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$opts['headers'][$prefix . $k] = self::prepareHeaderValue($v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|string[]
|
||||
*/
|
||||
private static function prepareHeaderValue($value)
|
||||
{
|
||||
if (is_scalar($value)) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
if ($value === []) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($value as $key => $item) {
|
||||
if (!is_scalar($item)) {
|
||||
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
|
||||
}
|
||||
|
||||
$value[$key] = (string) $item;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
|
||||
}
|
||||
|
||||
private function applyQuery($name, Shape $member, $value, array &$opts)
|
||||
{
|
||||
if ($member instanceof MapShape) {
|
||||
|
||||
@ -30,7 +30,7 @@ class RestXmlSerializer extends RestSerializer
|
||||
{
|
||||
$opts['headers']['Content-Type'] = 'application/xml';
|
||||
$body = $this->getXmlBody($member, $value);
|
||||
$opts['headers']['Content-Length'] = strlen($body);
|
||||
$opts['headers']['Content-Length'] = (string) strlen($body);
|
||||
$opts['body'] = $body;
|
||||
}
|
||||
|
||||
|
||||
124
vendor/aws/aws-sdk-php/src/Api/Serializer/RpcV2CborSerializer.php
vendored
Executable file
124
vendor/aws/aws-sdk-php/src/Api/Serializer/RpcV2CborSerializer.php
vendored
Executable file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
namespace Aws\Api\Serializer;
|
||||
|
||||
use Aws\Api\Cbor\CborEncoder;
|
||||
use Aws\Api\Cbor\Exception\CborException;
|
||||
use Aws\Api\Exception\RpcV2CborException;
|
||||
use Aws\Api\Service;
|
||||
use Aws\Api\StructureShape;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Serializes requests according to Smithy RPC-V2 CBOR protocol standards.
|
||||
*
|
||||
* https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class RpcV2CborSerializer extends AbstractRpcV2Serializer
|
||||
{
|
||||
/** @var array|string[] */
|
||||
protected static array $defaultHeaders = [
|
||||
self::HEADER_SMITHY_PROTOCOL => 'rpc-v2-cbor',
|
||||
self::HEADER_CONTENT_TYPE => 'application/cbor',
|
||||
self::HEADER_ACCEPT => 'application/cbor',
|
||||
];
|
||||
|
||||
/** @var CborEncoder */
|
||||
private CborEncoder $encoder;
|
||||
|
||||
/**
|
||||
* @param Service $api Service API description
|
||||
* @param string $endpoint Endpoint to connect to
|
||||
*/
|
||||
public function __construct(Service $api, string $endpoint)
|
||||
{
|
||||
$this->encoder = new CborEncoder();
|
||||
parent::__construct($api, $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StructureShape $inputShape
|
||||
* @param array $commandArgs
|
||||
*
|
||||
* @return string
|
||||
* @throws RpcV2CborException
|
||||
*/
|
||||
public function serialize(
|
||||
StructureShape $inputShape,
|
||||
array $commandArgs
|
||||
): string
|
||||
{
|
||||
try {
|
||||
$resolvedInput = $this->resolveInputShape($inputShape, $commandArgs);
|
||||
return !empty($resolvedInput)
|
||||
? $this->encoder->encode($resolvedInput)
|
||||
: $this->encoder->encodeEmptyIndefiniteMap();
|
||||
} catch (CborException $e) {
|
||||
throw new RpcV2CborException(
|
||||
'Unable to encode CBOR document ' . $inputShape->getName() . ': ' .
|
||||
$e->getMessage() . PHP_EOL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps blob values in order to be encoded properly into
|
||||
* byte strings.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string[]
|
||||
* @throws RpcV2CborException
|
||||
*/
|
||||
protected function resolveBlob(mixed $value): array
|
||||
{
|
||||
if (is_resource($value)) {
|
||||
$value = stream_get_contents($value);
|
||||
if ($value === false) {
|
||||
throw new RpcV2CborException(
|
||||
'Failed to read resource stream value during serialization',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper to differentiate byte string values during encoding
|
||||
return ['__cbor_bytes' => (string) $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps timestamp values in order to be encoded properly into
|
||||
* value tag 1.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string[]
|
||||
* @throws RpcV2CborException
|
||||
*/
|
||||
protected function resolveTimestamp(
|
||||
int|float|string|DateTimeInterface $value
|
||||
): array
|
||||
{
|
||||
if (is_numeric($value)) {
|
||||
return ['__cbor_timestamp' => $value];
|
||||
}
|
||||
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
// Preserve milliseconds
|
||||
$micro = (int) $value->format('u');
|
||||
$value = $value->getTimestamp() + $micro / 1e6;
|
||||
} else {
|
||||
$timestamp = strtotime($value);
|
||||
if ($timestamp === false) {
|
||||
throw new RpcV2CborException(
|
||||
'Request serialization failed: Invalid date/time: ' . $value,
|
||||
);
|
||||
}
|
||||
|
||||
$value = $timestamp;
|
||||
}
|
||||
|
||||
// Wrapper to differentiate timestamp values during encoding
|
||||
return ['__cbor_timestamp' => $value];
|
||||
}
|
||||
}
|
||||
9
vendor/aws/aws-sdk-php/src/Api/Service.php
vendored
9
vendor/aws/aws-sdk-php/src/Api/Service.php
vendored
@ -91,7 +91,8 @@ class Service extends AbstractModel
|
||||
'json' => Serializer\JsonRpcSerializer::class,
|
||||
'query' => Serializer\QuerySerializer::class,
|
||||
'rest-json' => Serializer\RestJsonSerializer::class,
|
||||
'rest-xml' => Serializer\RestXmlSerializer::class
|
||||
'rest-xml' => Serializer\RestXmlSerializer::class,
|
||||
'smithy-rpc-v2-cbor' => Serializer\RpcV2CborSerializer::class
|
||||
];
|
||||
|
||||
$proto = $api->getProtocol();
|
||||
@ -126,7 +127,8 @@ class Service extends AbstractModel
|
||||
'query' => ErrorParser\XmlErrorParser::class,
|
||||
'rest-json' => ErrorParser\RestJsonErrorParser::class,
|
||||
'rest-xml' => ErrorParser\XmlErrorParser::class,
|
||||
'ec2' => ErrorParser\XmlErrorParser::class
|
||||
'ec2' => ErrorParser\XmlErrorParser::class,
|
||||
'smithy-rpc-v2-cbor' => ErrorParser\RpcV2CborErrorParser::class
|
||||
];
|
||||
|
||||
if (isset($mapping[$protocol])) {
|
||||
@ -149,7 +151,8 @@ class Service extends AbstractModel
|
||||
'json' => Parser\JsonRpcParser::class,
|
||||
'query' => Parser\QueryParser::class,
|
||||
'rest-json' => Parser\RestJsonParser::class,
|
||||
'rest-xml' => Parser\RestXmlParser::class
|
||||
'rest-xml' => Parser\RestXmlParser::class,
|
||||
'smithy-rpc-v2-cbor' => Parser\RpcV2CborParser::class
|
||||
];
|
||||
|
||||
$proto = $api->getProtocol();
|
||||
|
||||
@ -8,6 +8,7 @@ namespace Aws\Api;
|
||||
enum SupportedProtocols: string
|
||||
{
|
||||
case JSON = 'json';
|
||||
case CBOR = 'smithy-rpc-v2-cbor';
|
||||
case REST_JSON = 'rest-json';
|
||||
case REST_XML = 'rest-xml';
|
||||
case QUERY = 'query';
|
||||
|
||||
@ -28,16 +28,16 @@ class TimestampShape extends Shape
|
||||
$value = $value->getTimestamp();
|
||||
} elseif (is_string($value)) {
|
||||
$value = strtotime($value);
|
||||
} elseif (!is_int($value)) {
|
||||
} elseif (!is_int($value) && !is_float($value)) {
|
||||
throw new \InvalidArgumentException('Unable to handle the provided'
|
||||
. ' timestamp type: ' . gettype($value));
|
||||
}
|
||||
|
||||
switch ($format) {
|
||||
case 'iso8601':
|
||||
return gmdate('Y-m-d\TH:i:s\Z', $value);
|
||||
return gmdate('Y-m-d\TH:i:s\Z', (int) $value);
|
||||
case 'rfc822':
|
||||
return gmdate('D, d M Y H:i:s \G\M\T', $value);
|
||||
return gmdate('D, d M Y H:i:s \G\M\T', (int) $value);
|
||||
case 'unixTimestamp':
|
||||
return $value;
|
||||
default:
|
||||
|
||||
@ -131,6 +131,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise disassociateFleetAsync(array $args = [])
|
||||
* @method \Aws\Result disassociateSoftwareFromImageBuilder(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise disassociateSoftwareFromImageBuilderAsync(array $args = [])
|
||||
* @method \Aws\Result drainSessionInstance(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise drainSessionInstanceAsync(array $args = [])
|
||||
* @method \Aws\Result enableUser(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise enableUserAsync(array $args = [])
|
||||
* @method \Aws\Result expireSession(array $args = [])
|
||||
|
||||
54
vendor/aws/aws-sdk-php/src/AwsClient.php
vendored
54
vendor/aws/aws-sdk-php/src/AwsClient.php
vendored
@ -283,6 +283,7 @@ class AwsClient implements AwsClientInterface
|
||||
$args['with_resolved']($config);
|
||||
}
|
||||
$this->addUserAgentMiddleware($config);
|
||||
$this->addEventStreamHttpFlagMiddleware();
|
||||
}
|
||||
|
||||
public function getHandlerList()
|
||||
@ -543,7 +544,7 @@ class AwsClient implements AwsClientInterface
|
||||
{
|
||||
$list = $this->getHandlerList();
|
||||
$list->appendBuild(
|
||||
Middleware::mapRequest(function (RequestInterface $r) {
|
||||
Middleware::mapRequest(static function (RequestInterface $r) {
|
||||
return $r->withHeader(
|
||||
'x-amzn-query-mode',
|
||||
"true"
|
||||
@ -649,6 +650,34 @@ class AwsClient implements AwsClientInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables streaming the response by using the stream flag.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addEventStreamHttpFlagMiddleware(): void
|
||||
{
|
||||
$api = $this->getApi();
|
||||
$this->getHandlerList()
|
||||
-> appendInit(
|
||||
static function (callable $handler) use ($api) {
|
||||
return static function (CommandInterface $command, $request = null) use ($handler, $api) {
|
||||
$operation = $api->getOperation($command->getName());
|
||||
$output = $operation->getOutput();
|
||||
foreach ($output->getMembers() as $memberProps) {
|
||||
if (!empty($memberProps['eventstream'])) {
|
||||
$command['@http']['stream'] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $handler($command, $request);
|
||||
};
|
||||
},
|
||||
'event-streaming-flag-middleware'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves client context param definition from service model,
|
||||
* creates mapping of client context param names with client-provided
|
||||
@ -737,29 +766,6 @@ class AwsClient implements AwsClientInterface
|
||||
return $this->endpointProvider instanceof EndpointProviderV2;
|
||||
}
|
||||
|
||||
public static function emitDeprecationWarning() {
|
||||
trigger_error(
|
||||
"This method is deprecated. It will be removed in an upcoming release."
|
||||
, E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
$phpVersion = PHP_VERSION_ID;
|
||||
if ($phpVersion < 70205) {
|
||||
$phpVersionString = phpversion();
|
||||
@trigger_error(
|
||||
"This installation of the SDK is using PHP version"
|
||||
. " {$phpVersionString}, which will be deprecated on August"
|
||||
. " 15th, 2023. Please upgrade your PHP version to a minimum of"
|
||||
. " 7.2.5 before then to continue receiving updates to the AWS"
|
||||
. " SDK for PHP. To disable this warning, set"
|
||||
. " suppress_php_deprecation_warning to true on the client constructor"
|
||||
. " or set the environment variable AWS_SUPPRESS_PHP_DEPRECATION_WARNING"
|
||||
. " to true.",
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a service model and doc model with any necessary changes
|
||||
|
||||
@ -75,7 +75,7 @@ trait AwsClientTrait
|
||||
$name = $this->aliases[ucfirst($name)];
|
||||
}
|
||||
|
||||
$params = isset($args[0]) ? $args[0] : [];
|
||||
$params = $args['args'] ?? $args[0] ?? [];
|
||||
|
||||
if (!empty($isAsync)) {
|
||||
return $this->executeAsync(
|
||||
|
||||
@ -7,14 +7,24 @@ use Aws\AwsClient;
|
||||
* This client is used to interact with the **AWS Billing and Cost Management Dashboards** service.
|
||||
* @method \Aws\Result createDashboard(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createDashboardAsync(array $args = [])
|
||||
* @method \Aws\Result createScheduledReport(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createScheduledReportAsync(array $args = [])
|
||||
* @method \Aws\Result deleteDashboard(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteDashboardAsync(array $args = [])
|
||||
* @method \Aws\Result deleteScheduledReport(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteScheduledReportAsync(array $args = [])
|
||||
* @method \Aws\Result executeScheduledReport(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise executeScheduledReportAsync(array $args = [])
|
||||
* @method \Aws\Result getDashboard(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getDashboardAsync(array $args = [])
|
||||
* @method \Aws\Result getResourcePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getResourcePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result getScheduledReport(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getScheduledReportAsync(array $args = [])
|
||||
* @method \Aws\Result listDashboards(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDashboardsAsync(array $args = [])
|
||||
* @method \Aws\Result listScheduledReports(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listScheduledReportsAsync(array $args = [])
|
||||
* @method \Aws\Result listTagsForResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
|
||||
* @method \Aws\Result tagResource(array $args = [])
|
||||
@ -23,5 +33,7 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
|
||||
* @method \Aws\Result updateDashboard(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateDashboardAsync(array $args = [])
|
||||
* @method \Aws\Result updateScheduledReport(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateScheduledReportAsync(array $args = [])
|
||||
*/
|
||||
class BCMDashboardsClient extends AwsClient {}
|
||||
|
||||
@ -101,6 +101,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise getBackupVaultNotificationsAsync(array $args = [])
|
||||
* @method \Aws\Result getLegalHold(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getLegalHoldAsync(array $args = [])
|
||||
* @method \Aws\Result getPITRMalwareScanResults(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPITRMalwareScanResultsAsync(array $args = [])
|
||||
* @method \Aws\Result getRecoveryPointIndexDetails(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getRecoveryPointIndexDetailsAsync(array $args = [])
|
||||
* @method \Aws\Result getRecoveryPointRestoreMetadata(array $args = [])
|
||||
|
||||
12
vendor/aws/aws-sdk-php/src/Batch/BatchClient.php
vendored
12
vendor/aws/aws-sdk-php/src/Batch/BatchClient.php
vendored
@ -13,6 +13,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise createConsumableResourceAsync(array $args = [])
|
||||
* @method \Aws\Result createJobQueue(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createJobQueueAsync(array $args = [])
|
||||
* @method \Aws\Result createQuotaShare(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createQuotaShareAsync(array $args = [])
|
||||
* @method \Aws\Result createSchedulingPolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createSchedulingPolicyAsync(array $args = [])
|
||||
* @method \Aws\Result createServiceEnvironment(array $args = [])
|
||||
@ -23,6 +25,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise deleteConsumableResourceAsync(array $args = [])
|
||||
* @method \Aws\Result deleteJobQueue(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteJobQueueAsync(array $args = [])
|
||||
* @method \Aws\Result deleteQuotaShare(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteQuotaShareAsync(array $args = [])
|
||||
* @method \Aws\Result deleteSchedulingPolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteSchedulingPolicyAsync(array $args = [])
|
||||
* @method \Aws\Result deleteServiceEnvironment(array $args = [])
|
||||
@ -39,6 +43,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise describeJobQueuesAsync(array $args = [])
|
||||
* @method \Aws\Result describeJobs(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise describeJobsAsync(array $args = [])
|
||||
* @method \Aws\Result describeQuotaShare(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise describeQuotaShareAsync(array $args = [])
|
||||
* @method \Aws\Result describeSchedulingPolicies(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise describeSchedulingPoliciesAsync(array $args = [])
|
||||
* @method \Aws\Result describeServiceEnvironments(array $args = [])
|
||||
@ -53,6 +59,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise listJobsAsync(array $args = [])
|
||||
* @method \Aws\Result listJobsByConsumableResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listJobsByConsumableResourceAsync(array $args = [])
|
||||
* @method \Aws\Result listQuotaShares(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listQuotaSharesAsync(array $args = [])
|
||||
* @method \Aws\Result listSchedulingPolicies(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listSchedulingPoliciesAsync(array $args = [])
|
||||
* @method \Aws\Result listServiceJobs(array $args = [])
|
||||
@ -79,9 +87,13 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise updateConsumableResourceAsync(array $args = [])
|
||||
* @method \Aws\Result updateJobQueue(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateJobQueueAsync(array $args = [])
|
||||
* @method \Aws\Result updateQuotaShare(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateQuotaShareAsync(array $args = [])
|
||||
* @method \Aws\Result updateSchedulingPolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateSchedulingPolicyAsync(array $args = [])
|
||||
* @method \Aws\Result updateServiceEnvironment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateServiceEnvironmentAsync(array $args = [])
|
||||
* @method \Aws\Result updateServiceJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateServiceJobAsync(array $args = [])
|
||||
*/
|
||||
class BatchClient extends AwsClient {}
|
||||
|
||||
@ -5,10 +5,14 @@ use Aws\AwsClient;
|
||||
|
||||
/**
|
||||
* This client is used to interact with the **Amazon Bedrock** service.
|
||||
* @method \Aws\Result batchDeleteAdvancedPromptOptimizationJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise batchDeleteAdvancedPromptOptimizationJobAsync(array $args = [])
|
||||
* @method \Aws\Result batchDeleteEvaluationJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise batchDeleteEvaluationJobAsync(array $args = [])
|
||||
* @method \Aws\Result cancelAutomatedReasoningPolicyBuildWorkflow(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise cancelAutomatedReasoningPolicyBuildWorkflowAsync(array $args = [])
|
||||
* @method \Aws\Result createAdvancedPromptOptimizationJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createAdvancedPromptOptimizationJobAsync(array $args = [])
|
||||
* @method \Aws\Result createAutomatedReasoningPolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createAutomatedReasoningPolicyAsync(array $args = [])
|
||||
* @method \Aws\Result createAutomatedReasoningPolicyTestCase(array $args = [])
|
||||
@ -71,10 +75,14 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise deletePromptRouterAsync(array $args = [])
|
||||
* @method \Aws\Result deleteProvisionedModelThroughput(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteProvisionedModelThroughputAsync(array $args = [])
|
||||
* @method \Aws\Result deleteResourcePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteResourcePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result deregisterMarketplaceModelEndpoint(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deregisterMarketplaceModelEndpointAsync(array $args = [])
|
||||
* @method \Aws\Result exportAutomatedReasoningPolicyVersion(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise exportAutomatedReasoningPolicyVersionAsync(array $args = [])
|
||||
* @method \Aws\Result getAdvancedPromptOptimizationJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getAdvancedPromptOptimizationJobAsync(array $args = [])
|
||||
* @method \Aws\Result getAutomatedReasoningPolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getAutomatedReasoningPolicyAsync(array $args = [])
|
||||
* @method \Aws\Result getAutomatedReasoningPolicyAnnotations(array $args = [])
|
||||
@ -121,8 +129,12 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise getPromptRouterAsync(array $args = [])
|
||||
* @method \Aws\Result getProvisionedModelThroughput(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getProvisionedModelThroughputAsync(array $args = [])
|
||||
* @method \Aws\Result getResourcePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getResourcePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result getUseCaseForModelAccess(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getUseCaseForModelAccessAsync(array $args = [])
|
||||
* @method \Aws\Result listAdvancedPromptOptimizationJobs(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listAdvancedPromptOptimizationJobsAsync(array $args = [])
|
||||
* @method \Aws\Result listAutomatedReasoningPolicies(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listAutomatedReasoningPoliciesAsync(array $args = [])
|
||||
* @method \Aws\Result listAutomatedReasoningPolicyBuildWorkflows(array $args = [])
|
||||
@ -169,6 +181,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise putEnforcedGuardrailConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result putModelInvocationLoggingConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise putModelInvocationLoggingConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result putResourcePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise putResourcePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result putUseCaseForModelAccess(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise putUseCaseForModelAccessAsync(array $args = [])
|
||||
* @method \Aws\Result registerMarketplaceModelEndpoint(array $args = [])
|
||||
@ -177,6 +191,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise startAutomatedReasoningPolicyBuildWorkflowAsync(array $args = [])
|
||||
* @method \Aws\Result startAutomatedReasoningPolicyTestWorkflow(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startAutomatedReasoningPolicyTestWorkflowAsync(array $args = [])
|
||||
* @method \Aws\Result stopAdvancedPromptOptimizationJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopAdvancedPromptOptimizationJobAsync(array $args = [])
|
||||
* @method \Aws\Result stopEvaluationJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopEvaluationJobAsync(array $args = [])
|
||||
* @method \Aws\Result stopModelCustomizationJob(array $args = [])
|
||||
|
||||
@ -13,16 +13,36 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise batchUpdateMemoryRecordsAsync(array $args = [])
|
||||
* @method \Aws\Result completeResourceTokenAuth(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise completeResourceTokenAuthAsync(array $args = [])
|
||||
* @method \Aws\Result createABTest(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createABTestAsync(array $args = [])
|
||||
* @method \Aws\Result createEvent(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createEventAsync(array $args = [])
|
||||
* @method \Aws\Result createPaymentInstrument(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createPaymentInstrumentAsync(array $args = [])
|
||||
* @method \Aws\Result createPaymentSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createPaymentSessionAsync(array $args = [])
|
||||
* @method \Aws\Result deleteABTest(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteABTestAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBatchEvaluation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBatchEvaluationAsync(array $args = [])
|
||||
* @method \Aws\Result deleteEvent(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteEventAsync(array $args = [])
|
||||
* @method \Aws\Result deleteMemoryRecord(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteMemoryRecordAsync(array $args = [])
|
||||
* @method \Aws\Result deletePaymentInstrument(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deletePaymentInstrumentAsync(array $args = [])
|
||||
* @method \Aws\Result deletePaymentSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deletePaymentSessionAsync(array $args = [])
|
||||
* @method \Aws\Result deleteRecommendation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteRecommendationAsync(array $args = [])
|
||||
* @method \Aws\Result evaluate(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise evaluateAsync(array $args = [])
|
||||
* @method \Aws\Result getABTest(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getABTestAsync(array $args = [])
|
||||
* @method \Aws\Result getAgentCard(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getAgentCardAsync(array $args = [])
|
||||
* @method \Aws\Result getBatchEvaluation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBatchEvaluationAsync(array $args = [])
|
||||
* @method \Aws\Result getBrowserSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBrowserSessionAsync(array $args = [])
|
||||
* @method \Aws\Result getCodeInterpreterSession(array $args = [])
|
||||
@ -31,10 +51,20 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise getEventAsync(array $args = [])
|
||||
* @method \Aws\Result getMemoryRecord(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getMemoryRecordAsync(array $args = [])
|
||||
* @method \Aws\Result getPaymentInstrument(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPaymentInstrumentAsync(array $args = [])
|
||||
* @method \Aws\Result getPaymentInstrumentBalance(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPaymentInstrumentBalanceAsync(array $args = [])
|
||||
* @method \Aws\Result getPaymentSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPaymentSessionAsync(array $args = [])
|
||||
* @method \Aws\Result getRecommendation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getRecommendationAsync(array $args = [])
|
||||
* @method \Aws\Result getResourceApiKey(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getResourceApiKeyAsync(array $args = [])
|
||||
* @method \Aws\Result getResourceOauth2Token(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getResourceOauth2TokenAsync(array $args = [])
|
||||
* @method \Aws\Result getResourcePaymentToken(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getResourcePaymentTokenAsync(array $args = [])
|
||||
* @method \Aws\Result getWorkloadAccessToken(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getWorkloadAccessTokenAsync(array $args = [])
|
||||
* @method \Aws\Result getWorkloadAccessTokenForJWT(array $args = [])
|
||||
@ -43,10 +73,20 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise getWorkloadAccessTokenForUserIdAsync(array $args = [])
|
||||
* @method \Aws\Result invokeAgentRuntime(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise invokeAgentRuntimeAsync(array $args = [])
|
||||
* @method \Aws\Result invokeAgentRuntimeCommand(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise invokeAgentRuntimeCommandAsync(array $args = [])
|
||||
* @method \Aws\Result invokeBrowser(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise invokeBrowserAsync(array $args = [])
|
||||
* @method \Aws\Result invokeCodeInterpreter(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise invokeCodeInterpreterAsync(array $args = [])
|
||||
* @method \Aws\Result invokeHarness(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise invokeHarnessAsync(array $args = [])
|
||||
* @method \Aws\Result listABTests(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listABTestsAsync(array $args = [])
|
||||
* @method \Aws\Result listActors(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listActorsAsync(array $args = [])
|
||||
* @method \Aws\Result listBatchEvaluations(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listBatchEvaluationsAsync(array $args = [])
|
||||
* @method \Aws\Result listBrowserSessions(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listBrowserSessionsAsync(array $args = [])
|
||||
* @method \Aws\Result listCodeInterpreterSessions(array $args = [])
|
||||
@ -57,22 +97,42 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise listMemoryExtractionJobsAsync(array $args = [])
|
||||
* @method \Aws\Result listMemoryRecords(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listMemoryRecordsAsync(array $args = [])
|
||||
* @method \Aws\Result listPaymentInstruments(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPaymentInstrumentsAsync(array $args = [])
|
||||
* @method \Aws\Result listPaymentSessions(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPaymentSessionsAsync(array $args = [])
|
||||
* @method \Aws\Result listRecommendations(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listRecommendationsAsync(array $args = [])
|
||||
* @method \Aws\Result listSessions(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listSessionsAsync(array $args = [])
|
||||
* @method \Aws\Result processPayment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise processPaymentAsync(array $args = [])
|
||||
* @method \Aws\Result retrieveMemoryRecords(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise retrieveMemoryRecordsAsync(array $args = [])
|
||||
* @method \Aws\Result saveBrowserSessionProfile(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise saveBrowserSessionProfileAsync(array $args = [])
|
||||
* @method \Aws\Result searchRegistryRecords(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise searchRegistryRecordsAsync(array $args = [])
|
||||
* @method \Aws\Result startBatchEvaluation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startBatchEvaluationAsync(array $args = [])
|
||||
* @method \Aws\Result startBrowserSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startBrowserSessionAsync(array $args = [])
|
||||
* @method \Aws\Result startCodeInterpreterSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startCodeInterpreterSessionAsync(array $args = [])
|
||||
* @method \Aws\Result startMemoryExtractionJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startMemoryExtractionJobAsync(array $args = [])
|
||||
* @method \Aws\Result startRecommendation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startRecommendationAsync(array $args = [])
|
||||
* @method \Aws\Result stopBatchEvaluation(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopBatchEvaluationAsync(array $args = [])
|
||||
* @method \Aws\Result stopBrowserSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopBrowserSessionAsync(array $args = [])
|
||||
* @method \Aws\Result stopCodeInterpreterSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopCodeInterpreterSessionAsync(array $args = [])
|
||||
* @method \Aws\Result stopRuntimeSession(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopRuntimeSessionAsync(array $args = [])
|
||||
* @method \Aws\Result updateABTest(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateABTestAsync(array $args = [])
|
||||
* @method \Aws\Result updateBrowserStream(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateBrowserStreamAsync(array $args = [])
|
||||
*/
|
||||
|
||||
@ -5,6 +5,8 @@ use Aws\AwsClient;
|
||||
|
||||
/**
|
||||
* This client is used to interact with the **Amazon Bedrock Agent Core Control Plane Fronting Layer** service.
|
||||
* @method \Aws\Result addDatasetExamples(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise addDatasetExamplesAsync(array $args = [])
|
||||
* @method \Aws\Result createAgentRuntime(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createAgentRuntimeAsync(array $args = [])
|
||||
* @method \Aws\Result createAgentRuntimeEndpoint(array $args = [])
|
||||
@ -13,24 +15,46 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise createApiKeyCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result createBrowser(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBrowserAsync(array $args = [])
|
||||
* @method \Aws\Result createBrowserProfile(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBrowserProfileAsync(array $args = [])
|
||||
* @method \Aws\Result createCodeInterpreter(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createCodeInterpreterAsync(array $args = [])
|
||||
* @method \Aws\Result createConfigurationBundle(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createConfigurationBundleAsync(array $args = [])
|
||||
* @method \Aws\Result createDataset(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createDatasetAsync(array $args = [])
|
||||
* @method \Aws\Result createDatasetVersion(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createDatasetVersionAsync(array $args = [])
|
||||
* @method \Aws\Result createEvaluator(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createEvaluatorAsync(array $args = [])
|
||||
* @method \Aws\Result createGateway(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createGatewayAsync(array $args = [])
|
||||
* @method \Aws\Result createGatewayRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createGatewayRuleAsync(array $args = [])
|
||||
* @method \Aws\Result createGatewayTarget(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createGatewayTargetAsync(array $args = [])
|
||||
* @method \Aws\Result createHarness(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createHarnessAsync(array $args = [])
|
||||
* @method \Aws\Result createMemory(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createMemoryAsync(array $args = [])
|
||||
* @method \Aws\Result createOauth2CredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createOauth2CredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result createOnlineEvaluationConfig(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createOnlineEvaluationConfigAsync(array $args = [])
|
||||
* @method \Aws\Result createPaymentConnector(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createPaymentConnectorAsync(array $args = [])
|
||||
* @method \Aws\Result createPaymentCredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createPaymentCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result createPaymentManager(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createPaymentManagerAsync(array $args = [])
|
||||
* @method \Aws\Result createPolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createPolicyAsync(array $args = [])
|
||||
* @method \Aws\Result createPolicyEngine(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createPolicyEngineAsync(array $args = [])
|
||||
* @method \Aws\Result createRegistry(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createRegistryAsync(array $args = [])
|
||||
* @method \Aws\Result createRegistryRecord(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createRegistryRecordAsync(array $args = [])
|
||||
* @method \Aws\Result createWorkloadIdentity(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createWorkloadIdentityAsync(array $args = [])
|
||||
* @method \Aws\Result deleteAgentRuntime(array $args = [])
|
||||
@ -41,24 +65,46 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise deleteApiKeyCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBrowser(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBrowserAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBrowserProfile(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBrowserProfileAsync(array $args = [])
|
||||
* @method \Aws\Result deleteCodeInterpreter(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteCodeInterpreterAsync(array $args = [])
|
||||
* @method \Aws\Result deleteConfigurationBundle(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteConfigurationBundleAsync(array $args = [])
|
||||
* @method \Aws\Result deleteDataset(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteDatasetAsync(array $args = [])
|
||||
* @method \Aws\Result deleteDatasetExamples(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteDatasetExamplesAsync(array $args = [])
|
||||
* @method \Aws\Result deleteEvaluator(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteEvaluatorAsync(array $args = [])
|
||||
* @method \Aws\Result deleteGateway(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteGatewayAsync(array $args = [])
|
||||
* @method \Aws\Result deleteGatewayRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteGatewayRuleAsync(array $args = [])
|
||||
* @method \Aws\Result deleteGatewayTarget(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteGatewayTargetAsync(array $args = [])
|
||||
* @method \Aws\Result deleteHarness(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteHarnessAsync(array $args = [])
|
||||
* @method \Aws\Result deleteMemory(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteMemoryAsync(array $args = [])
|
||||
* @method \Aws\Result deleteOauth2CredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteOauth2CredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result deleteOnlineEvaluationConfig(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteOnlineEvaluationConfigAsync(array $args = [])
|
||||
* @method \Aws\Result deletePaymentConnector(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deletePaymentConnectorAsync(array $args = [])
|
||||
* @method \Aws\Result deletePaymentCredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deletePaymentCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result deletePaymentManager(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deletePaymentManagerAsync(array $args = [])
|
||||
* @method \Aws\Result deletePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deletePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result deletePolicyEngine(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deletePolicyEngineAsync(array $args = [])
|
||||
* @method \Aws\Result deleteRegistry(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteRegistryAsync(array $args = [])
|
||||
* @method \Aws\Result deleteRegistryRecord(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteRegistryRecordAsync(array $args = [])
|
||||
* @method \Aws\Result deleteResourcePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteResourcePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result deleteWorkloadIdentity(array $args = [])
|
||||
@ -71,26 +117,54 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise getApiKeyCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result getBrowser(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBrowserAsync(array $args = [])
|
||||
* @method \Aws\Result getBrowserProfile(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBrowserProfileAsync(array $args = [])
|
||||
* @method \Aws\Result getCodeInterpreter(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getCodeInterpreterAsync(array $args = [])
|
||||
* @method \Aws\Result getConfigurationBundle(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getConfigurationBundleAsync(array $args = [])
|
||||
* @method \Aws\Result getConfigurationBundleVersion(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getConfigurationBundleVersionAsync(array $args = [])
|
||||
* @method \Aws\Result getDataset(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getDatasetAsync(array $args = [])
|
||||
* @method \Aws\Result getEvaluator(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getEvaluatorAsync(array $args = [])
|
||||
* @method \Aws\Result getGateway(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getGatewayAsync(array $args = [])
|
||||
* @method \Aws\Result getGatewayRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getGatewayRuleAsync(array $args = [])
|
||||
* @method \Aws\Result getGatewayTarget(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getGatewayTargetAsync(array $args = [])
|
||||
* @method \Aws\Result getHarness(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getHarnessAsync(array $args = [])
|
||||
* @method \Aws\Result getMemory(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getMemoryAsync(array $args = [])
|
||||
* @method \Aws\Result getOauth2CredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getOauth2CredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result getOnlineEvaluationConfig(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getOnlineEvaluationConfigAsync(array $args = [])
|
||||
* @method \Aws\Result getPaymentConnector(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPaymentConnectorAsync(array $args = [])
|
||||
* @method \Aws\Result getPaymentCredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPaymentCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result getPaymentManager(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPaymentManagerAsync(array $args = [])
|
||||
* @method \Aws\Result getPolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPolicyAsync(array $args = [])
|
||||
* @method \Aws\Result getPolicyEngine(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPolicyEngineAsync(array $args = [])
|
||||
* @method \Aws\Result getPolicyEngineSummary(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPolicyEngineSummaryAsync(array $args = [])
|
||||
* @method \Aws\Result getPolicyGeneration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPolicyGenerationAsync(array $args = [])
|
||||
* @method \Aws\Result getPolicyGenerationSummary(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPolicyGenerationSummaryAsync(array $args = [])
|
||||
* @method \Aws\Result getPolicySummary(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getPolicySummaryAsync(array $args = [])
|
||||
* @method \Aws\Result getRegistry(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getRegistryAsync(array $args = [])
|
||||
* @method \Aws\Result getRegistryRecord(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getRegistryRecordAsync(array $args = [])
|
||||
* @method \Aws\Result getResourcePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getResourcePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result getTokenVault(array $args = [])
|
||||
@ -105,30 +179,62 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise listAgentRuntimesAsync(array $args = [])
|
||||
* @method \Aws\Result listApiKeyCredentialProviders(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listApiKeyCredentialProvidersAsync(array $args = [])
|
||||
* @method \Aws\Result listBrowserProfiles(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listBrowserProfilesAsync(array $args = [])
|
||||
* @method \Aws\Result listBrowsers(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listBrowsersAsync(array $args = [])
|
||||
* @method \Aws\Result listCodeInterpreters(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listCodeInterpretersAsync(array $args = [])
|
||||
* @method \Aws\Result listConfigurationBundleVersions(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listConfigurationBundleVersionsAsync(array $args = [])
|
||||
* @method \Aws\Result listConfigurationBundles(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listConfigurationBundlesAsync(array $args = [])
|
||||
* @method \Aws\Result listDatasetExamples(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDatasetExamplesAsync(array $args = [])
|
||||
* @method \Aws\Result listDatasetVersions(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDatasetVersionsAsync(array $args = [])
|
||||
* @method \Aws\Result listDatasets(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDatasetsAsync(array $args = [])
|
||||
* @method \Aws\Result listEvaluators(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listEvaluatorsAsync(array $args = [])
|
||||
* @method \Aws\Result listGatewayRules(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listGatewayRulesAsync(array $args = [])
|
||||
* @method \Aws\Result listGatewayTargets(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listGatewayTargetsAsync(array $args = [])
|
||||
* @method \Aws\Result listGateways(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listGatewaysAsync(array $args = [])
|
||||
* @method \Aws\Result listHarnesses(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listHarnessesAsync(array $args = [])
|
||||
* @method \Aws\Result listMemories(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listMemoriesAsync(array $args = [])
|
||||
* @method \Aws\Result listOauth2CredentialProviders(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listOauth2CredentialProvidersAsync(array $args = [])
|
||||
* @method \Aws\Result listOnlineEvaluationConfigs(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listOnlineEvaluationConfigsAsync(array $args = [])
|
||||
* @method \Aws\Result listPaymentConnectors(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPaymentConnectorsAsync(array $args = [])
|
||||
* @method \Aws\Result listPaymentCredentialProviders(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPaymentCredentialProvidersAsync(array $args = [])
|
||||
* @method \Aws\Result listPaymentManagers(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPaymentManagersAsync(array $args = [])
|
||||
* @method \Aws\Result listPolicies(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPoliciesAsync(array $args = [])
|
||||
* @method \Aws\Result listPolicyEngineSummaries(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPolicyEngineSummariesAsync(array $args = [])
|
||||
* @method \Aws\Result listPolicyEngines(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPolicyEnginesAsync(array $args = [])
|
||||
* @method \Aws\Result listPolicyGenerationAssets(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPolicyGenerationAssetsAsync(array $args = [])
|
||||
* @method \Aws\Result listPolicyGenerationSummaries(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPolicyGenerationSummariesAsync(array $args = [])
|
||||
* @method \Aws\Result listPolicyGenerations(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPolicyGenerationsAsync(array $args = [])
|
||||
* @method \Aws\Result listPolicySummaries(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listPolicySummariesAsync(array $args = [])
|
||||
* @method \Aws\Result listRegistries(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listRegistriesAsync(array $args = [])
|
||||
* @method \Aws\Result listRegistryRecords(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listRegistryRecordsAsync(array $args = [])
|
||||
* @method \Aws\Result listTagsForResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
|
||||
* @method \Aws\Result listWorkloadIdentities(array $args = [])
|
||||
@ -139,6 +245,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise setTokenVaultCMKAsync(array $args = [])
|
||||
* @method \Aws\Result startPolicyGeneration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startPolicyGenerationAsync(array $args = [])
|
||||
* @method \Aws\Result submitRegistryRecordForApproval(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise submitRegistryRecordForApprovalAsync(array $args = [])
|
||||
* @method \Aws\Result synchronizeGatewayTargets(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise synchronizeGatewayTargetsAsync(array $args = [])
|
||||
* @method \Aws\Result tagResource(array $args = [])
|
||||
@ -151,22 +259,44 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise updateAgentRuntimeEndpointAsync(array $args = [])
|
||||
* @method \Aws\Result updateApiKeyCredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateApiKeyCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result updateConfigurationBundle(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateConfigurationBundleAsync(array $args = [])
|
||||
* @method \Aws\Result updateDataset(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateDatasetAsync(array $args = [])
|
||||
* @method \Aws\Result updateDatasetExamples(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateDatasetExamplesAsync(array $args = [])
|
||||
* @method \Aws\Result updateEvaluator(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateEvaluatorAsync(array $args = [])
|
||||
* @method \Aws\Result updateGateway(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateGatewayAsync(array $args = [])
|
||||
* @method \Aws\Result updateGatewayRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateGatewayRuleAsync(array $args = [])
|
||||
* @method \Aws\Result updateGatewayTarget(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateGatewayTargetAsync(array $args = [])
|
||||
* @method \Aws\Result updateHarness(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateHarnessAsync(array $args = [])
|
||||
* @method \Aws\Result updateMemory(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateMemoryAsync(array $args = [])
|
||||
* @method \Aws\Result updateOauth2CredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateOauth2CredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result updateOnlineEvaluationConfig(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateOnlineEvaluationConfigAsync(array $args = [])
|
||||
* @method \Aws\Result updatePaymentConnector(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updatePaymentConnectorAsync(array $args = [])
|
||||
* @method \Aws\Result updatePaymentCredentialProvider(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updatePaymentCredentialProviderAsync(array $args = [])
|
||||
* @method \Aws\Result updatePaymentManager(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updatePaymentManagerAsync(array $args = [])
|
||||
* @method \Aws\Result updatePolicy(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updatePolicyAsync(array $args = [])
|
||||
* @method \Aws\Result updatePolicyEngine(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updatePolicyEngineAsync(array $args = [])
|
||||
* @method \Aws\Result updateRegistry(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateRegistryAsync(array $args = [])
|
||||
* @method \Aws\Result updateRegistryRecord(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateRegistryRecordAsync(array $args = [])
|
||||
* @method \Aws\Result updateRegistryRecordStatus(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateRegistryRecordStatusAsync(array $args = [])
|
||||
* @method \Aws\Result updateWorkloadIdentity(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateWorkloadIdentityAsync(array $args = [])
|
||||
*/
|
||||
|
||||
@ -11,22 +11,40 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise createBlueprintAsync(array $args = [])
|
||||
* @method \Aws\Result createBlueprintVersion(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBlueprintVersionAsync(array $args = [])
|
||||
* @method \Aws\Result createDataAutomationLibrary(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createDataAutomationLibraryAsync(array $args = [])
|
||||
* @method \Aws\Result createDataAutomationProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createDataAutomationProjectAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBlueprint(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBlueprintAsync(array $args = [])
|
||||
* @method \Aws\Result deleteDataAutomationLibrary(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteDataAutomationLibraryAsync(array $args = [])
|
||||
* @method \Aws\Result deleteDataAutomationProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteDataAutomationProjectAsync(array $args = [])
|
||||
* @method \Aws\Result getBlueprint(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBlueprintAsync(array $args = [])
|
||||
* @method \Aws\Result getBlueprintOptimizationStatus(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBlueprintOptimizationStatusAsync(array $args = [])
|
||||
* @method \Aws\Result getDataAutomationLibrary(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getDataAutomationLibraryAsync(array $args = [])
|
||||
* @method \Aws\Result getDataAutomationLibraryEntity(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getDataAutomationLibraryEntityAsync(array $args = [])
|
||||
* @method \Aws\Result getDataAutomationLibraryIngestionJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getDataAutomationLibraryIngestionJobAsync(array $args = [])
|
||||
* @method \Aws\Result getDataAutomationProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getDataAutomationProjectAsync(array $args = [])
|
||||
* @method \Aws\Result invokeBlueprintOptimizationAsync(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise invokeBlueprintOptimizationAsyncAsync(array $args = [])
|
||||
* @method \Aws\Result invokeDataAutomationLibraryIngestionJob(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise invokeDataAutomationLibraryIngestionJobAsync(array $args = [])
|
||||
* @method \Aws\Result listBlueprints(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listBlueprintsAsync(array $args = [])
|
||||
* @method \Aws\Result listDataAutomationLibraries(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDataAutomationLibrariesAsync(array $args = [])
|
||||
* @method \Aws\Result listDataAutomationLibraryEntities(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDataAutomationLibraryEntitiesAsync(array $args = [])
|
||||
* @method \Aws\Result listDataAutomationLibraryIngestionJobs(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDataAutomationLibraryIngestionJobsAsync(array $args = [])
|
||||
* @method \Aws\Result listDataAutomationProjects(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDataAutomationProjectsAsync(array $args = [])
|
||||
* @method \Aws\Result listTagsForResource(array $args = [])
|
||||
@ -37,6 +55,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
|
||||
* @method \Aws\Result updateBlueprint(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateBlueprintAsync(array $args = [])
|
||||
* @method \Aws\Result updateDataAutomationLibrary(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateDataAutomationLibraryAsync(array $args = [])
|
||||
* @method \Aws\Result updateDataAutomationProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateDataAutomationProjectAsync(array $args = [])
|
||||
*/
|
||||
|
||||
664
vendor/aws/aws-sdk-php/src/Cbor/CborDecoder.php
vendored
Executable file
664
vendor/aws/aws-sdk-php/src/Cbor/CborDecoder.php
vendored
Executable file
@ -0,0 +1,664 @@
|
||||
<?php
|
||||
namespace Aws\Cbor;
|
||||
|
||||
use Aws\Cbor\Exception\CborException;
|
||||
|
||||
/**
|
||||
* Decodes Concise Binary Object Representation encoded strings
|
||||
* into PHP values according to RFC 8949
|
||||
*
|
||||
* https://www.rfc-editor.org/rfc/rfc8949.html
|
||||
*
|
||||
* Supports Major types 0-7 including:
|
||||
* - Type 0: Unsigned integers
|
||||
* - Type 1: Negative integers
|
||||
* - Type 2: Byte strings
|
||||
* - Type 3: Text strings (UTF-8)
|
||||
* - Type 4: Arrays
|
||||
* - Type 5: Maps
|
||||
* - Type 6: Tagged values (timestamps)
|
||||
* - Type 7: Simple values (null, bool, float)
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class CborDecoder
|
||||
{
|
||||
private int $offset;
|
||||
private int $length;
|
||||
|
||||
/**
|
||||
* Decode CBOR binary data to PHP value
|
||||
*
|
||||
* @param string $data The CBOR-encoded binary data to decode
|
||||
*
|
||||
* @return mixed The decoded PHP value (can be any type: int, string, array, bool, null, float)
|
||||
* @throws CborException If data is empty or malformed CBOR
|
||||
*/
|
||||
public function decode(string $data): mixed
|
||||
{
|
||||
if ($data === '') {
|
||||
throw new CborException("No data to decode");
|
||||
}
|
||||
|
||||
$this->offset = 0;
|
||||
$this->length = strlen($data);
|
||||
|
||||
return $this->decodeValue($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode multiple CBOR values from sequential binary data
|
||||
*
|
||||
* @param string $data The CBOR-encoded binary data containing multiple values
|
||||
*
|
||||
* @return array Array of decoded PHP values in the order they appear in the data
|
||||
* @throws CborException If data is malformed CBOR
|
||||
*/
|
||||
public function decodeAll(string $data): array
|
||||
{
|
||||
$this->length = strlen($data);
|
||||
$this->offset = 0;
|
||||
$values = [];
|
||||
|
||||
while ($this->offset < $this->length) {
|
||||
$values[] = $this->decodeValue($data);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a single CBOR value at the current offset
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
*
|
||||
* @return mixed The decoded value
|
||||
* @throws CborException If unexpected end of data or invalid CBOR format
|
||||
*/
|
||||
private function decodeValue(string &$data): mixed
|
||||
{
|
||||
$offset = $this->offset;
|
||||
$length = $this->length;
|
||||
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
$byte = ord($data[$offset++]);
|
||||
$majorType = $byte >> 5;
|
||||
$info = $byte & 0x1F;
|
||||
|
||||
switch ($majorType) {
|
||||
case 0: // Unsigned integer
|
||||
if ($info < 24) {
|
||||
$this->offset = $offset;
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 1;
|
||||
|
||||
return ord($data[$offset]);
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 2;
|
||||
|
||||
return (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 4;
|
||||
|
||||
return unpack('N', $data, $offset)[1];
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 8;
|
||||
|
||||
return unpack('J', $data, $offset)[1];
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for integer: $info");
|
||||
}
|
||||
|
||||
case 1: // Negative integer
|
||||
if ($info < 24) {
|
||||
$this->offset = $offset;
|
||||
|
||||
return -1 - $info;
|
||||
}
|
||||
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 1;
|
||||
|
||||
return -1 - ord($data[$offset]);
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 2;
|
||||
|
||||
return -1 - ((ord($data[$offset]) << 8) | ord($data[$offset + 1]));
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 4;
|
||||
|
||||
return -1 - unpack('N', $data, $offset)[1];
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 8;
|
||||
$unsigned = unpack('J', $data, $offset)[1];
|
||||
|
||||
return ($unsigned === 9223372036854775807) ? PHP_INT_MIN : -1 - $unsigned;
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for integer: $info");
|
||||
}
|
||||
|
||||
case 2: // Byte string
|
||||
if ($info < 24) {
|
||||
$len = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteString($data, 0x40);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for byte string: $info");
|
||||
}
|
||||
}
|
||||
|
||||
if ($offset + $len > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + $len;
|
||||
|
||||
return substr($data, $offset, $len);
|
||||
|
||||
case 3: // Text string
|
||||
if ($info < 24) {
|
||||
$len = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteString($data, 0x60);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for text string: $info");
|
||||
}
|
||||
}
|
||||
|
||||
if ($offset + $len > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + $len;
|
||||
|
||||
return substr($data, $offset, $len);
|
||||
|
||||
case 4: // Array
|
||||
if ($info < 24) {
|
||||
$count = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteArray($data);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for array: $info");
|
||||
}
|
||||
}
|
||||
|
||||
$this->offset = $offset;
|
||||
$arr = [];
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$arr[] = $this->decodeValue($data);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
|
||||
case 5: // Map
|
||||
if ($info < 24) {
|
||||
$count = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$count = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
case 31:
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeIndefiniteMap($data);
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid additional info for map: $info");
|
||||
}
|
||||
}
|
||||
|
||||
$this->offset = $offset;
|
||||
$map = [];
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$key = $this->decodeValue($data);
|
||||
$map[$key] = $this->decodeValue($data);
|
||||
}
|
||||
|
||||
return $map;
|
||||
|
||||
case 6: // Tag
|
||||
switch ($info) {
|
||||
case 24:
|
||||
$offset++;
|
||||
break;
|
||||
|
||||
case 25:
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
$offset += 8;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this->decodeValue($data);
|
||||
|
||||
case 7: // Simple/float
|
||||
switch ($info) {
|
||||
case 20:
|
||||
$this->offset = $offset;
|
||||
|
||||
return false;
|
||||
|
||||
case 21:
|
||||
$this->offset = $offset;
|
||||
|
||||
return true;
|
||||
|
||||
case 22:
|
||||
case 23:
|
||||
$this->offset = $offset;
|
||||
|
||||
return null;
|
||||
|
||||
case 25: // Half-precision float
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 2;
|
||||
$half = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$sign = ($half >> 15) & 0x01;
|
||||
$exp = ($half >> 10) & 0x1F;
|
||||
$mant = $half & 0x3FF;
|
||||
|
||||
if ($exp === 0) {
|
||||
return $mant === 0
|
||||
? ($sign ? -0.0 : 0.0)
|
||||
: ($sign ? -1 : 1) * pow(2, -14) * ($mant / 1024);
|
||||
}
|
||||
|
||||
if ($exp === 31) {
|
||||
return $mant === 0 ? ($sign ? -INF : INF) : NAN;
|
||||
}
|
||||
|
||||
return (float) (($sign ? -1 : 1) * pow(2, $exp - 15) * (1 + $mant / 1024));
|
||||
|
||||
case 26: // Single-precision float
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 4;
|
||||
|
||||
return unpack('G', $data, $offset)[1];
|
||||
|
||||
case 27: // Double-precision float
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$this->offset = $offset + 8;
|
||||
|
||||
return unpack('E', $data, $offset)[1];
|
||||
|
||||
case 31:
|
||||
throw new CborException("Unexpected break");
|
||||
|
||||
default:
|
||||
throw new CborException("Unknown simple value: $info");
|
||||
}
|
||||
|
||||
default:
|
||||
throw new CborException("Unknown major type: $majorType");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode indefinite-length string (byte or text)
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
* @param int $expectedMajor Expected major type (0x40 for byte string, 0x60 for text string)
|
||||
*
|
||||
* @return string The concatenated string from all chunks
|
||||
* @throws CborException If invalid chunk format or unexpected end of data
|
||||
*/
|
||||
private function decodeIndefiniteString(string &$data, int $expectedMajor): string
|
||||
{
|
||||
$chunks = [];
|
||||
|
||||
while (true) {
|
||||
$offset = $this->offset;
|
||||
$length = $this->length;
|
||||
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
$byte = ord($data[$offset++]);
|
||||
|
||||
if ($byte === 0xFF) {
|
||||
$this->offset = $offset;
|
||||
|
||||
return implode('', $chunks);
|
||||
}
|
||||
|
||||
if (($byte & 0xE0) !== $expectedMajor) {
|
||||
throw new CborException("Invalid chunk in indefinite string");
|
||||
}
|
||||
|
||||
$info = $byte & 0x1F;
|
||||
|
||||
if ($info === 31) {
|
||||
throw new CborException("Nested indefinite string");
|
||||
}
|
||||
|
||||
if ($info < 24) {
|
||||
$len = $info;
|
||||
} else {
|
||||
switch ($info) {
|
||||
case 24:
|
||||
if ($offset >= $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = ord($data[$offset++]);
|
||||
break;
|
||||
|
||||
case 25:
|
||||
if ($offset + 2 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
|
||||
$offset += 2;
|
||||
break;
|
||||
|
||||
case 26:
|
||||
if ($offset + 4 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('N', $data, $offset)[1];
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
if ($offset + 8 > $length) {
|
||||
throw new CborException("Not enough data");
|
||||
}
|
||||
|
||||
$len = unpack('J', $data, $offset)[1];
|
||||
$offset += 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new CborException("Invalid chunk length info: $info");
|
||||
}
|
||||
}
|
||||
|
||||
if ($offset + $len > $length) {
|
||||
throw new CborException("Not enough data for chunk");
|
||||
}
|
||||
|
||||
$chunks[] = substr($data, $offset, $len);
|
||||
$this->offset = $offset + $len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode indefinite-length array
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
*
|
||||
* @return array The decoded array elements
|
||||
* @throws CborException If unexpected end of data
|
||||
*/
|
||||
private function decodeIndefiniteArray(string &$data): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
while (true) {
|
||||
if ($this->offset >= $this->length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
if (ord($data[$this->offset]) === 0xFF) {
|
||||
$this->offset++;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result[] = $this->decodeValue($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode indefinite-length map
|
||||
*
|
||||
* @param string $data Reference to the CBOR data being decoded
|
||||
*
|
||||
* @return array The decoded map as associative array
|
||||
* @throws CborException If unexpected end of data or odd number of items
|
||||
*/
|
||||
private function decodeIndefiniteMap(string &$data): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
while (true) {
|
||||
if ($this->offset >= $this->length) {
|
||||
throw new CborException("Unexpected end of data");
|
||||
}
|
||||
|
||||
if (ord($data[$this->offset]) === 0xFF) {
|
||||
$this->offset++;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$key = $this->decodeValue($data);
|
||||
$result[$key] = $this->decodeValue($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
357
vendor/aws/aws-sdk-php/src/Cbor/CborEncoder.php
vendored
Executable file
357
vendor/aws/aws-sdk-php/src/Cbor/CborEncoder.php
vendored
Executable file
@ -0,0 +1,357 @@
|
||||
<?php
|
||||
namespace Aws\Cbor;
|
||||
|
||||
use Aws\Cbor\Exception\CborException;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Encodes PHP values to Concise Binary Object Representation according to RFC 8949
|
||||
* https://www.rfc-editor.org/rfc/rfc8949.html
|
||||
*
|
||||
* Supports Major types 0-7 including:
|
||||
* - Type 0: Unsigned integers
|
||||
* - Type 1: Negative integers
|
||||
* - Type 2: Byte strings (via ['__cbor_bytes' => $data] wrappers)
|
||||
* - Type 3: Text strings (UTF-8)
|
||||
* - Type 4: Arrays
|
||||
* - Type 5: Maps
|
||||
* - Type 6: Tagged values (timestamps)
|
||||
* - Type 7: Simple values (null, bool, float)
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class CborEncoder
|
||||
{
|
||||
/**
|
||||
* Pre-encoded integers 0-23 (single byte) and common larger values
|
||||
* CBOR major type 0 (unsigned integer)
|
||||
*/
|
||||
private const INT_CACHE = [
|
||||
0 => "\x00", 1 => "\x01", 2 => "\x02", 3 => "\x03",
|
||||
4 => "\x04", 5 => "\x05", 6 => "\x06", 7 => "\x07",
|
||||
8 => "\x08", 9 => "\x09", 10 => "\x0A", 11 => "\x0B",
|
||||
12 => "\x0C", 13 => "\x0D", 14 => "\x0E", 15 => "\x0F",
|
||||
16 => "\x10", 17 => "\x11", 18 => "\x12", 19 => "\x13",
|
||||
20 => "\x14", 21 => "\x15", 22 => "\x16", 23 => "\x17",
|
||||
24 => "\x18\x18", 25 => "\x18\x19", 26 => "\x18\x1A",
|
||||
32 => "\x18\x20", 50 => "\x18\x32", 64 => "\x18\x40",
|
||||
100 => "\x18\x64", 128 => "\x18\x80", 200 => "\x18\xC8",
|
||||
255 => "\x18\xFF", 256 => "\x19\x01\x00", 500 => "\x19\x01\xF4",
|
||||
1000 => "\x19\x03\xE8", 1023 => "\x19\x03\xFF",
|
||||
];
|
||||
|
||||
/**
|
||||
* Pre-encoded negative integers -1 to -24 and common larger values
|
||||
* CBOR major type 1 (negative integer)
|
||||
*/
|
||||
private const NEG_CACHE = [
|
||||
-1 => "\x20", -2 => "\x21", -3 => "\x22", -4 => "\x23",
|
||||
-5 => "\x24", -10 => "\x29", -20 => "\x33", -24 => "\x37",
|
||||
-25 => "\x38\x18", -50 => "\x38\x31", -100 => "\x38\x63",
|
||||
];
|
||||
|
||||
/**
|
||||
* Encode a PHP value to CBOR binary string
|
||||
*
|
||||
* @param mixed $value The value to encode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encode(mixed $value): string
|
||||
{
|
||||
return $this->encodeValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively encode a value to CBOR
|
||||
*
|
||||
* @param mixed $value Value to encode
|
||||
* @return string Encoded CBOR bytes
|
||||
*/
|
||||
private function encodeValue(mixed $value): string
|
||||
{
|
||||
switch (gettype($value)) {
|
||||
case 'string':
|
||||
$len = strlen($value);
|
||||
if ($len < 24) {
|
||||
return chr(0x60 | $len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x100) {
|
||||
return "\x78" . chr($len) . $value;
|
||||
}
|
||||
|
||||
return $this->encodeTextString($value);
|
||||
|
||||
case 'array':
|
||||
// Encode a byte string (major type 2)
|
||||
if (isset($value['__cbor_bytes'])) {
|
||||
$bytes = $value['__cbor_bytes'];
|
||||
$len = strlen($bytes);
|
||||
if ($len < 24) {
|
||||
return chr(0x40 | $len) . $bytes;
|
||||
}
|
||||
|
||||
if ($len < 0x100) {
|
||||
return "\x58" . chr($len) . $bytes;
|
||||
}
|
||||
|
||||
if ($len < 0x10000) {
|
||||
return "\x59" . pack('n', $len) . $bytes;
|
||||
}
|
||||
|
||||
return "\x5A" . pack('N', $len) . $bytes;
|
||||
}
|
||||
|
||||
if (array_is_list($value)) {
|
||||
return $this->encodeArray($value);
|
||||
}
|
||||
|
||||
return $this->encodeMap($value);
|
||||
|
||||
case 'integer':
|
||||
if (isset(self::INT_CACHE[$value])) {
|
||||
return self::INT_CACHE[$value];
|
||||
}
|
||||
|
||||
if (isset(self::NEG_CACHE[$value])) {
|
||||
return self::NEG_CACHE[$value];
|
||||
}
|
||||
|
||||
// Fast path for positive integers
|
||||
// Major type 0: unsigned integer
|
||||
if ($value >= 0) {
|
||||
if ($value < 24) {
|
||||
return chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x100) {
|
||||
return "\x18" . chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x10000) {
|
||||
return "\x19" . pack('n', $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100000000) {
|
||||
return "\x1A" . pack('N', $value);
|
||||
}
|
||||
|
||||
return "\x1B" . pack('J', $value);
|
||||
}
|
||||
|
||||
return $this->encodeInteger($value);
|
||||
|
||||
case 'double':
|
||||
// Encode a float (major type 7, float 64)
|
||||
return "\xFB" . pack('E', $value);
|
||||
|
||||
case 'boolean':
|
||||
// Encode a boolean (major type 7, simple)
|
||||
return $value ? "\xF5" : "\xF4";
|
||||
|
||||
case 'NULL':
|
||||
// Encode null (major type 7, simple)
|
||||
return "\xF6";
|
||||
|
||||
case 'object':
|
||||
// Encode timestamp (major type 6, tag 1)
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
$timestamp = $value->getTimestamp();
|
||||
$micro = (int) $value->format('u');
|
||||
if ($micro === 0) {
|
||||
if ($timestamp >= 0 && $timestamp < 0x100000000) {
|
||||
return "\xC1\x1A" . pack('N', $timestamp);
|
||||
}
|
||||
|
||||
return "\xC1" . $this->encodeInteger($timestamp);
|
||||
}
|
||||
|
||||
return "\xC1\xFB" . pack('E', $timestamp + $micro / 1e6);
|
||||
}
|
||||
|
||||
throw new CborException("Cannot encode object of type: " . get_class($value));
|
||||
|
||||
default:
|
||||
throw new CborException("Cannot encode value of type: " . gettype($value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an integer (major type 0 or 1)
|
||||
*
|
||||
* @param int $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeInteger(int $value): string
|
||||
{
|
||||
if (isset(self::INT_CACHE[$value])) {
|
||||
return self::INT_CACHE[$value];
|
||||
}
|
||||
|
||||
if (isset(self::NEG_CACHE[$value])) {
|
||||
return self::NEG_CACHE[$value];
|
||||
}
|
||||
|
||||
if ($value >= 0) {
|
||||
// Major type 0: unsigned integer
|
||||
if ($value < 24) {
|
||||
return chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x100) {
|
||||
return "\x18" . chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x10000) {
|
||||
return "\x19" . pack('n', $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100000000) {
|
||||
return "\x1A" . pack('N', $value);
|
||||
}
|
||||
|
||||
return "\x1B" . pack('J', $value);
|
||||
}
|
||||
|
||||
// Major type 1: negative integer (-1 - n)
|
||||
$value = -1 - $value;
|
||||
if ($value < 24) {
|
||||
return chr(0x20 | $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100) {
|
||||
return "\x38" . chr($value);
|
||||
}
|
||||
|
||||
if ($value < 0x10000) {
|
||||
return "\x39" . pack('n', $value);
|
||||
}
|
||||
|
||||
if ($value < 0x100000000) {
|
||||
return "\x3A" . pack('N', $value);
|
||||
}
|
||||
|
||||
return "\x3B" . pack('J', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a text string (major type 3)
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeTextString(string $value): string
|
||||
{
|
||||
$len = strlen($value);
|
||||
|
||||
if ($len < 24) {
|
||||
return chr(0x60 | $len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x100) {
|
||||
return "\x78" . chr($len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x10000) {
|
||||
return "\x79" . pack('n', $len) . $value;
|
||||
}
|
||||
|
||||
if ($len < 0x100000000) {
|
||||
return "\x7A" . pack('N', $len) . $value;
|
||||
}
|
||||
|
||||
return "\x7B" . pack('J', $len) . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an array (major type 4)
|
||||
*
|
||||
* @param array $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeArray(array $value): string
|
||||
{
|
||||
$count = count($value);
|
||||
|
||||
if ($count < 24) {
|
||||
$result = chr(0x80 | $count);
|
||||
} elseif ($count < 0x100) {
|
||||
$result = "\x98" . chr($count);
|
||||
} elseif ($count < 0x10000) {
|
||||
$result = "\x99" . pack('n', $count);
|
||||
} elseif ($count < 0x100000000) {
|
||||
$result = "\x9A" . pack('N', $count);
|
||||
} else {
|
||||
$result = "\x9B" . pack('J', $count);
|
||||
}
|
||||
|
||||
foreach ($value as $item) {
|
||||
$result .= $this->encodeValue($item);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a map (major type 5)
|
||||
*
|
||||
* @param array $value
|
||||
* @return string
|
||||
*/
|
||||
private function encodeMap(array $value): string
|
||||
{
|
||||
$count = count($value);
|
||||
|
||||
if ($count < 24) {
|
||||
$result = chr(0xA0 | $count);
|
||||
} elseif ($count < 0x100) {
|
||||
$result = "\xB8" . chr($count);
|
||||
} elseif ($count < 0x10000) {
|
||||
$result = "\xB9" . pack('n', $count);
|
||||
} elseif ($count < 0x100000000) {
|
||||
$result = "\xBA" . pack('N', $count);
|
||||
} else {
|
||||
$result = "\xBB" . pack('J', $count);
|
||||
}
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_int($k)) {
|
||||
$result .= $this->encodeInteger($k);
|
||||
} else {
|
||||
$len = strlen($k);
|
||||
if ($len < 24) {
|
||||
$result .= chr(0x60 | $len) . $k;
|
||||
} elseif ($len < 0x100) {
|
||||
$result .= "\x78" . chr($len) . $k;
|
||||
} else {
|
||||
$result .= "\x79" . pack('n', $len) . $k;
|
||||
}
|
||||
}
|
||||
|
||||
$result .= $this->encodeValue($v);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty map (major type 5 with 0 elements)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encodeEmptyMap(): string
|
||||
{
|
||||
return "\xA0";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty indefinite map (major type 5 indefinite length)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encodeEmptyIndefiniteMap(): string
|
||||
{
|
||||
return "\xBF\xFF";
|
||||
}
|
||||
}
|
||||
6
vendor/aws/aws-sdk-php/src/Cbor/Exception/CborException.php
vendored
Executable file
6
vendor/aws/aws-sdk-php/src/Cbor/Exception/CborException.php
vendored
Executable file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Aws\Cbor\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class CborException extends RuntimeException {}
|
||||
97
vendor/aws/aws-sdk-php/src/ClientResolver.php
vendored
97
vendor/aws/aws-sdk-php/src/ClientResolver.php
vendored
@ -27,10 +27,13 @@ use Aws\Endpoint\UseFipsEndpoint\ConfigurationProvider as UseFipsConfigProvider;
|
||||
use Aws\EndpointDiscovery\ConfigurationInterface;
|
||||
use Aws\EndpointDiscovery\ConfigurationProvider;
|
||||
use Aws\EndpointV2\EndpointDefinitionProvider;
|
||||
use Aws\EndpointV2\EndpointProviderV2;
|
||||
use Aws\Exception\AwsException;
|
||||
use Aws\Exception\InvalidRegionException;
|
||||
use Aws\Retry\ConfigurationInterface as RetryConfigInterface;
|
||||
use Aws\Retry\ConfigurationProvider as RetryConfigProvider;
|
||||
use Aws\Retry\V3\OptIn as NewRetriesOptIn;
|
||||
use Aws\Retry\V3\RetryMiddleware as RetryV3Middleware;
|
||||
use Aws\Signature\SignatureProvider;
|
||||
use Aws\Token\Token;
|
||||
use Aws\Token\TokenInterface;
|
||||
@ -547,28 +550,42 @@ class ClientResolver
|
||||
public static function _apply_retries($value, array &$args, HandlerList $list)
|
||||
{
|
||||
// A value of 0 for the config option disables retries
|
||||
if ($value) {
|
||||
$config = RetryConfigProvider::unwrap($value);
|
||||
|
||||
if ($config->getMode() === 'legacy') {
|
||||
// # of retries is 1 less than # of attempts
|
||||
$decider = RetryMiddleware::createDefaultDecider(
|
||||
$config->getMaxAttempts() - 1
|
||||
);
|
||||
$list->appendSign(
|
||||
Middleware::retry($decider, null, $args['stats']['retries']),
|
||||
'retry'
|
||||
);
|
||||
} else {
|
||||
$list->appendSign(
|
||||
RetryMiddlewareV2::wrap(
|
||||
$config,
|
||||
['collect_stats' => $args['stats']['retries']]
|
||||
),
|
||||
'retry'
|
||||
);
|
||||
}
|
||||
if (!$value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$config = RetryConfigProvider::unwrap($value);
|
||||
|
||||
if ($config->getMode() === 'legacy') {
|
||||
// # of retries is 1 less than # of attempts
|
||||
$decider = RetryMiddleware::createDefaultDecider(
|
||||
$config->getMaxAttempts() - 1
|
||||
);
|
||||
$list->appendSign(
|
||||
Middleware::retry($decider, null, $args['stats']['retries']),
|
||||
'retry'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NewRetriesOptIn::isEnabled()) {
|
||||
$list->appendSign(
|
||||
RetryV3Middleware::wrap($config, [
|
||||
'collect_stats' => $args['stats']['retries'],
|
||||
'service' => $args['service'],
|
||||
]),
|
||||
'retry'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$list->appendSign(
|
||||
RetryMiddlewareV2::wrap(
|
||||
$config,
|
||||
['collect_stats' => $args['stats']['retries']]
|
||||
),
|
||||
'retry'
|
||||
);
|
||||
}
|
||||
|
||||
public static function _apply_defaults($value, array &$args, HandlerList $list)
|
||||
@ -791,7 +808,7 @@ class ClientResolver
|
||||
public static function _apply_endpoint_provider($value, array &$args)
|
||||
{
|
||||
if (!isset($args['endpoint'])) {
|
||||
if ($value instanceof \Aws\EndpointV2\EndpointProviderV2) {
|
||||
if ($value instanceof EndpointProviderV2) {
|
||||
$options = self::getEndpointProviderOptions($args);
|
||||
$value = PartitionEndpointProvider::defaultProvider($options)
|
||||
->getPartition($args['region'], $args['service']);
|
||||
@ -1112,14 +1129,13 @@ class ClientResolver
|
||||
if (self::isValidService($serviceName)
|
||||
&& self::isValidApiVersion($serviceName, $apiVersion)
|
||||
) {
|
||||
$ruleset = EndpointDefinitionProvider::getEndpointRuleset(
|
||||
$partitions = EndpointDefinitionProvider::getPartitions();
|
||||
$parsed = EndpointDefinitionProvider::getParsedRuleset(
|
||||
$service->getServiceName(),
|
||||
$service->getApiVersion()
|
||||
);
|
||||
return new \Aws\EndpointV2\EndpointProviderV2(
|
||||
$ruleset,
|
||||
EndpointDefinitionProvider::getPartitions()
|
||||
$service->getApiVersion(),
|
||||
$partitions
|
||||
);
|
||||
return new EndpointProviderV2($parsed, $partitions);
|
||||
}
|
||||
$options = self::getEndpointProviderOptions($args);
|
||||
return PartitionEndpointProvider::defaultProvider($options)
|
||||
@ -1167,7 +1183,7 @@ class ClientResolver
|
||||
}
|
||||
|
||||
// Assign user's preferred auth scheme list
|
||||
$args['auth_scheme_preference'] = $value;
|
||||
$args['config']['auth_scheme_preference'] = $value;
|
||||
}
|
||||
|
||||
public static function _default_signature_version(array &$args)
|
||||
@ -1247,12 +1263,6 @@ class ClientResolver
|
||||
$args['suppress_php_deprecation_warning'] =
|
||||
\Aws\boolean_value($_ENV["AWS_SUPPRESS_PHP_DEPRECATION_WARNING"]);
|
||||
}
|
||||
|
||||
if ($args['suppress_php_deprecation_warning'] === false
|
||||
&& PHP_VERSION_ID < 80100
|
||||
) {
|
||||
self::emitDeprecationWarning();
|
||||
}
|
||||
}
|
||||
|
||||
public static function _default_endpoint(array &$args)
|
||||
@ -1440,21 +1450,4 @@ EOT;
|
||||
__DIR__ . "/data/{$service}/$apiVersion"
|
||||
);
|
||||
}
|
||||
|
||||
private static function emitDeprecationWarning()
|
||||
{
|
||||
$phpVersionString = phpversion();
|
||||
trigger_error(
|
||||
"This installation of the SDK is using PHP version"
|
||||
. " {$phpVersionString}, which will be deprecated on January"
|
||||
. " 13th, 2025.\nPlease upgrade your PHP version to a minimum of"
|
||||
. " 8.1.x to continue receiving updates for the AWS"
|
||||
. " SDK for PHP.\nTo disable this warning, set"
|
||||
. " suppress_php_deprecation_warning to true on the client constructor"
|
||||
. " or set the environment variable AWS_SUPPRESS_PHP_DEPRECATION_WARNING"
|
||||
. " to true.\nMore information can be found at: "
|
||||
. "https://aws.amazon.com/blogs/developer/announcing-the-end-of-support-for-php-runtimes-8-0-x-and-below-in-the-aws-sdk-for-php/\n",
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
33
vendor/aws/aws-sdk-php/src/CloudFront/Signer.php
vendored
33
vendor/aws/aws-sdk-php/src/CloudFront/Signer.php
vendored
@ -81,8 +81,10 @@ class Signer
|
||||
$signatureHash = [];
|
||||
if ($policy) {
|
||||
$policy = preg_replace('/\s/s', '', $policy);
|
||||
self::validatePolicy($policy);
|
||||
$signatureHash['Policy'] = $this->encode($policy);
|
||||
} elseif ($resource && $expires) {
|
||||
self::validateResourceUrl($resource);
|
||||
$expires = (int) $expires; // Handle epoch passed as string
|
||||
$policy = $this->createCannedPolicy($resource, $expires);
|
||||
$signatureHash['Expires'] = $expires;
|
||||
@ -136,4 +138,35 @@ class Signer
|
||||
{
|
||||
return strtr(base64_encode($policy), '+=/', '-_~');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a customer provided json document.
|
||||
*
|
||||
* @param string $jsonPolicy
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function validatePolicy(string $jsonPolicy): void
|
||||
{
|
||||
$policy = json_decode($jsonPolicy, true);
|
||||
foreach ($policy['Statement'] ?? [] as $statement) {
|
||||
if (isset($statement['Resource'])) {
|
||||
self::validateResourceUrl($statement['Resource']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function validateResourceUrl(string $url): void
|
||||
{
|
||||
if (preg_match('/["\\\\\x00-\x1F]/', $url)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'URL contains invalid characters: ", \\, or control characters'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ class UrlSigner
|
||||
$parts = parse_url($url);
|
||||
$pathParts = pathinfo($parts['path']);
|
||||
$resource = ltrim(
|
||||
$pathParts['dirname'] . '/' . $pathParts['basename'],
|
||||
str_replace('\\', '/', $pathParts['dirname']) . '/' . $pathParts['basename'],
|
||||
'/'
|
||||
);
|
||||
|
||||
|
||||
@ -78,7 +78,7 @@ class CloudSearchDomainClient extends AwsClient
|
||||
$query = $r->getUri()->getQuery();
|
||||
$req = $r->withMethod('POST')
|
||||
->withBody(Psr7\Utils::streamFor($query))
|
||||
->withHeader('Content-Length', strlen($query))
|
||||
->withHeader('Content-Length', (string) strlen($query))
|
||||
->withHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
->withUri($r->getUri()->withQuery(''));
|
||||
return $req;
|
||||
|
||||
@ -6,6 +6,8 @@ use Aws\AwsClient;
|
||||
/**
|
||||
* This client is used to interact with the **Amazon CloudWatch** service.
|
||||
*
|
||||
* @method \Aws\Result deleteAlarmMuteRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteAlarmMuteRuleAsync(array $args = [])
|
||||
* @method \Aws\Result deleteAlarms(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteAlarmsAsync(array $args = [])
|
||||
* @method \Aws\Result deleteAnomalyDetector(array $args = [])
|
||||
@ -36,6 +38,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise enableAlarmActionsAsync(array $args = [])
|
||||
* @method \Aws\Result enableInsightRules(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise enableInsightRulesAsync(array $args = [])
|
||||
* @method \Aws\Result getAlarmMuteRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getAlarmMuteRuleAsync(array $args = [])
|
||||
* @method \Aws\Result getDashboard(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getDashboardAsync(array $args = [])
|
||||
* @method \Aws\Result getInsightRuleReport(array $args = [])
|
||||
@ -48,6 +52,10 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise getMetricStreamAsync(array $args = [])
|
||||
* @method \Aws\Result getMetricWidgetImage(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getMetricWidgetImageAsync(array $args = [])
|
||||
* @method \Aws\Result getOTelEnrichment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getOTelEnrichmentAsync(array $args = [])
|
||||
* @method \Aws\Result listAlarmMuteRules(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listAlarmMuteRulesAsync(array $args = [])
|
||||
* @method \Aws\Result listDashboards(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listDashboardsAsync(array $args = [])
|
||||
* @method \Aws\Result listManagedInsightRules(array $args = [])
|
||||
@ -58,6 +66,8 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise listMetricsAsync(array $args = [])
|
||||
* @method \Aws\Result listTagsForResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
|
||||
* @method \Aws\Result putAlarmMuteRule(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise putAlarmMuteRuleAsync(array $args = [])
|
||||
* @method \Aws\Result putAnomalyDetector(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise putAnomalyDetectorAsync(array $args = [])
|
||||
* @method \Aws\Result putCompositeAlarm(array $args = [])
|
||||
@ -78,8 +88,12 @@ use Aws\AwsClient;
|
||||
* @method \GuzzleHttp\Promise\Promise setAlarmStateAsync(array $args = [])
|
||||
* @method \Aws\Result startMetricStreams(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startMetricStreamsAsync(array $args = [])
|
||||
* @method \Aws\Result startOTelEnrichment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startOTelEnrichmentAsync(array $args = [])
|
||||
* @method \Aws\Result stopMetricStreams(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopMetricStreamsAsync(array $args = [])
|
||||
* @method \Aws\Result stopOTelEnrichment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopOTelEnrichmentAsync(array $args = [])
|
||||
* @method \Aws\Result tagResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
|
||||
* @method \Aws\Result untagResource(array $args = [])
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
<?php
|
||||
namespace Aws\CloudWatchEvidently;
|
||||
|
||||
use Aws\AwsClient;
|
||||
|
||||
/**
|
||||
* This client is used to interact with the **Amazon CloudWatch Evidently** service.
|
||||
* @method \Aws\Result batchEvaluateFeature(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise batchEvaluateFeatureAsync(array $args = [])
|
||||
* @method \Aws\Result createExperiment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createExperimentAsync(array $args = [])
|
||||
* @method \Aws\Result createFeature(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createFeatureAsync(array $args = [])
|
||||
* @method \Aws\Result createLaunch(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createLaunchAsync(array $args = [])
|
||||
* @method \Aws\Result createProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createProjectAsync(array $args = [])
|
||||
* @method \Aws\Result createSegment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createSegmentAsync(array $args = [])
|
||||
* @method \Aws\Result deleteExperiment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteExperimentAsync(array $args = [])
|
||||
* @method \Aws\Result deleteFeature(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteFeatureAsync(array $args = [])
|
||||
* @method \Aws\Result deleteLaunch(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteLaunchAsync(array $args = [])
|
||||
* @method \Aws\Result deleteProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteProjectAsync(array $args = [])
|
||||
* @method \Aws\Result deleteSegment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteSegmentAsync(array $args = [])
|
||||
* @method \Aws\Result evaluateFeature(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise evaluateFeatureAsync(array $args = [])
|
||||
* @method \Aws\Result getExperiment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getExperimentAsync(array $args = [])
|
||||
* @method \Aws\Result getExperimentResults(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getExperimentResultsAsync(array $args = [])
|
||||
* @method \Aws\Result getFeature(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getFeatureAsync(array $args = [])
|
||||
* @method \Aws\Result getLaunch(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getLaunchAsync(array $args = [])
|
||||
* @method \Aws\Result getProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getProjectAsync(array $args = [])
|
||||
* @method \Aws\Result getSegment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getSegmentAsync(array $args = [])
|
||||
* @method \Aws\Result listExperiments(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listExperimentsAsync(array $args = [])
|
||||
* @method \Aws\Result listFeatures(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listFeaturesAsync(array $args = [])
|
||||
* @method \Aws\Result listLaunches(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listLaunchesAsync(array $args = [])
|
||||
* @method \Aws\Result listProjects(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listProjectsAsync(array $args = [])
|
||||
* @method \Aws\Result listSegmentReferences(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listSegmentReferencesAsync(array $args = [])
|
||||
* @method \Aws\Result listSegments(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listSegmentsAsync(array $args = [])
|
||||
* @method \Aws\Result listTagsForResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
|
||||
* @method \Aws\Result putProjectEvents(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise putProjectEventsAsync(array $args = [])
|
||||
* @method \Aws\Result startExperiment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startExperimentAsync(array $args = [])
|
||||
* @method \Aws\Result startLaunch(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise startLaunchAsync(array $args = [])
|
||||
* @method \Aws\Result stopExperiment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopExperimentAsync(array $args = [])
|
||||
* @method \Aws\Result stopLaunch(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise stopLaunchAsync(array $args = [])
|
||||
* @method \Aws\Result tagResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
|
||||
* @method \Aws\Result testSegmentPattern(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise testSegmentPatternAsync(array $args = [])
|
||||
* @method \Aws\Result untagResource(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
|
||||
* @method \Aws\Result updateExperiment(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateExperimentAsync(array $args = [])
|
||||
* @method \Aws\Result updateFeature(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateFeatureAsync(array $args = [])
|
||||
* @method \Aws\Result updateLaunch(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateLaunchAsync(array $args = [])
|
||||
* @method \Aws\Result updateProject(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateProjectAsync(array $args = [])
|
||||
* @method \Aws\Result updateProjectDataDelivery(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateProjectDataDeliveryAsync(array $args = [])
|
||||
*/
|
||||
class CloudWatchEvidentlyClient extends AwsClient {}
|
||||
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
namespace Aws\CloudWatchEvidently\Exception;
|
||||
|
||||
use Aws\Exception\AwsException;
|
||||
|
||||
/**
|
||||
* Represents an error interacting with the **Amazon CloudWatch Evidently** service.
|
||||
*/
|
||||
class CloudWatchEvidentlyException extends AwsException {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user