En esta segunda entrada, el foco va a ser usar el paquete flag para que el usuario pueda proporcionar, por ejemplo, el projectId donde listar las API keys.
Haciendo limpieza de repositorios “viejos”, me encontré con GoogleCloudPlatform/professional-services. El servicio de IPAM (IP Address Management) que empezamos usando fue el proporcionado por los Servicios Profesionales de Google Cloud, pero antes de borrar el repositorio, me entretuve mirando qué otras soluciones se ofrecían…
En particular, GCP API key rotation checker me llamó la atención, pero tiene dos problemas fundamentales; el primero, que hace más de cinco años que no se ha actualizado 😔, y el segundo, que está en Python 😅. Afortunadamente, los dos pueden solucionarse re-escribiéndo la solución en Go 😜.
Tenemos varios procesos que se basan en parsear datos proporcionados por los usuarios en un fichero CSV, validarlos y “hacer algo con ellos”.
Los scripts que se encargan de estos procesos se pueden dividir en dos categorías: sencillos pero “delicados” y robustos pero complicados.
Uno de nuestros scripts de validación ha crecido y crecido para incorporar todo tipo de transformaciones y validaciones… Pero como suele pasar en estos casos, la evolución ha sido orgánica, es decir, sin planificación… Y aunque es perfecto para la tarea que realiza, es difícil aprovechar todo lo que hemos desarrollado para poder reutilizarlo en cualquiera de los otros procesos y así, mejorarlos.
El problema no son las funciones que implementan una u otra función de validación; el problema es que hay que hay una relación invisible entre los datos en el CSV y las funciones que deben aplicarse a cada campo… Al no estar documentada esa relación, mantener los scripts resulta complicado si no se está familiarizado con la tarea para la que fueron diseñados.
En vez de seguir ampliando el problema, he desarrollado una aplicación que espero que pueda aplicarse de forma “universal” en todos los procesos y acabar con el problema de una vez por todas.
Y la solución que se me ocurrió fue la de definir un schema para el CSV.
Uno de los problemas que he encontrado en mi reinterpretación de la API que uso en el trabajo ha sido la conexión con la base de datos.
Siguiendo las buenas prácticas, mi intención era reutilizar el cliente a la base de datos. En vez de usar una variable global, que suele ser la aproximación recomendada, guardaba la struct en el contexto de Gin.
Sin embargo, el contexto se propaga para una misma petición, pero no se mantiene entre peticiones… Así que para cada nueva petición que recibía la API se creaba un nuevo client 😞.
Todo lo relacionado con la base de datos se encuentra en su propio paquete, por lo que tendría sentido crear una variable “global” dentro del paquete…
Sin embargo, seguía quedando pendiente el tema de cerrar la conexión con la base de datos al finalizar la aplicación…
En esta entrada explico la solución que quiero probar (ya veremos si funciona 😉, parece que sí).
El functional options pattern permite crear un objeto con un número arbitrario de “opciones” manteniendo siempre la signature de la función que lo crea.
La idea original fue propuesta, si no estoy equivocado, por Dave Cheney allá por 2014 en el artículo Functional options for friendly APIs. El artículo muestra múltiples maneras de atacar el problema y cómo las functional options es una de la soluciones más sencillas.
Imagina que quieres crear un objeto pizza. Pero no todo el mundo quiere la misma pizza; unos quieren masa fina, con extra de queso o con peperoni y champiñones, otros masa normal sin ningún extra o topping adicional, etc.
¿Cómo defines la función NewPizza para que puedas satisfacer a todos tus clientes? Tampoco quieres tener que cambiar la función cada vez que se añada una nueva opción o ingrediente a la pizza…
La solución son las funcional options.
Actualización: 26/12/2023
Algunos aspectos de este artículo quizás no están del todo bien explicados; por ejemplo, las propiedades en config están en mayúsculas (exportadas), por lo que se puede establecer sin necesidad de functional options. La signatura de la función NewClient es incorrecta, pues no incluye que devuelve (*DBClient, error)… En vez de corregirla, he creado una entrada más concisa y sin errores conocidos, al menos por ahora.
Hace unas semanas comentaba que estaba trabajando en un proyecto personal para implementar un parseador de reglas para el proxy, en Go.
Después de leer los artículos Write packages, not programs y From packages to commands de John Arundel (así como los artículos a los que enlaza), decidí enfocar de manera diferente el parseador de reglas del proxy.
Empecé a escribir un nuevo módulo rules guiado por tests…
El resultado se encuentra disponible en ontehdock/proxy-rules en Github, pero aquí apunto algunas pinceladas (a modo de notas personales).
Aprovechando la calma de estos días, he revisado algo del código que tenemos en algunas de nuestras pipelines. He aplicado las ideas explicadas en estos artículos y el incremento en la claridad del código ha sido espectacular.
En entradas anteriores he escrito sobre cómo interaccionar con una API a través de scripts en Bash, usando curl y poco más… En vez de dejar en manos de los usuarios la tarea de generar el payload que enviar vía curl a la API, desarrollé un cliente en Bash: Cliente API en Bash (con curl).
Desde entonces, he estado trabajando en una versión en Go del cliente para ésta API… Y creo que ¡ya está lista 🎉!
En esta entrada, describo por encima cómo funciona, pero sobre todo cómo ha sido la experiencia de desarrollarla.