data_drain 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +12 -0
- data/CHANGELOG.md +43 -0
- data/README.md +30 -0
- data/docs/IMPROVEMENT_PLAN.md +114 -0
- data/docs/execution/v0.4.0-OBSERVACIONES.md +144 -0
- data/docs/execution/v0.4.0.md +1216 -0
- data/docs/execution/v0.5.0-OBSERVACIONES.md +167 -0
- data/docs/execution/v0.5.0.md +900 -0
- data/docs/glue-jobs-lifecycle.md +330 -0
- data/docs/glue_pyspark_example.py +49 -19
- data/lib/data_drain/glue_runner.rb +236 -1
- data/lib/data_drain/storage/base.rb +12 -0
- data/lib/data_drain/storage/local.rb +13 -0
- data/lib/data_drain/storage/s3.rb +17 -0
- data/lib/data_drain/validations.rb +8 -0
- data/lib/data_drain/version.rb +1 -1
- data/skill/SKILL.md +64 -3
- data/skill/references/eventos-telemetria.md +8 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c92c85e6232344565dc090539d3d58aa47904ca1e454b696ba4eba12e2648881
|
|
4
|
+
data.tar.gz: 1b3332ac50288dfd6793aed0c51ab8fa49d8dc309f7bc8dd4a642ecffd97e3cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0cecb3d168ad96943b9cc70eb936e9b95e92d03dc0bd08ed5996ac1967ef627191802892097abf3b094d5b59515c150ca8edc4ffcefe13be8b4a2d7721a180a
|
|
7
|
+
data.tar.gz: 45018ca7e4287bf055cb7060e5720f83da318bd7c9ad44a30d8e832972feb1eb99d29286a9813128d4f84171d4c032e12f9a4f4dd7cc0022b36e44a3098d91c8
|
data/.rubocop.yml
CHANGED
|
@@ -23,6 +23,18 @@ Metrics/BlockLength:
|
|
|
23
23
|
- data_drain.gemspec
|
|
24
24
|
- lib/**/*.rb
|
|
25
25
|
|
|
26
|
+
Metrics/ParameterLists:
|
|
27
|
+
Exclude:
|
|
28
|
+
- lib/**/*.rb
|
|
29
|
+
|
|
30
|
+
Metrics/CyclomaticComplexity:
|
|
31
|
+
Exclude:
|
|
32
|
+
- lib/**/*.rb
|
|
33
|
+
|
|
34
|
+
Metrics/PerceivedComplexity:
|
|
35
|
+
Exclude:
|
|
36
|
+
- lib/**/*.rb
|
|
37
|
+
|
|
26
38
|
Layout/LineLength:
|
|
27
39
|
Exclude:
|
|
28
40
|
- lib/**/configuration.rb # connection string URL > 120 chars
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-04-15
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- `Storage::S3#upload_file` y `Storage::Local#upload_file`: primitiva para subir archivos al storage configurado. (item 37)
|
|
8
|
+
- `GlueRunner.upload_script(local_path:, bucket:, folder:, filename:)`: sube script Python local a S3 usando el `Storage::S3` adapter existente. Emite `glue_runner.script_uploaded` (INFO) y `glue_runner.script_upload_error` (ERROR). (item 37)
|
|
9
|
+
- `GlueRunner.create_job` y `GlueRunner.ensure_job` aceptan `script_path:` + `script_bucket:` + `script_folder:` + `script_filename:` para subir scripts locales automáticamente. Si se usa `script_location:`, comportamiento idéntico al anterior. (item 37)
|
|
10
|
+
|
|
11
|
+
### Docs
|
|
12
|
+
|
|
13
|
+
- `docs/glue-jobs-lifecycle.md`: sección "Subir scripts locales" con patrón completo, permisos IAM mínimos y limitación de concurrencia.
|
|
14
|
+
- `docs/glue_pyspark_example.py`: ejemplo de uso con `script_path`.
|
|
15
|
+
|
|
16
|
+
### Notas
|
|
17
|
+
|
|
18
|
+
- **Upload NO es idempotente en sentido estricto:** `put_object` sobrescribe siempre. Documentado.
|
|
19
|
+
- `upload_script` requiere `storage_mode = :s3`. En `:local` levanta `ConfigurationError`.
|
|
20
|
+
|
|
21
|
+
## [0.4.0] - 2026-04-15
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
- `GlueRunner.job_exists?(job_name)`: verifica si un job existe. Retorna `true`/`false`. (item 35)
|
|
26
|
+
- `GlueRunner.get_job(job_name)`: obtiene la configuración completa de un job. Retorna `Aws::Glue::Types::Job`. (item 35)
|
|
27
|
+
- `GlueRunner.create_job(job_name, role_arn:, script_location:, ...)`: crea un job con configuración completa. Retorna el job creado. (item 32)
|
|
28
|
+
- `GlueRunner.update_job(job_name, ...)`: actualiza un job existente. Retorna el job actualizado. (item 33)
|
|
29
|
+
- `GlueRunner.delete_job(job_name)`: elimina un job. Retorna `nil`. (item 34)
|
|
30
|
+
- `GlueRunner.ensure_job(job_name, ...)`: upsert idempotente — crea si no existe, actualiza si existe. (item 36)
|
|
31
|
+
|
|
32
|
+
### Validations
|
|
33
|
+
|
|
34
|
+
- `DataDrain::Validations.validate_glue_name!`: validación específica para nombres de Glue Jobs (letras, números, guiones; no permite guiones bajos ni espacios).
|
|
35
|
+
|
|
36
|
+
### Tests
|
|
37
|
+
|
|
38
|
+
- 163 specs, coverage 97.39%.
|
|
39
|
+
|
|
40
|
+
### Docs
|
|
41
|
+
|
|
42
|
+
- `docs/glue-jobs-lifecycle.md`: referencia completa de la API de Glue Jobs.
|
|
43
|
+
- README.md actualizado con ejemplos de todos los métodos.
|
|
44
|
+
- `skill/references/eventos-telemetria.md`: nuevos eventos `glue_runner.job_exists` y `glue_runner.job_created`.
|
|
45
|
+
|
|
3
46
|
## [0.3.2] - 2026-04-15
|
|
4
47
|
|
|
5
48
|
### Regresiónfix (desde v0.3.1)
|
data/README.md
CHANGED
|
@@ -107,6 +107,36 @@ DataDrain::Engine.new(
|
|
|
107
107
|
### Orquestación con AWS Glue (tablas 1TB+)
|
|
108
108
|
|
|
109
109
|
```ruby
|
|
110
|
+
# Verificar si un job existe
|
|
111
|
+
DataDrain::GlueRunner.job_exists?("my-glue-export-job")
|
|
112
|
+
# => true / false
|
|
113
|
+
|
|
114
|
+
# Obtener configuración de un job
|
|
115
|
+
job = DataDrain::GlueRunner.get_job("my-glue-export-job")
|
|
116
|
+
# => Aws::Glue::Types::Job (Name, Command, DefaultArguments, etc.)
|
|
117
|
+
|
|
118
|
+
# Crear un job
|
|
119
|
+
job = DataDrain::GlueRunner.create_job(
|
|
120
|
+
"my-glue-export-job",
|
|
121
|
+
role_arn: "arn:aws:iam::123:role/GlueServiceRole",
|
|
122
|
+
script_location: "s3://my-bucket/scripts/export.py",
|
|
123
|
+
default_arguments: { "--extra-files" => "s3://my-bucket/scripts/udf.py" },
|
|
124
|
+
timeout: 1440,
|
|
125
|
+
max_retries: 2
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Asegurar job idempotente (crea si no existe, actualiza si existe)
|
|
129
|
+
job = DataDrain::GlueRunner.ensure_job(
|
|
130
|
+
"my-glue-export-job",
|
|
131
|
+
role_arn: "arn:aws:iam::123:role/GlueServiceRole",
|
|
132
|
+
script_location: "s3://my-bucket/scripts/export.py",
|
|
133
|
+
timeout: 1440
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Eliminar un job
|
|
137
|
+
DataDrain::GlueRunner.delete_job("my-glue-export-job")
|
|
138
|
+
|
|
139
|
+
# Ejecutar y esperar
|
|
110
140
|
DataDrain::GlueRunner.run_and_wait(
|
|
111
141
|
"my-glue-export-job",
|
|
112
142
|
{
|
data/docs/IMPROVEMENT_PLAN.md
CHANGED
|
@@ -1521,3 +1521,117 @@ El workflow actual usa `bundler-cache: false`. Habilitar `bundler-cache: true` j
|
|
|
1521
1521
|
```
|
|
1522
1522
|
|
|
1523
1523
|
**Riesgo:** Requiere que el step "Download DuckDB library" corra antes de bundle install para que Bundler cachee correctamente los gems compilados.
|
|
1524
|
+
|
|
1525
|
+
---
|
|
1526
|
+
|
|
1527
|
+
### Item 32 — Glue Jobs Lifecycle: create/update/delete atómicos
|
|
1528
|
+
|
|
1529
|
+
**Estado:** `[x]`
|
|
1530
|
+
**Prioridad:** P2
|
|
1531
|
+
**Tipo:** `feat`
|
|
1532
|
+
**Estimación:** M
|
|
1533
|
+
**Release sugerido:** v0.4.0
|
|
1534
|
+
|
|
1535
|
+
##### Contexto
|
|
1536
|
+
|
|
1537
|
+
`GlueRunner.run_and_wait` solo ejecuta jobs pre-existentes. Para automatizar el ciclo de vida completo (infra-as-code), se agregan métodos para crear, actualizar y eliminar jobs.
|
|
1538
|
+
|
|
1539
|
+
##### Cambios
|
|
1540
|
+
|
|
1541
|
+
1. `GlueRunner.create_job(job_name, role_arn:, script_location:, ...)` — crea un Glue Job con defaults razonables. Retorna `Aws::Glue::Types::Job`.
|
|
1542
|
+
2. `GlueRunner.update_job(job_name, ...)` — actualiza un job existente. Retorna el job actualizado.
|
|
1543
|
+
3. `GlueRunner.delete_job(job_name)` — elimina un job. Retorna `nil`.
|
|
1544
|
+
|
|
1545
|
+
##### Criterios de aceptación
|
|
1546
|
+
|
|
1547
|
+
- [x] `create_job` retorna el job object creado.
|
|
1548
|
+
- [x] `update_job` falla con EntityNotFoundException si no existe.
|
|
1549
|
+
- [x] `delete_job` retorna nil.
|
|
1550
|
+
- [x] `validate_glue_name!` permite guiones en nombres (regex `[a-zA-Z0-9-]`).
|
|
1551
|
+
|
|
1552
|
+
---
|
|
1553
|
+
|
|
1554
|
+
### Item 33 — `ensure_job` idempotente
|
|
1555
|
+
|
|
1556
|
+
**Estado:** `[x]`
|
|
1557
|
+
**Prioridad:** P2
|
|
1558
|
+
**Tipo:** `feat`
|
|
1559
|
+
**Estimación:** M
|
|
1560
|
+
**Release sugerido:** v0.4.0
|
|
1561
|
+
|
|
1562
|
+
##### Contexto
|
|
1563
|
+
|
|
1564
|
+
Wrapper idempotente que garantiza un job existe con la config deseada: lo crea si no existe, lo actualiza si difiere.
|
|
1565
|
+
|
|
1566
|
+
##### Cambios
|
|
1567
|
+
|
|
1568
|
+
```ruby
|
|
1569
|
+
DataDrain::GlueRunner.ensure_job("my-job", role_arn: "...", script_location: "...")
|
|
1570
|
+
# => Aws::Glue::Types::Job
|
|
1571
|
+
```
|
|
1572
|
+
|
|
1573
|
+
##### Criterios de aceptación
|
|
1574
|
+
|
|
1575
|
+
- [x] Crea el job si no existe.
|
|
1576
|
+
- [x] Actualiza el job si ya existe.
|
|
1577
|
+
- [x] Emite `glue_runner.job_created` / `glue_runner.job_exists`.
|
|
1578
|
+
|
|
1579
|
+
---
|
|
1580
|
+
|
|
1581
|
+
### Item 34 — Helpers consultivos: `job_exists?` + `get_job`
|
|
1582
|
+
|
|
1583
|
+
**Estado:** `[x]`
|
|
1584
|
+
**Prioridad:** P2
|
|
1585
|
+
**Tipo:** `feat`
|
|
1586
|
+
**Estimación:** S
|
|
1587
|
+
**Release sugerido:** v0.4.0
|
|
1588
|
+
|
|
1589
|
+
##### Contexto
|
|
1590
|
+
|
|
1591
|
+
Foundation para items 32 y 33. `get_job` retorna el Job object; `job_exists?` es boolean.
|
|
1592
|
+
|
|
1593
|
+
##### Criterios de aceptación
|
|
1594
|
+
|
|
1595
|
+
- [x] `get_job` retorna `Aws::Glue::Types::Job`.
|
|
1596
|
+
- [x] `job_exists?` retorna boolean.
|
|
1597
|
+
- [x] EntityNotFoundException → false (no propaga en `job_exists?`).
|
|
1598
|
+
|
|
1599
|
+
---
|
|
1600
|
+
|
|
1601
|
+
### Item 35 — Tests consolidación Glue Jobs
|
|
1602
|
+
|
|
1603
|
+
**Estado:** `[x]`
|
|
1604
|
+
**Prioridad:** P2
|
|
1605
|
+
**Tipo:** `test`
|
|
1606
|
+
**Estimación:** M
|
|
1607
|
+
**Release sugerido:** v0.4.0
|
|
1608
|
+
|
|
1609
|
+
##### Contexto
|
|
1610
|
+
|
|
1611
|
+
Suite de tests con `Aws::Glue::Client.stub_responses` para los 5 nuevos métodos. Coverage ≥ 90%.
|
|
1612
|
+
|
|
1613
|
+
##### Criterios de aceptación
|
|
1614
|
+
|
|
1615
|
+
- [ ] Tests para todos los nuevos métodos.
|
|
1616
|
+
- [ ] Edge cases: `default_arguments` hash equality, Symbol vs String keys.
|
|
1617
|
+
- [ ] Coverage ≥ 90%.
|
|
1618
|
+
|
|
1619
|
+
---
|
|
1620
|
+
|
|
1621
|
+
### Item 36 — Docs: `glue-jobs-lifecycle.md`
|
|
1622
|
+
|
|
1623
|
+
**Estado:** `[x]`
|
|
1624
|
+
**Prioridad:** P2
|
|
1625
|
+
**Tipo:** `docs`
|
|
1626
|
+
**Estimación:** S
|
|
1627
|
+
**Release sugerido:** v0.4.0
|
|
1628
|
+
|
|
1629
|
+
##### Contexto
|
|
1630
|
+
|
|
1631
|
+
Documentación del nuevo feature: pre-requisitos IAM, API de cada método, eventos de telemetría, limitaciones, patrón completo ensure+run.
|
|
1632
|
+
|
|
1633
|
+
##### Criterios de aceptación
|
|
1634
|
+
|
|
1635
|
+
- [x] `docs/glue-jobs-lifecycle.md` creado.
|
|
1636
|
+
- [x] README actualizado con ejemplo.
|
|
1637
|
+
- [x] Eventos catalogados en `eventos-telemetria.md`.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Observaciones — Plan v0.4.0
|
|
2
|
+
|
|
3
|
+
**Fecha:** 2026-04-15
|
|
4
|
+
**Proyecto:** data_drain
|
|
5
|
+
**Release:** v0.4.0 — Glue Jobs Lifecycle
|
|
6
|
+
**Estado:** En análisis
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Crítica — Validación de nombres de Glue Jobs (BLOCKING)
|
|
11
|
+
|
|
12
|
+
**Ubicación:** Fase 2, sección 2.4 + línea 1008-1009 del Plan B
|
|
13
|
+
|
|
14
|
+
**Problema:**
|
|
15
|
+
`Validations.validate_identifier!` usa regex `\A[a-zA-Z_][a-zA-Z0-9_]*\z` — no permite guiones (`-`). AWS Glue SÍ permite guiones en nombres de jobs:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
data-drain-export-versions ✅ válido en AWS
|
|
19
|
+
data_drain_export_versions ✅ válido en regex actual
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
El caso de uso del plan (líneas 19-26) usa `name: "data-drain-export-versions"` con guiones.
|
|
23
|
+
|
|
24
|
+
**Impacto:** El feature completo queda bloqueado si no se resuelve antes de Fase 2.
|
|
25
|
+
|
|
26
|
+
**Solución propuesta:** Crear `validate_glue_name!` en `lib/data_drain/validations.rb`:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
def self.validate_glue_name!(field_name, value)
|
|
30
|
+
return if value.to_s.match?(/\A[a-zA-Z0-9_-]+\z/)
|
|
31
|
+
|
|
32
|
+
raise ConfigurationError, "#{field_name} debe ser un Glue Job name válido (alfanumérico, guiones y guiones bajos)"
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Alternativas:
|
|
37
|
+
- Modificar `validate_identifier!` existente para permitir `-` → riesgo: afecta todos los usos existentes
|
|
38
|
+
- No validar → más simple pero menos defensive
|
|
39
|
+
|
|
40
|
+
**Recomendación:** Crear `validate_glue_name!` específica para Glue, no tocar `validate_identifier!`.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Media — `extract_current_config` puede retornar nil silenciosamente
|
|
45
|
+
|
|
46
|
+
**Ubicación:** Fase 3, sección 3.2
|
|
47
|
+
|
|
48
|
+
**Problema:**
|
|
49
|
+
`extract_current_config` usa safe navigation (`&.`) para todos los campos anidados:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
script_location: job.command&.script_location,
|
|
53
|
+
command_name: job.command&.name,
|
|
54
|
+
default_arguments: job.default_arguments&.to_h || {},
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Si AWS retorna un job sin `command` (edge case improbable pero posible), `script_location` retorna `nil`. Luego en `changed_fields`:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
desired_config[field] != extracted[field] # nil != "s3://..." → true
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Esto generaría un false positive: `ensure_job` dispararía `update_job` por un campo que el job no soporta en ese estado.
|
|
64
|
+
|
|
65
|
+
**Mitigación:** Los stubs de test en el plan incluyen `command:` siempre. Pero el test "ignora campos no especificados por el caller" (línea 686-700) no verifica este edge case.
|
|
66
|
+
|
|
67
|
+
**Recomendación:** En `extract_current_config`, si un campo es `nil` y el caller no lo especificó, tratarlo como "no opinion" — no debería Disparar diff. Modificar `changed_fields` para excluir campos donde `extracted[field].nil?` Y `!desired_config.key?(field)`.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Media — `update_job` API shape requiere verificación
|
|
72
|
+
|
|
73
|
+
**Ubicación:** Fase 2, sección 2.3, líneas 325-332
|
|
74
|
+
|
|
75
|
+
**Problema:**
|
|
76
|
+
El plan asume esta shape para `update_job`:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
client.update_job(name: config[:name], job_update: job_update)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Donde `job_update = aws_params.except(:name)`. Pero la AWS Glue API para `update_job` tiene quirks:
|
|
83
|
+
|
|
84
|
+
1. `job_update` no puede incluir `Name` (es el path param)
|
|
85
|
+
2. Algunos campos como `Command` requieren la estructura completa, no parcial
|
|
86
|
+
3. `ExecutionProperty` requiere `{ max_concurrent_runs: Integer }` explícito
|
|
87
|
+
|
|
88
|
+
**Recomendación:** Antes de Fase 2, verificar con test de stub que la API acepta el hash generado. O escribir un test rápido contra el stub que captura los params enviados.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Baja — Timestamp en `update_job` no manejado
|
|
93
|
+
|
|
94
|
+
**Ubicación:** Fase 3
|
|
95
|
+
|
|
96
|
+
**Problema:**
|
|
97
|
+
AWS Glue Jobs tienen campos `CreatedOn` y `LastModifiedOn` (timestamps). Cuando `get_job` retorna el job actual, estos timestamps siempre difieren de lo que el caller setearía (porque el caller no los setea).
|
|
98
|
+
|
|
99
|
+
Si `changed_fields` incluyera `CreatedOn` o `LastModifiedOn`, siempre dispararía update.
|
|
100
|
+
|
|
101
|
+
El plan filtra por `desired_config.key?(field)` así que no debería pasar — pero hay que asegurar que `extract_current_config` NO extraiga estos campos.
|
|
102
|
+
|
|
103
|
+
**Recomendación:** Verificar que `extract_current_config` (líneas 593-608) no incluya `CreatedOn`, `LastModifiedOn`, ni `AllocatedCapacity`. Si los incluye, quitarlos.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Baja — Cobertura de `default_arguments` en diff
|
|
108
|
+
|
|
109
|
+
**Ubicación:** Fase 4, sección 4.1
|
|
110
|
+
|
|
111
|
+
**Problema:**
|
|
112
|
+
`default_arguments` es un Hash. La comparación `desired_config[field] != extracted[field]` en Ruby compara referencias, no contenido:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
{ "--key" => "val1" } != { "--key" => "val1" } # true (objetos distintos)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Esto significa que `ensure_job` siempre vería diff en `default_arguments` aunque los valores sean iguales.
|
|
119
|
+
|
|
120
|
+
**Recomendación:** Implementar comparación de hashes recursiva o usar `==` en lugar de `!=` en `changed_fields`, o convertir a JSON string para comparación:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
desired_config[field].to_json == extracted[field].to_json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Plan B — Items relevantes a verificar pre-ejecución
|
|
129
|
+
|
|
130
|
+
| Item | Riesgo | Acción pre-ejecución |
|
|
131
|
+
|------|--------|----------------------|
|
|
132
|
+
| Glue Job names con `-` | Confirmed blocking | Crear `validate_glue_name!` antes de Fase 2 |
|
|
133
|
+
| `update_job` API shape | Medio | Test con stub que captura params antes de Fase 2 |
|
|
134
|
+
| `default_arguments` comparison | Bajo | Implementar comparación por JSON en `changed_fields` |
|
|
135
|
+
| Timestamps en job | Bajo | Verificar `extract_current_config` no los incluye |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Orden sugerido de resolución pre-ejecución
|
|
140
|
+
|
|
141
|
+
1. **Hoy:** Crear `validate_glue_name!` en `Validations`
|
|
142
|
+
2. **Antes de Fase 2:** Escribir test rápido que verifica `update_job` API call shape
|
|
143
|
+
3. **Antes de Fase 3:** Implementar `JSON.parse(JSON.dump())` comparación para `default_arguments`
|
|
144
|
+
4. **Durante Fase 3:** Verificar que `extract_current_config` no extraiga timestamps
|