viernes, 15 de julio de 2022

[Whitepaper] Speed vs. Security: Protecting Modern Apps and APIs at the Pace of Modern Business

 

WHITEPAPER

https://interact.f5.com/rs/653-SMC-783/images/WP - Speed vs. Security Protecting Modern Apps and APIs at the Pace of Modern Business - 760x284.png

Hi Emanuel,

Customers, partners, and employees do not just demand the most from your technology-driven services, they expect it. DevOps, microservices, and containers can all help to deliver this much sought-after application agility. In this whitepaper you will learn that by integrating security early into the development process – shifting left – your organization can achieve both application and API agility and security, driving innovation while staying ahead of the competition.

In this whitepaper you will learn:

  • Why traditional security approaches are no longer suitable for your modern apps and APIs
  • Methods hackers use to target web applications, with 40% of attacks coming through APIs
  • How to achieve application speed and agility to ensure your business provides customers with the best possible experience
  • About integrating security early into the development lifecycle and automating policy so your business can remove bottlenecks, support shift left DevOps initiatives, and accelerate app velocity

martes, 12 de julio de 2022

Mónadas en Cats parte 17

cats.data.Writer es una mónada que nos permite llevar un registro junto con un cálculo. Podemos usarlo para registrar mensajes, errores o datos adicionales sobre un cálculo y extraer el registro junto con el resultado final.

Un uso común para Writers es registrar secuencias de pasos en cálculos de subprocesos múltiples donde las técnicas de registro imperativas estándar pueden generar mensajes intercalados de diferentes contextos. Con Writer, el registro para el cálculo está vinculado al resultado, por lo que podemos ejecutar cálculos simultáneos sin mezclar registros.

Un Writer[W, A] tiene dos valores: un registro de tipo W y un resultado de tipo A. Podemos crear un Writer a partir de valores de cada tipo de la siguiente manera:


import cats.data.Writer

import cats.instances.vector._ // for Monoid

Writer(Vector("It was the best of times","it was the worst of times"), 1859)

// res0: cats.data.WriterT[cats.package.Id, Vector[String], Int] = WriterT(

// (Vector("It was the best of times", "it was the worst of times"), 1859)

// )


Tenga en cuenta que el tipo informado en la consola es en realidad WriterT[Id, Vector[String], Int] en lugar de Writer[Vector[String], Int] como cabría esperar. En el espíritu de la reutilización de código, Cats implementa Writer en términos de otro tipo, WriterT. WriterT es un ejemplo de un nuevo concepto llamado transformador de mónadas, que trataremos más adelante

Tratemos de ignorar este detalle por ahora. Writer es un alias de tipo para WriterT, por lo que podemos leer tipos como WriterT[Id, W, A] como Writer[W, A]:

type Writer[W, A] = WriterT[Id, W, A]

Para mayor comodidad, Cats proporciona una forma de crear Writers especificando solo el registro o el resultado. Si solo tenemos un resultado, podemos usar la sintaxis pure estándar. Para hacer esto, debemos tener un Monoid[W] en el alcance para que Cats sepa cómo producir un registro vacío:

import cats.instances.vector._ // for Monoid

import cats.syntax.applicative._ // for pure

type Logged[A] = Writer[Vector[String], A]

123.pure[Logged]

// res1: Logged[Int] = WriterT((Vector(), 123))

Si tenemos un registro y ningún resultado, podemos crear un Writer[Unit] usando la sintaxis tell de cats.syntax.writer:

import cats.syntax.writer._ // for tell

Vector("msg1", "msg2", "msg3").tell

// res2: Writer[Vector[String], Unit] = WriterT(

// (Vector("msg1", "msg2", "msg3"), ())

// )

Si tenemos un resultado y un registro, podemos usar Writer.apply o podemos usar la sintaxis de Writer de cats.syntax.writer:

import cats.syntax.writer._ // for writer

val a = Writer(Vector("msg1", "msg2", "msg3"), 123)

// a: cats.data.WriterT[cats.package.Id, Vector[String], Int] = WriterT(

// (Vector("msg1", "msg2", "msg3"), 123)

// )

val b = 123.writer(Vector("msg1", "msg2", "msg3"))

// b: Writer[Vector[String], Int] = WriterT(

// (Vector("msg1", "msg2", "msg3"), 123)

// )

Podemos extraer el resultado y el registro de un Writer utilizando los métodos de valor y escrito respectivamente:

val aResult: Int = a.value

// aResult: Int = 123

val aLog: Vector[String] =a.written

// aLog: Vector[String] = Vector("msg1", "msg2", "msg3")

Podemos extraer ambos valores al mismo tiempo usando el método de ejecución:

val (log, result) = b.run

// log: Vector[String] = Vector("msg1", "msg2", "msg3")

// result: Int = 123

sábado, 9 de julio de 2022

Hacer un test de integración de un dao que guarda en Redis con Testcontainers y Spring boot


Me quedo relargo el titulo, pero la idea sería:

Tenemos una aplicación de spring boot y spring data que guarda información en Redis con un Dao y queremos probar este dao, ¿como lo hacemos?. Podemos usar testcontainers. 

Testcontainers es un framework que levanta una imagen de docker para que diferentes tecnologías esten accesibles para nuestros tests. 

Para utilizarlo debemos agregar esta dependencia en gradle o maven :


testImplementation "org.testcontainers:testcontainers:1.17.2"


Y en este ejemplo vamos a utilizar kotlin, pero funciona tambien con Java: 


@RunWith(SpringJUnit4ClassRunner::class)

@SpringBootTest(classes = [Application::class])

class EjemploRepositoryTest {


    @Autowired

    lateinit var ejemploRepository: EjemploRepository


    companion object {

        

        init {

            val redis : GenericContainer<Nothing>  = GenericContainer<Nothing>(DockerImageName.parse("redis:5.0.3-alpine"))

                .withExposedPorts(6379)


            redis.start()


            System.setProperty("redis.host", "${redis.host}")

            System.setProperty("redis.port", "${redis.firstMappedPort}")

        }

    }


    @Test

    fun `save and find all ejemplos`() {

        val count = ejemploRepository.findAll().count()

        val ejemplo = crearUnEjemplo() //este metodo te crea un ejemplo :D


        ejemploRepository.save(ejemplo)

        val ejemplos = ejemplo.findAll()


        Assert.assertNotNull(ejemplos)

        Assert.assertEquals(count + 1, ejemplos.count())

    }


}

En el código anterior falta el metodo crear ejemplo que va a crear el objeto para probar, no lo agrego porque no lo veo necesario. Por supuesto, el objeto ejemplo debe seguir con las notaciones de Spring data. 

Y listo! 

jueves, 7 de julio de 2022

Mónadas en Cats parte 16

Una propiedad útil de Eval es que sus métodos map y flatMap son trampolines. Esto significa que podemos anidar llamadas a map y flatMap arbitrariamente sin consumir  llamadas de la pila. Llamamos a esta propiedad "seguridad de pila". Por ejemplo, considere esta función para calcular factoriales:


def factorial(n: BigInt): BigInt = if(n == 1) n else n * factorial(n - 1)


Es relativamente fácil que la pila desborde:

factorial(50000)

// java.lang.StackOverflowError

//

...

Podemos reescribir el método usando Eval para hacerlo seguro:

def factorial(n: BigInt): Eval[BigInt] = if(n == 1) {

        Eval.now(n)

    } else {

        factorial(n - 1).map(_ * n)

    }

factorial(50000).value

// java.lang.StackOverflowError

//

...

¡Ups! Eso no funcionó, ¡nuestra pila aún explotó! Esto se debe a que aún estamos realizando todas las llamadas recursivas a factorial antes de comenzar a trabajar con el método de mapa de Eval. Podemos solucionar esto usando Eval.defer, que toma una instancia existente de Eval y difiere su evaluación. El método diferido es un trampolín como map y flatMap, por lo que podemos usarlo como una forma rápida de hacer que una pila de operaciones existente sea segura:


def factorial(n: BigInt): Eval[BigInt] = if(n == 1) {

        Eval.now(n)

    } else {

        Eval.defer(factorial(n - 1).map(_ * n))

    }

factorial(50000).value

// res: A very big value



martes, 5 de julio de 2022

¿Qué causó el aumento de la popularidad de la programación funcional?



Cuando comencé a dar clases de paradigmas de programación, la programación funcional solo se estudiaba con fines académicos o para la investigación, era poco usado en la industria. Pero esto ha cambiado. La industria ve con buenos ojos a este paradigma y está presente en todos los lenguajes de programación modernos. Para analizar este cambio debemos entender que es la programación funcional. 

 ¿Qué es la programación funcional? 

 Las principales características distintivas del desarrollo de software con Programación funcional son: 

  •     funciones puras; las cuales pueden componer nuevas funciones 
  •     evitar el estado compartido, datos mutables y efectos secundarios; 
  •     La prevalencia de un enfoque declarativo más que imperativo. 

Analicemos estos puntos más en detalle:  

funciones puras: Las funciones puras son funciones deterministas sin efectos secundarios. Una función determinista significa que, para el mismo conjunto de valores de entrada, devuelve el mismo resultado. Las propiedades de tales funciones son muy importantes: por ejemplo, las funciones puras tienen transparencia referencial, se puede reemplazar una llamada de función con su valor final sin cambiar el valor del programa. Y la composición de funciones se refiere al proceso de combinar dos o más funciones para crear una nueva función o realizar cálculos. 

evitar el estado compartido: El principal problema con los estados compartidos es que, para comprender los efectos de una función, debe conocer el historial completo de cada variable compartida que utiliza la función. Por lo tanto, la programación funcional evita estados compartidos, confiando en cambio en estructuras de datos inmutables y computación sin procesar para extraer nuevos datos de los existentes. Otro matiz que surge cuando se trabaja con estados compartidos es que cambiar el orden de las llamadas a funciones puede provocar una avalancha de errores. En consecuencia, al evitar estados compartidos, también evita este problema. 

Subyacente a toda la programación funcional está la inmutabilidad. Los objetos inmutables no se pueden cambiar en absoluto. Esto se logra mediante la congelación profunda de las variables. 

Con estas herramientas se evitan los efectos secundarios, los cuales significan que, además de devolver un valor, la función también interactúa con el estado mutable externo. ¿Por qué Programación funcional los evita? Porque de esta manera los efectos del programa son mucho más fáciles de entender y probar. Haskell, por ejemplo, usa mónadas para aislar los efectos secundarios de las funciones puras. 

enfoque declarativo: El punto es que el enfoque imperativo funciona según el principio de control de flujo y responde a la pregunta "cómo hacerlo". El enfoque declarativo describe el flujo de datos y responde a la pregunta "qué hacer". Además, el código imperativo a menudo se basa en instrucciones (operadores), mientras que el código declarativo se basa en expresiones. 

Ventajas 

Entonces, descubrimos qué es la programación funcional y qué necesitamos saber al respecto. Ahora veamos las ventajas de utilizarlo.   

Uno de los beneficios más obvios de la programación funcional son las abstracciones de alto nivel que ocultan muchos de los detalles de las operaciones de rutina, como la iteración. Debido a esto, el código es más corto y, como resultado, garantiza menos errores que se pueden cometer. 

Además, el Programación funcional contiene menos primitivas de lenguaje. Las clases simplemente no se usan: en lugar de crear una descripción única de un objeto con operaciones en forma de métodos, la programación funcional utiliza varias primitivas básicas del lenguaje que están bien optimizadas internamente. 

Además, la programación funcional permite al desarrollador acercar el lenguaje al problema, en lugar de viceversa, todo a expensas de las estructuras flexibles y la flexibilidad del lenguaje. Además, Programación funcional ofrece a los desarrolladores nuevas herramientas para resolver problemas complejos . 

De hecho, es demasiado largo para enumerar todas las ventajas de la programación funcional; realmente hay muchas de ellas. Puedo decir esto: trabajar con lenguajes funcionales proporciona una escritura de código precisa y rápida, facilita las pruebas y la depuración, los programas son de nivel superior y las firmas de funciones son más informativas. 

La programación funcional permite escribir código más conciso y predecible, y es más fácil de probar (aunque aprender no es fácil). 

Popularidad 

En el mundo de TI, nada sucede porque sí. Una cosa se aferra a la otra, y ahora todas las tendencias más actuales están interconectadas. 

Si recordamos las tendencias más sensacionales de 2016-2017, estas, por supuesto, serán AI, IoT, Big Data y Blockchain. Estaban y están en boca de todos, todos conocen su potencial y sus características claves. Y son algunas de estas tendencias las que han catalizado la creciente popularidad de la programación funcional entre los desarrolladores. 

Actualmente, el problema del procesamiento paralelo y el trabajo con grandes flujos de datos se ha incrementado notablemente. Y, paralelizando el procesamiento de estos datos, podemos obtener el resultado deseado en menor tiempo que con un procesamiento secuencial. Además, la informática descentralizada (distribuida): blockchains y otras, que, en esencia, son un mecanismo bastante complejo. Y para tales cálculos, el código funcional es más adecuado debido a todos los principios de la programación funcional (como las funciones puras, por ejemplo). El uso de todas las técnicas básicas facilita la ejecución y el mantenimiento de código paralelo y distribuido. 

Además, la programación funcional no solo se usaba para resolver problemas específicos, dado el incremento de su popularidad, ahora incluso se aplica a proyectos clásicos.  

Conclusión 

Como probablemente ya haya entendido, no debe temer a la programación funcional.  

 Aquí hay algunos consejos para aquellos que han decidido probarse en un nuevo paradigma y aprender algo radicalmente nuevo: 

Al principio será muy complicado, teniendo en cuenta que tendrás que dejar atrás lo que sabes y aprender nuevos enfoques y principios.  

  • Comience con micro tareas para tenerlo en sus manos. 
  • Si vienes de Java, scala es un gran aleado para empezar, podemos dividir nuestras soluciones y pensar parte en funcional, parte en objeto. Y en .net tenemos F# que es un gran lenguaje.  

La programación funcional tiene un crecimiento constante y es una opción real a problemas que con otros paradigmas no han encontrado una correcta solución.  


lunes, 4 de julio de 2022

Mónadas en Cats parte 15

Como todas las mónadas, los métodos map y flatMap de Eval agregan cálculos a una cadena. En este caso, sin embargo, la cadena se almacena explícitamente como una lista de funciones. Las funciones no se ejecutan hasta que llamamos al método de valor de Eval para solicitar un resultado:

val greeting = Eval.always{ println("Step 1"); "Hello" }.map{ str => println("Step 2"); s"$str world" }

// greeting: Eval[String] = cats.Eval$$anon$4@496b9f25

greeting.value

// Step 1

// Step 2

// res16: String = "Hello world"


Si bien se mantiene la semántica de las instancias de Eval de origen, las funciones de map siempre se llaman de forma perezosa bajo demanda:


val ans = for {

    a <- Eval.now{ println("Calculating A"); 40 }

    b <- Eval.always{ println("Calculating B"); 2 }

} yield { 

    println("Adding A and B")

    a + b

}

// Calculating A

// ans: Eval[Int] = cats.Eval$$anon$4@6e0e633

ans.value // first access

// Calculating B

// Adding A and B

// res17: Int = 42 // first access

ans.value // second access

// Calculating B

// Adding A and B

// res18: Int = 42


Eval tiene un método memoize que nos permite memorizar una cadena de cálculos. El resultado de la cadena hasta la llamada para memorizar se almacena en caché, mientras que los cálculos posteriores a la llamada conservan su semántica original:

val saying = Eval.always{ println("Step 1"); "The cat" }

  .map{ str => println("Step 2"); s"$str sat on" }.memoize

  .map{ str => println("Step 3"); s"$str the mat" }

// saying: Eval[String] = cats.Eval$$anon$4@77e677ee

saying.value // first access

// Step 1

// Step 2

// Step 3

// res19: String = "The cat sat on the mat" // first access

saying.value // second access

// Step 3

// res20: String = "The cat sat on the mat"


¿Que es Kotlin?

 


¿De qué se trata Kotlin? Es un nuevo lenguaje de programación dirigido a la plataforma Java. Kotlin es conciso, seguro, pragmático y se centra en la interoperabilidad con el código Java. Se puede usar en casi todos los lugares donde se usa Java hoy en día: para desarrollo del lado del servidor, aplicaciones de Android y mucho más. Kotlin funciona muy bien con todas las bibliotecas y frameworks Java existentes y se ejecuta con el mismo nivel de rendimiento que Java.

Comencemos con un pequeño ejemplo para demostrar cómo se ve Kotlin. Este ejemplo define una clase Person, crea una colección de personas, encuentra la más vieja e imprime el resultado. Incluso en este pequeño fragmento de código, puedes ver muchas características interesantes de Kotlin. Si quieres ejecutar este ejemplo, la opción más sencilla es utilizar https://play.kotlinlang.org/

data class Person(val name: String, val age: Int? = null)

fun main(args: Array<String>) {

   val persons = listOf(Person("Alice"), Person("Bob", age = 29))

   val oldest = persons.maxBy { it.age ?: 0 }

   println("The oldest is: $oldest")

}

// The oldest is: Person(name=Bob, age=29)

Que estamos haciendo aquí? Se declara una clase con dos propiedades: nombre y edad. La propiedad de edad es nula de forma predeterminada (si no se especifica). Al crear la lista de personas, omite la edad de Alice, por lo que se utiliza el valor predeterminado null. Luego usa la función maxBy para encontrar a la persona más vieja en la lista. La expresión lambda que se pasa a la función usa como el nombre predeterminado de ese parámetro el "it". El operador de Elvis (?:) devuelve cero si la edad es nula. Debido a que no se especifica la edad de Alice, el operador de Elvis la reemplaza con cero, por lo que Bob es el más viejo. 

En fin, un ejemplo vale más que mil palabras, y se puede ver que Kotlin cumple con ser conciso, seguro y pragmático.


sábado, 2 de julio de 2022

Mónadas en Cats parte 14

cats.Eval es una mónada que nos permite abstraernos sobre diferentes modelos de evaluación. Por lo general, hablamos de dos modelos de este tipo: eager y lazy, también llamados llamada por valor y llamada por nombre, respectivamente. Eval también permite memorizar un resultado, lo que nos brinda una evaluación de llamada por necesidad. Eval también es seguro para la pila, lo que significa que podemos usarlo en recursiones muy profundas sin explotar la pila.

¿Qué significan estos términos para modelos de evaluación? Veamos algunos ejemplos. Veamos primero Scala vals. Podemos ver el modelo de evaluación utilizando un cálculo con un efecto secundario visible. En el siguiente ejemplo, el código para calcular el valor de x ocurre en el lugar donde se define en lugar de acceder. Acceder a x recupera el valor almacenado sin volver a ejecutar el código.

val x = {

    println("Computing X")

    math.random

}

// Computing X

// x: Double = 0.0396922778013471

x // first access

// res0: Double = 0.0396922778013471

x // second access

// res1: Double = 0.0396922778013471


Este es un ejemplo de evaluación de llamada por valor:

• el cómputo se evalúa en el punto donde se define (eager); y

• el cálculo se evalúa una vez (se memoriza).

Veamos un ejemplo usando un def. El código para calcular y a continuación no se ejecuta hasta que lo usamos y se vuelve a ejecutar en cada acceso:


def y = {

    println("Computing Y")

    math.random

}

y // first access

// Computing Y

// res2: Double = 0.5270290953284378 

y // second access

// Computing Y

// res3: Double = 0.348549829974959


Estas son las propiedades de la evaluación llamada por nombre:

• el cálculo se evalúa en el punto de uso (lazy); y

• el cálculo se evalúa cada vez que se utiliza (no se memoriza).

Por último, pero no menos importante, los valores perezosos son un ejemplo de evaluación de llamada por necesidad. El código para calcular z a continuación no se ejecuta hasta que lo usamos por primera vez (perezoso). Luego, el resultado se almacena en caché y se reutiliza en accesos posteriores (memorizados):


lazy val z = {

    println("Computing Z")

    math.random

}

z // first access

// Computing Z

// res4: Double = 0.6672110951657263 // first access

z // second access

// res5: Double = 0.6672110951657263


Resumamos. Hay dos propiedades de interés:

• evaluación en el punto de definición (ansioso) versus en el punto de uso (perezoso); y

• los valores se guardan una vez evaluados (memorizados) o no (no memorizados).

Hay tres posibles combinaciones de estas propiedades:

• llamada por valor que está ansiosa y memorizada;

• llamada por nombre que es perezosa y no memorizada; y

• llamar por necesidad, que es perezoso y memorizado.

La combinación final, ansiosa y no memorizada, no es posible.


Eval tiene tres subtipos: Now, Always, y Later. Corresponden a llamada por valor, llamada por nombre y llamada por necesidad, respectivamente. Los construimos con tres métodos constructores, que crean instancias de las tres clases y las devuelven escritas como Eval:

import cats.Eval

val now = Eval.now(math.random + 1000)

// now: Eval[Double] = Now(1000.7009661848473)

val always = Eval.always(math.random + 3000)

// always: Eval[Double] = cats.Always@2a4e7955

val later = Eval.later(math.random + 2000)

// later: Eval[Double] = cats.Later@7684da18


Podemos extraer el resultado de un Eval usando su método de valor:


now.value

// res6: Double = 1000.7009661848473

always.value

// res7: Double = 3000.5158510235524

later.value

// res8: Double = 2000.6995448328964


Cada tipo de Eval calcula su resultado utilizando uno de los modelos de evaluación definidos anteriormente. Eval.now captura un valor ahora mismo. Su semántica es similar a un val: ansioso y memorizado:

val x = Eval.now{

println("Computing X")

math.random

}

// Computing X

// x: Eval[Double] = Now(0.6969571260771719)

x.value // first access

// res10: Double = 0.6969571260771719 // first access

x.value // second access

// res11: Double = 0.6969571260771719


Eval.always captura un cálculo perezoso, similar a una definición:


val y = Eval.always{

println("Computing Y")

math.random

}

// y: Eval[Double] = cats.Always@6d355284

y.value // first access

// Computing Y

// res12: Double = 0.8575236846076497 // first access

y.value // second access

// Computing Y

// res13: Double = 0.15716382484701563


Finalmente, Eval.later captura un cálculo memorizado perezoso, similar a un valor perezoso:


val z = Eval.later{

println("Computing Z")

math.random

}

// z: Eval[Double] = cats.Later@3429dabc

z.value // first access

// Computing Z

// res14: Double = 0.5149108999064906 // first access

z.value // second access

// res15: Double = 0.5149108999064906



viernes, 1 de julio de 2022

[eBook] The Secret to Modern Application Security

 

EBOOK

[eBook] The Secret to Modern Application Security

Hi Emanuel,

Every business strives for sustained agility. Many see the benefits of a distributed microservices architecture, using modern applications and APIs to keep pace with the competition and customer demand. In fact, 85% of new workloads are deployed in containers and 83% of internet traffic is made up of API calls. In this eBook you will learn how NGINX App Protect WAF can be deployed with NGINX Ingress Controller to protect your apps and APIs in Kubernetes clusters, enforce security policies, inspect traffic, and eliminate threats before they cross the perimeter, allowing you to innovate at speed without compromising on security.

In this eBook you will learn:

  • Why integrating security automation into your CI/CD pipeline is key to the agile software development of your applications and APIs
  • Ways the performance of your application directly impacts customer experience and sales
  • About implementing strong security into an application’s operating system using NGINX App Protect WAF, which supports multiple modern application deployment topologies
  • How NGINX App Protect WAF works with NGINX Ingress Controller as the gatekeeper for an entire Kubernetes cluster and supports enforcing policies at a granular level within the cluster

martes, 28 de junio de 2022

Mónadas en Cats parte 13

Cats proporciona una class type adicional llamada MonadError que abstrae los tipos de datos similares a los que se usan para el manejo de errores. MonadError proporciona operaciones adicionales para generar y manejar errores.

package cats

trait MonadError[F[_], E] extends Monad[F] {

// Lift an error into the `F` context:

def raiseError[A](e: E): F[A]

// Handle an error, potentially recovering from it:

def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]

// Handle all errors, recovering from them:

def handleError[A](fa: F[A])(f: E => A): F[A]

// Test an instance of `F`,

// failing if the predicate is not satisfied:

def ensure[A](fa: F[A])(e: E)(f: A => Boolean): F[A]

}


MonadError se define en términos de dos parámetros de tipo:

• F es el tipo de la mónada;

• E es el tipo de error contenido en F.

Para demostrar cómo encajan estos parámetros, aquí hay un ejemplo en el que instanciamos la clase de tipo Either:


import cats.MonadError

import cats.instances.either._ // for MonadError

type ErrorOr[A] = Either[String, A]

val monadError = MonadError[ErrorOr, String]


Los dos métodos más importantes de MonadError son raiseError y handleErrorWith. raiseError es como el método puro para Monad excepto que crea una instancia que representa una falla:


val success = monadError.pure(42)

// success: ErrorOr[Int] = Right(42)

val failure = monadError.raiseError("Badness")

// failure: ErrorOr[Nothing] = Left("Badness")


handleErrorWith es el complemento de raiseError. Nos permite consumir un error y (posiblemente) convertirlo en un éxito, similar al método de recuperación de Future:


monadError.handleErrorWith(failure) {

case "Badness" =>

monadError.pure("It's ok")

case _ =>

monadError.raiseError("It's not ok")

}

// res0: ErrorOr[String] = Right("It's ok")


Si sabemos que podemos manejar todos los errores posibles, podemos usar handleWith.


monadError.handleError(failure) {

case "Badness" => 42

case _ => -1

}

// res1: ErrorOr[Int] = Right(42)


Hay otro método útil llamado ensure que implementa un comportamiento similar al de un filtro. Probamos el valor de una mónada exitosa con un predicado y especificamos un error para generar si el predicado devuelve falso:


monadError.ensure(success)("Number too low!")(_ > 1000)

// res2: ErrorOr[Int] = Left("Number too low!")


Cats proporciona instancias de MonadError para numerosos tipos de datos, incluidos Either, Future y Try. La instancia de Either se puede personalizar para cualquier tipo de error, mientras que las instancias de Future y Try, siempre representan errores como Throwables:


import scala.util.Try

import cats.instances.try_._ // for MonadError

val exn: Throwable = new RuntimeException("It's all gone wrong")

exn.raiseError[Try, Int]

// res6: Try[Int] = Failure(java.lang.RuntimeException: It's all gone wrong)

sábado, 25 de junio de 2022

Resultado de la encuesta 2022 de StackOverflow

StackOverflow hace una encuesta cada año a los desarrolladores y es un buen termómetro para ver como va evolucionando la industria: 

Cosas a destacar o que me llamaron la atención:

Lenguajes de programación más usados: Javascript, Python, Typescript y Java. Acá me llamo la atención que Typescript le gane a java. 


Base de datos: Mysql, postgresql, SQLite, mongodb. SQLite le gana a mongo, eso es raro.


Más amados y más odiados aparezca Delphi


Y por último entre los lenguajes mejor pagos, hay 6 lenguajes funcionales (no me llamo la atención pero lo quiero marcar) :  

Y te llamo la atención algo más? 

Dejo link: 

https://survey.stackoverflow.co/2022/?utm_source=so-owned&utm_medium=announcement-banner&utm_campaign=dev-survey-2022&utm_content=results

jueves, 23 de junio de 2022

Agregar código java a un proyecto Clojure en Leiningen


 Quiero hacer unas clases en java y utilizarlas en mis funciones clojure. Es muy fácil, tengo que agregar   :java-source-paths a mi proyecto y ya toma las clases.  

Veamos un ejemplo

(defproject ejemplo "0.1.0-SNAPSHOT"

  :description "FIXME: write description"

  :url "http://example.com/FIXME"

  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"

            :url "https://www.eclipse.org/legal/epl-2.0/"}

  :dependencies [[org.clojure/clojure "1.10.3"],

                 ...                 ]

  :java-source-paths ["src-java"]

  :main com.assembly.ejemplo.core

  :aot :all

  :target-path "target/%s"

  :profiles {:uberjar {:aot :all

                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})


Y listo!!

martes, 21 de junio de 2022

Como chequear si un registro esta siendo cacheado por Hibernate en una aplicación Spring boot.


Necesitaba ver si un registro estaba siendo cacheado por hibernate es decir JPA, en una aplicación spring boot. 

Hice lo que cualquiera hubiera hecho, puse show-sql en true, corrí un test que busca 2 veces el registro y me fije en el log que no hubiera 2 selects. Fácil pero poco científico, estaría bueno hacer un test que pruebe esto, y bueno hice esto : 


@RunWith(SpringJUnit4ClassRunner::class)

@SpringBootTest(classes = [Application::class])

class EjemploRepositoryTest {


    @Autowired

    lateinit var ejemploRepository: EjemploRepository


    @Autowired

    lateinit var entityManagerFactory: EntityManagerFactory


    @Test

    fun `find a Ejemplo from Empty cache`() {

        val ejemplo= getEjemplo()

        val cache = entityManagerFactory.cache

        Assert.assertFalse(cache.contains(Ejemplo::class.java, ejemplo.id))

    }


    @Test

    fun `find a Ejemplo from cache`() {

        val ejemplo= getEjemplo()

        saveAndFind(ejemplo)


        val cache = entityManagerFactory.cache


        Assert.assertTrue(cache.contains(Ejemplo::class.java, ejemplo.id))

    }


    private fun saveAndFind(ejemplo: Ejemplo): Ejemplo{

        ejemploRepository.save(ejemplo)

        return ejemploRepository.findById(ejemplo.id).get()

    }


    private fun getEjemplo(): Ejemplo{

        return  ... //Aca construimos un ejemplo 

    }


}

Y listo!!

lunes, 20 de junio de 2022

Mónadas en Cats parte 12

Either se usa típicamente para implementar el manejo de errores rápidos. Secuenciamos los cálculos utilizando flatMap como de costumbre. Si un cálculo falla, los cálculos restantes no se ejecutan:

for {

a <- 1.asRight[String]

b <- 0.asRight[String]

c <- if(b == 0) "DIV0".asLeft[Int]

else (a / b).asRight[String]

} yield c * 100

// res21: Either[String, Int] = Left("DIV0")

Cuando usamos Either para el manejo de errores, necesitamos determinar qué tipo queremos usar para representar los errores. Podríamos usar Throwable para esto:


type Result[A] = Either[Throwable, A]


Esto nos da una semántica similar a scala.util.Try. El problema, sin embargo, es que Throwable es un tipo extremadamente amplio. No tenemos (casi) idea de qué tipo de error ocurrió.

Otro enfoque es definir un tipo de datos algebraicos para representar los errores que pueden ocurrir en nuestro programa:

object wrapper {

   sealed trait LoginError extends Product with Serializable

   final case class UserNotFound(username: String) extends LoginError

   final case class PasswordIncorrect(username: String) extends LoginError

   case object UnexpectedError extends LoginError

}; 

import wrapper._


case class User(username: String, password: String)

type LoginResult = Either[LoginError, User]


Este enfoque resuelve los problemas que vimos con Throwable. Nos da un conjunto fijo de tipos de errores esperados. También obtenemos la seguridad de la comprobación exhaustiva de cualquier coincidencia de patrones que hagamos:

// Choose error-handling behaviour based on type:

def handleError(error: LoginError): Unit =

    error match {

        case UserNotFound(u) => println(s"User not found: $u")

        case PasswordIncorrect(u) => println(s"Password incorrect: $u")

        case UnexpectedError => println(s"Unexpected error")

    }

val result1: LoginResult = User("dave", "passw0rd").asRight

// result1: LoginResult = Right(User("dave", "passw0rd"))

val result2: LoginResult = UserNotFound("dave").asLeft

// result2: LoginResult = Left(UserNotFound("dave"))

result1.fold(handleError, println)

// User(dave,passw0rd)

result2.fold(handleError, println)

// User not found: dave


sábado, 18 de junio de 2022

Crear un proyecto Spring boot, con Leiningen y Clojure


La idea es crear un proyecto spring boot, con Clojure y Leiningen. (ya lo dice el titulo)

Primero hacemos un proyecto hello word con Leiningen (ojo tienen que tener Leiningen instalado, esta bueno instalarlo con sdkman, así :  sdk install leiningen) 

lein new app ejemplo

Luego agregamos las dependencias de spring boot al proyecto, para que quede algo así: 

(defproject github "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.3"],
                 [org.springframework.boot/spring-boot-starter-web "2.6.7"],
                 [org.springframework.boot/spring-boot-configuration-processor "2.6.7"]]
  :main com.assembly.example.core
  :aot :all
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Ojo, tambien hay que agregar :aot y :all 

Ahora hacemos el Application, yo modifique el hello wold que vino por default, pero si lo hacen tengan cuidado de ponerlo en un paquete. 

(ns com.assembly.example.core
  (:import (org.springframework.boot SpringApplication))
  (:gen-class
    :name ^{org.springframework.boot.autoconfigure.SpringBootApplication []
            org.springframework.context.annotation.ComponentScan {:basePackages ["com.assembly.example"]}}
    com.assembly.example.core.Application
    :main true)
  )

(defn -main []
  (SpringApplication/run (Class/forName "com.assembly.example.core.Application") (into-array String '()))
  )

Y ahora hacemos un endpoint, en este ejemplo voy a hacer un hola mundo : 

(ns com.assembly.exanple.controller.greet

  (:import (org.springframework.web.bind.annotation PathVariable RequestMapping RequestMethod RestController)

           (org.springframework.http ResponseEntity))

  )


(gen-class

  :name ^{RestController {}

          RequestMapping {:value ["/v1/greet"]}} com.assembly.exanple.controller.greet.GreeterEndPoint

  :methods [[^{RequestMapping {:value ["/hello"]

                               :method [RequestMethod/GET]}} sayHello [] Object ]

            [^{RequestMapping {:value ["/helloto/{name}"]

                               :method [RequestMethod/GET]}} sayHelloTo [^{PathVariable {:value "name"}} String] Object ]

            ]

  :state injected

  :init init

)


(defn -init

  "Initialize the class by setting the state to an empty map, which can be populated with injected dependencies."

  []

  [[] (atom {})])


(defn -sayHello

  [this]

  (ResponseEntity/ok "Hello"))


(defn -sayHelloTo

  [this name]

  (ResponseEntity/ok (str "Hello " name)))


Y listo, si corremos el main, vamos a levantar el tomcat embebido de spring y vamos a : 

http://localhost:8080/v1/greet/hello

y veremos como nos saluda spring boot con clojure!!