Que puto máquina, a favoritos.
#31 ¡Gracias!
Por cierto, que al final se me ha pasado ponerlo. La estructura del backend se me ha quedado así de momento, a la espera de añadir lo que pueda ir faltando:
Día 10
En el backend he añadido un formatter para que me devuelva las cantidades como una moneda y no como un double con trescientos decimales. En el front he estado todo el día pegándome con Bootstrap y me ha quedado así de momento:
Los datos son completamente dummy, están ahí hardcodeados para poder ver cómo quedaría el layout con los datos buenos.
Siguientes pasos
- Recuperar la respuesta de la XMLHttpRequest y meterlos en los tags correspondientes. Esto va a ser largo, no sé ni por dónde empezar.
- Hacerme una playlist de funk, la de grunge la tengo agotada y Vulfpeck me están molando mucho.
#36 ¿Cómo lo hubieras hecho tú? Teniendo en cuenta que es portafolio para un dev backend Java que quiere irse hacia un perfil full stack y la idea es demostrar que puede hacer esto con y sin usar frameworks.
#37 La cosa es que nadie hace las cosas sin una libreria (que no framework), al igual que no te pones a dise;ar ladrillos para cuando te quieres construir una casa. Si te quieres ir hacia un perfil full stak entonces metete de cabez aa full stack con las tecnologias que ello conlleva
Yo hubiera tirado por Kotlin y con Vert.x/Ktor si no te mola spring o yoquese
#38 Ya, por eso la idea es después rehacerlo con Spring, React y alguna más y tener las dos versiones en github.
Como mola!!
La cantidad de líneas que hay que escribir en java
He leido XMLHttpResponse, no piensas utilizar JSON como formato de intercambio entre tu front y back?
Dale duro al proyecto!!
Por cierto, para que piensas usar docker?
Y si necesitas ayuda con React no soy ningun experto pero seguro que en algo te puedo ayudar, asique cuenta conmigo!
Día 12
Día de intentar que los datos se pinten en el front. No lo he conseguido.
Esta es la función que tengo en mi JS (es un wip para ver que se pinta bien, no es definitivo):
function call() {
var data;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
data = JSON.parse(xhr.responseText);
}
};
xhr.open('POST', 'http://localhost:8081/cryptonite/cryptoprices', true);
xhr.send(
"{'service':'cryptopriceservice', 'start':'1','limit':'4','convert':'EUR'}"
);
xhr.onload = function () {
if (this.status == 200) {
var output = '';
for (var i in data) {
output +=
'<ul class="list-inline">' +
'<li><img src="images/' +
data[i].symbol +
'.png" /></li>' +
'<li id="coin-name">' +
data[i].name +
'</li>' +
'<li id="coin-symbol">' +
data[i].symbol +
'</li>' +
'<li id="coin-price">' +
data[i].price +
'</li>' +
'<li id="coin-percent-change">' +
data[i].percent_change +
'</li>' +
'</ul>';
}
console.log(data);
document.getElementById('coins').innerHTML = output;
}
};
}
Y esto es lo que devuelve la llamada, por si alguien quiere probarlo:
{
"data": [
{
"name": "Bitcoin",
"symbol": "BTC",
"currency": "EUR",
"price": "8.124,949",
"percent_change": "2,048",
"last_updated": "May 13, 2020, 12:37:00 AM"
},
{
"name": "Ethereum",
"symbol": "ETH",
"currency": "EUR",
"price": "174,209",
"percent_change": "1,163",
"last_updated": "May 13, 2020, 12:37:00 AM"
},
{
"name": "XRP",
"symbol": "XRP",
"currency": "EUR",
"price": "0,182",
"percent_change": "1,996",
"last_updated": "May 13, 2020, 12:37:00 AM"
}
]
}
El console.log()
pinta bien el contenido de data
. Vamos, que los datos vienen bien y en el formato correcto (antes lo estaba devolviendo con un JSON mal formado pero lo arreglé), pero luego, al iterar en el for, me da undefined. No tengo ni idea de por qué. Seguiré debugando a ver pero la ayuda es bienvenida.
Por qué no usas fetch
en vez de XMLHttpRequest
?
Y ya si quieres hacerlo molón del todo (ya que no quieres usar frameworks de ui):
https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry
https://developer.mozilla.org/es/docs/Web/API/Document/createElement
#46 Había contemplado usar fetch, sí, pero me decidí por XmlHttpRequest porque dentro de lo poco que sé de JS es lo que mejor controlo. Igual lo refactorizo antes de darlo por cerrado. Y lo de crear elementos custom me acaba de volar la cabeza, gracias!
#43 Wow, porque XMLHttpRequest y no un simple fetch? Alejate de eso.
https://developer.mozilla.org/es/docs/Web/API/Fetch_API/Utilizando_Fetch
Eso ya es de otra época, así se hace hoy en día en fetch:
con fetch es const response = await fetch( _URL_, {method : 'POST').then( response => response.json() ).catch(error => console.log(error))
En response tendrás el array con toda la data.
Ahi puedes hacer un response.ok en la primera resolución de la promesa (Si no devuelve 200) y el catch the pilla si directamente ni puede llegar a la url.
Eso si, tienes que aprender sobre promesas en JS
https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Usar_promesas
#48 Lo probaré una vez tenga todo en su sitio y lo porte a Spring, aprovecharé y usaré también fetch.
Día 13
Llevo todo el día, y lo que te rondaré, actualizando la partición de Ubuntu en la que llevaba ocho o nueve meses sin entrar. Entre eso y que está montada sobre un disco mecánico y no sobre el SSD voy a tardar la puta vida. Además el proceso tiene el updater bloqueado y no puedo ni instalarme el Gitkraken, así que me dedicaré a mirar vídeos del resto de cosas que tengo que usar.
Día 14
No cuento los días intermedios porque quedaría una numeración rara.
Bueno, tras unos días de ajetreos en casa (el coche, limpiezas varias, etc) la aplicación ya es funcional... en local. Está dockerizada junto con el mongo y se habn los tres (app, mongo y front) muy bien. Ahora queda la parte de ops y por supuesto pulir el front.
Próximas tareas
- Averiguar qué plan de qué proveedor de hosting me conviene más. Esto es una app de portfolio, no quiero gastarme demasiado y si es posible nada en absoluto.
- Registrar un dominio. Voy a aprovechar la coyuntura para hacerme una web personal y usarla para exponer los proyectos. Esa será la próxima tarea cuando acabe con esto, junto con un blog para hablar de cosas que me molan (juegos, libros, etc).
- Los caps de la API que uso suponen un single point of failure. En el tier gratuito tengo un soft cap de 333 llamadas diarias y un hard cap de 10.000 mensuales, supongo que con eso iré servido aunque se haga una llamada en el onload de la ventana, que a fin de cuentas es una aplicación de portfolio. Si veo que me quedo corto, que no creo, ya me inventaré algo, pero paso mil de pagar el siguiente tier que son 30 pavos al mes. El problema es que si me quedo corto mi aplicación no sirve para nada. Siempre puedo hacer yo una llamada cada cinco minutos y refactorizar el flujo para que vaya a buscar esos datos desde el front, es algo que tengo que contemplar como mejora de la aplicación.
Día 15
Voy a cambiar el flujo de la app.
Tenia pensado que el front llamara al servicio en una función en el window.onload
, pero es demasiado arriesgado y me puedo quedar sin llamadas si mi app, por lo que sea, tiene más de 10.000 visitas en un mes. No es que crea que vaya a llegar ni a la tercera parte de eso, pero parte del scope del proyecto es diseñarla pensando en todo lo que puede fallar.
Así que tengo tres opciones para presentar datos:
- Sigo como hasta ahora con el
window.onload
. Esta queda descartada. - Le pongo un botón para hacer la llamada. Estamos casi en las mismas y además quedaría horrible.
- Hago que sea el servicio el que llame a la API cada diez minutos y que el front cargue la última llamada en lugar de hacerla directamente.
Creo que la última opción es la más lógica. Así controlo yo en todo momento la cantidad de llamadas y tampoco creo que en tramos de diez minutos cambie demasiado la cotización de ninguna moneda.
Hoy me dedicaré a eso si me dejan un momento libre, que ayer estuvo complicada la cosa en ese aspecto.
Vale, después de un par de horillas ya está cambiado el flujo principal. Ahora el scheduler hace una llamada cada X tiempo a la API y popula la colección. Cuando el front llama al servicio éste va a esa colección, busca el registro más reciente y lo devuelve.
La parte buena de todo esto es que ahora tengo yo mi propio histórico de cotizaciones. El tier gratis de la API no soporta la llamada que devuelve el histórico de cada moneda, de manera que he matado dos pájaros de un tiro.
Lo de la frecuencia de la llamada del scheduler y unas cuantas cosas más las tengo ahora mismo hardcodeadas por comodidad pero obviamente tienen que salir de ahí. Y tampoco es plan de llenar el .properties
de gilipolleces como la cantidad de monedas que quiero traerme de la API o la currency que quiero buscar, claro, que ese no es su sitio. Y tampoco me mola tenerlo en ningún tipo de fichero de configuración. Al final me veo montándome una DB MySQL aparte para tener una sola tabla de configuración y traérmelo de allí, ya verás. Se aceptan sugerencias, por supuesto.
Eh, me rindo, no consigo conectar con mi instancia de Ubuntu en AWS. Me da timeout todas las veces y estoy poniendo bien el comando, pero no sé qué puede estar pasando. Sospecho que es un tema de permisos del .pem
, pero no estoy seguro.
Estoy usando MobaXTerm, por eso, y no hay manera. Nunca se me han dado bien estas cosas de ops.
Mañana más.
Edit: pues mañana no, hoy. Ya está, ya puedo conectar. Resulta que la instance no tenía habilitado por defecto el tráfico por SSH, se lo he tenido que añadir a manopla. Pero nada, que ya funciona.
Día 16
Cambiado el flujo principal de la aplicación.
Hasta ahora las peticiones a la API venían determinadas por el frontend, cada carga de la página disparaba una petición. A la larga hubiera sido insostenible. Ahora hay un job que cada cinco minutos llama a la API y persiste el resultado en una colección. El frontend, en cada carga de la página, recibe la última respuesta persistida.
Eso me ha solucionado tres problemas de golpe:
- No me pasaré nunca del hard cap del tier gratuito de la API porque sólo yo decido cuando se hace una llamada.
- He separado las funcionalidades de persistencia en DB y de procesado de peticiones de manera que ya no se hacen las dos seguidas. Eso me daba problemas porque hasta que no acababa de persistirse la respuesta no se enviaba al front y tardaba varios segundos en cargar la página.
- Tener un registro de cotizaciones a intervalos de cinco minutos me crea de manera accidental un histórico de cotizaciones que compensa el no tener acceso al de la API por limitaciones del tier gratuito.
Por lo demás, me he creado mi AWS EC2, he levantado un Ubuntu y he recordado por qué el MobaXTerm mola tanto. He instalado Docker y creado un mongo con persistencia para no perder datos si por lo que sea se para el contenedor.
Conclusiones
Hoy he aprendido una barbaridad de Docker, Mongo y AWS. Todavía me falta mucho que aprender, pero me está quedando una documentación personal que da gloria verla. También me he hecho una tarjeta de recarga con el banco para meterle dinero y pagar de ahí el server (y si lo llego a pensar antes el dominio también), así ya no me preocupa si alguna vez me paso de requisitos del tier gratuito.
Siguientes pasos
Montar integración contínua para desplegar el .war
y arrancar la aplicación. Aquí tengo varias opciones: TeamCity (que lo conozco porque ya monté uno una vez), Jenkins (que se usa mucho y queda bien en un CV), CircleCI (que es lo que siempre les leo usar a devs americanos y me produce curiosidad) y creo que alguna que otra más. Tengo que mirar cómo se montan Jenkins y CircleCI y decidir.
Montar una DB MySQL para guardar datos de configuración: nombres de usuario, passwords, cadenas de texto que no deban estar ni hardcodeadas ni en un .properties
(como las cadenas de conexión con el mongo, por ejemplo) y desplegarla en otro contenedor.
Día 17
En realidad son dos días, que es lo que llevo pegándome con AWS.
Creo que me he quedado corto de cpu y de ram en el EC2 que he montado. En cuanto le despliego el war (con CI o a mano via el manager de Tomcat) se me queda tostado y no responde. Voy a probar a ver si puedo seguir usando el tier gratuito con algo más potente.
Por lo demás, estoy aprendiendo un montón sopre ops. Hace una semana no tenía ni idea de docker, mongo, AWS y casi que ni me acordaba de usar la shell de linux y hoy hasta he editado ficheros con nano. Esto es una maravilla. Aunque no acabe saliendo nada de esto al menos puedo ir llenando ya CV.
A ver si mañana traigo mejores noticias porque vamos, está siendo frustrante.
Día 17, addendum
Al final lo he arreglado. Ahora tengo dos instancias:
- EC2 para mongo y lo que se tercie, tal vez MySQL en un futuro (de momento lo descarto).
- Elastic Beanstalk para despliegue de la aplicación (a mano, qué se le va a hacer).
Al final sí que se me quedaba muy corto el EC2 para montarle un Jenkins y un Tomcat, con sólo medio GB de ram y un solo core en la cpu no iba a ningún sitio, se me ponía todo al 100% y no podía ni entrar por SSH. Con lo que ahora tengo instalados ahí el Jenkins y el Tomcat para nada, pero bueno, no ocupan sitio apenas y no molestan.
Así que tengo un .war
desplegado en Elastic Beanstalk (y que tengo que subir desde mi máquina) que se conecta a un mongo en el EC2 para persistir y recuperar información. Una instancia más de las que quería hacer, pero bueno, al menos funciona. Una aplicación que cada diez minutos llama a la API y persiste los datos en el mongo.
Ahora el problema es que desde el Postman y desde el live server del VSC llama y recibe bien los datos y los pinta bien, pero desde mi hosting recibo un ERR_CONNECTION_REFUSED
. En mi EB tengo abiertos los puertos HTTP y HTTPS y desde mi ip no tengo problema, pero algo, no sé el qué, me falla al llamar desde mi hosting.
Pero si eso ya lo miro mañana. Porque en cuanto solucione ese problema la versión pasará a la 1.0 y sólo quedará añadir un par de detalles más para tenerla, como mínimo, enseñable. De momento voy a dejar toda la noche en marcha la aplicación para qeu vaya rellenando el mongo con valores para el histórico.
Siguientes pasos
- Arreglar el error al llamar desde el hosting.
- Modificar el scheduler para que llame dos veces a la API, una para recuperar la cotización en EUR y otra en USD.
- Eso significa que si ahora hago una llamada cada diez minutos tendré que hacer dos cada veinte.
- Modificar el front para añadir la opción de recuperar ambas monedas.
Día 18
Por partes:
Arreglar el error al llamar desde el hosting
No ha habido manera. No entiendo qué pasa, pero no puedo arreglarlo. He preguntado en el subreddit de AWS a ver si alguien sabe algo porque esto es rarísimo y desesperante, es lo último que me queda para tener la app en marcha. Por más que googlee no veo ninguna solución.
Modificar el scheduler para que llame dos veces a la API, una para recuperar la cotización en EUR y otra en USD.
Me llevó literalmente treinta segundos. Además ahora hago una llamada cada hora, no cada diez minutos. Mi EC2 descansa más y total, tampoco hace falta tanta muestra para el histórico.
Y en eso he estado hoy, dándome cabezazos con AWS y no entendiendo por qué en local y con Postman me va bien y con la web desplegada en el hosting no.
Usas servicios amazon para aprender no? Lo digo porque un vps creo que te hubiese simplificado todo...
#58 Sí, y también porque por lo que vi (aunque no indagué demasiado) el equivalente al tier gratis de AWS cuesta dinero en todos los proveedores de vps que encontré. Pero principalmente porque quería aprender a usarlo, veo que muchas ofertas lo ponen como bonus y como no me iba a costar un duro aprender...
Aunque al final no funcione y me tenga que rascar el bolsillo ya sólo con lo que llevo aprendido esta semana ha valido la pena los dolores de cabeza.
Día 19
La parte mala es que no he avanzado nada. La parte buena es que ya sé qué pasa.
El problema tiene toda la pinta de ser un tema de CORS. Me dio ese mismo error cuando levanté el servlet en mi tomcat local por primera vez y me tocó añadir la cabecera a la respuesta para poder verla en el Postman. El problema es que para modificar el Apache/Nginx de mi Elastic Beanstalk tengo que hacer una puta movida: añadir una carpeta al root del .war con un fichero dentro con sintaxis de yaml que añada lo que quiero al fichero de configuración. Y como siempre, hay setenta formas de hacerlo, algunas no funcionan, otras están deprecadas, etc. De hecho me he cargado completamente una instancia de mi EB modificando cosas y he tenido que recrear el entorno de nuevo, pero para eso está AWS.
Así que como hoy he intentado solucionarlo por la vía rápida (probando cosas sin pararme a ver qué hacía pensando que si se solucionaba ya me preocuparía después en entender qué había pasado) y no ha funcionado a partir de mañana me pondré a estudiar el tema y a entenderlo del todo, tanto el CORS como la configuración de Apache/Nginx y cómo se puede cambiar con un .config hasta que se lo pueda explicar a mi abuela.
Tiene cojones que la última pieza que me falta sea la que más está costando, pero bueno. Voy a llenar más curriculum de lo que pensaba, que es una parte buena.
Edit: a todo esto, ayer upgradeé el tier de Basic a Developer para poder abrir un ticket preguntando esto, ticket que todavía está en "Unassigned". 29 pavos, la pregunta más cara de mi vida XDDD