Weblogs Código

Fixed Buffer

La potencia de la Reflexión en C# (Parte 2: Ensamblados)

junio 25, 2019 08:00

La imagen muestra el logo de C# para Reflexión Ensamblados

En la última entrada hablábamos sobre un problema que me había encontrado en el trabajo y como la reflexión me había permitido resolverlo de una manera elegante y con poco código. Al menos, muchísimo menos código del que habría necesitado sin haberla usado.

Pensaba hacer simplemente un par de entradas hablando sobre el tema, pero creo que tiene bastante miga, así que lo mejor va a ser hacer una pequeña serie hablando de todas las posibilidades que nos ofrece esta poderosa herramienta. Así que hoy vamos a empezar con la primera de la lista y la que a su vez las contiene a todas: «Assembly».

Assembly (ensamblado): Los ensamblados son los bloques de creación de las aplicaciones .NET Framework; constituyen la unidad fundamental de implementación, control de versiones, reutilización, ámbitos de activación y permisos de seguridad. Un ensamblado es una colección de tipos y recursos compilados para funcionar en conjunto y formar una unidad lógica de funcionalidad. Los ensamblados proporcionan a Common Language Runtime la información necesaria para conocer las implementaciones de tipos. Para la ejecución, un tipo no existe fuera del contexto de un ensamblado.

Fuente: MSDN

¿Y que es un assembly? Pues dicho con palabras sencillas, es la unidad mínima que el CLR puede ejecutar. Contiene las los módulos, las clases, los tipos… y lo más importante, el manifiesto, que es donde se registran todos los metadatos. (Cuando hablemos de los módulos veremos que la principal diferencia es esta última). Cuando una aplicación arranca, el CLR consulta los metadatos del ensamblado para conocer el punto de entrada a este. Él

static void Main(string[] args)

de toda la vida. Mediante reflexión, podemos obtener esos metadatos de el/los ensamblados cargados en la aplicación, pudiendo saber que versión de fichero estamos ejecutando, obtener una lista detallada de todas las clases y métodos que están disponibles en nuestra aplicación, o incluso cargar nuevos ensamblados en nuestra aplicación y permitir que estén disponibles de manera dinámica.

Ejemplos de uso para metadatos

Por ejemplo, un código como este nos permitiría saber la versión que estamos ejecutando:

using System;
using System.Reflection;

namespace PostReflexion
{
    class Program
    {
        static void Main(string[] args)
        {
            var assembly = Assembly.GetAssembly(typeof(Program));
            Console.WriteLine($"Versión: {assembly.GetName().Version}");
        }
    }
}

U obtener todos los tipos (clases, interfaces, enumeraciones….):

using System;
using System.Reflection;

namespace PostReflexion
{
    class Program
    {
        static void Main(string[] args)
        {
            var assembly = Assembly.GetAssembly(typeof(Program));
            foreach (var type in assembly.DefinedTypes)
            {
                Console.WriteLine(type);
            }
        }
    }
}

-Vale, ¿y eso para qué vale? ¡Tampoco es una grandísima utilidad…!

En si mismo, lo visto anteriormente no es de una gran utilidad, ya que para obtener la versión podríamos utilizar «FileVersionInfo» y saber el contenido de un ensamblado en tiempo de ejecución no nos vale de mucho… ¿O sí?

Una herramienta muy potente que nos da la clase Assembly, es que nos permite crear objetos de manera dinámica, es decir, durante el la ejecución del programa, y sin estar explícitamente escrito en nuestro código.

Imagina que tienes una aplicación que sirve de punto de lanzador de otra serie de aplicaciones contenidas en librerías. A simple vista, tienes 3 opciones:

  • Distribuir la aplicación con todas su dll referenciadas, haciendo que gente que no ha pagado por todas las aplicaciones se tenga que descargar un montón de ficheros que no va a poder usar. (Mayor tamaño de archivos)
  • Generar diferentes soluciones que utilicen unas u otras librerías para generar diferentes paquetes de aplicaciones, pero esto crece de manera exponencial con el número de librerías y tipos de «paquete». (Mayor trabajo de mantenimiento)
  • Listar las librerías disponibles dentro del directorio, obtener de ahí las clases que inician las diferentes aplicaciones y ejecutarlas de manera dinámica.

Nota: Cualquiera de las 3 opciones debería contar con un sistema de autenticación que acredite que realmente tiene derecho a utilizar la aplicación.

Creando instancias de manera dinámica

Para ello, aprovechando la opción que nos da la reflexión de instanciar clases, podríamos hacer algo como esto:

var assembly = Assembly.LoadFile("ruta a la librería");
            
//Creamos el objeto de manera dinámica
var formDinamico = assembly.CreateInstance("Nombre completo de la clase") as Form;

//Si hemos podido crear la instancia, abrimos el formulario
formDinamico?.ShowDialog();

Con este código y gracias a assembly, solo cambiando las rutas de cargar la librería y el nombre de la clase, podemos abrir cualquier formulario que herede de Form (WinForms). Esto mismo se puede aplicar a WPF sin ninguna dificultad.

Además, también es posible crear instancias de clases que tienen un constructor con argumentos, pasándole esos argumentos:

//Código de la clase
//=============================================
public class ClaseEjemplo
{
    private int _valor;
    public ClaseEjemplo(int valor)
    {
        _valor = valor;
    }
    public int Multiplicar(int por)
    {
        Console.WriteLine($"Llamada a {nameof(Multiplicar)} con parámetro {por}");
        return _valor * por;
    }
}

//Código para instanciar y ejecutar sus métodos
//=============================================
var assembly = Assembly.LoadFile("ruta a la librería");
//Creamos el objeto de manera dinámica
var objetoDinamico = assembly.CreateInstance("PostReflexion.ClaseEjemplo"
                               ,false
                               ,BindingFlags.ExactBinding
                               ,null
                               ,new object[]{2} //Contructor
                               ,null  
                               ,null);

// Creamos una referencia al método   
var m = objetoDinamico.GetType().GetMethod("Multiplicar");

//Llamamos al método pasandole el objeto creado dinámicamente y los argumentos dentro de un object[]
var ret = m.Invoke(objetoDinamico, new object[] { 3 });
Console.WriteLine($"El retorno de la función es: {ret}");
Console.WriteLine();

No vamos a entrar a ver como hemos llamado a su método, ya que eso lo veremos cuando lleguemos a esa parte, pero el código es funcional y podemos comprobar como hemos instanciado de manera dinámica la clase y le hemos pasado un valor a su constructor, pudiéndolo comprobar al llamar al método.

Estas son solo alguna de las muchísimas opciones que nos da la reflexión en C# a través de la clase Assembly. Combinándola con las demás que vamos a ver, nos permite ejecutar cualquier código de manera dinámica en tiempo de ejecución, lo cual es una herramienta muy potente y que abre un gran abanico de posibilidades.

He actualizado el repositorio de GitHub para añadir los ejemplos de obtención de metadatos y creación de clases de manera dinámica para poder probar esos conceptos.

**La entrada La potencia de la Reflexión en C# (Parte 2: Ensamblados) se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Variable not found

Etiquetado de consultas en Entity Framework Core

junio 25, 2019 07:08

Entity Framework CoreComo sabemos, las consultas que definimos mediante LINQ contra conjuntos de datos de Entity Framework son traducidas automáticamente a queries SQL, que es lo que finalmente ejecuta el servidor de base de datos.

Muchas veces estas sentencias SQL generadas de forma automática y ejecutadas al servidor son fáciles de leer y entender, pero hay veces que EF traduce el LINQ a consultas SQL enormes, complejas, con escasa legibilidad y difícilmente reconocibles.

Seguro que alguna vez habéis tenido por delante una de estas complejas sentencias SQL generada por Entity Framework y os hubiera gustado saber en qué punto del código fue lanzada. Esto es muy frecuente, por ejemplo, cuando estamos monitorizando las consultas en ejecución con SQL Profiler, o al examinar las queries que consumen mayor número de recursos desde los paneles de Azure SQL.

En versiones "clásicas" de Entity Framework había que ingeniárselas para conseguirlo, pero, como podréis comprobar a continuación, en EF Core la cosa se ha simplificado bastante :)

Actualizado el 25-Jun-2019: si buscas cómo conseguir algo parecido en EF6, puedes echar un vistazo a este post.

Etiquetando consultas

Cuando creamos consultas usando SQL a pelo, es bastante sencillo incluirles una marca que, a posteriori, nos ayude a identificar su origen o finalidad. Basta con insertar un comentario como en el siguiente ejemplo:
var sql = @"
-- Get top 10 older friends with country

SELECT TOP 10 [friend].[Name] AS [FriendName], [friend].[Age],
[friend.Country].[Name] AS [CountryName]
FROM [Friends] AS [friend]
LEFT JOIN [Countries] AS [friend.Country] ON [friend].[CountryId] = [friend.Country].[Id]
ORDER BY [friend].[Age] DESC";

context.Database.ExecuteSqlCommand(sql);
De esta forma, cuando veamos aparecer dicha consulta en los logs del servidor, podremos identificar rápidamente de qué se trata.

Pues bien, la solución planteada por Entity Framework Core 2.2 es igualmente sencilla, o incluso más ;) Basta con introducir una llamada a TagWith() en la especificación de la consulta LINQ, suministrándole la etiqueta que deseamos añadir a la misma, como se muestra a continuación:
var query = context.Friends
.OrderByDescending(friend => friend.Age)
.Take(10)
.Select(friend =>
new { FriendName = friend.Name, friend.Age, CountryName = friend.Country.Name })
.TagWith("Get top 10 older friends with country");
Y la consulta que enviaremos a la base de datos será la siguiente:
-- Get top 10 older friends with country

SELECT TOP(@__p_0) [friend].[Name] AS [FriendName], [friend].[Age],
[friend.Country].[Name] AS [CountryName]
FROM [Friends] AS [friend]
LEFT JOIN [Countries] AS [friend.Country] ON [friend].[CountryId] = [friend.Country].[Id]
ORDER BY [friend].[Age] DESC
Mola, ¿eh?

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Etiquetado de consultas en Entity Framework 6

junio 25, 2019 06:05

Entity FrameworkPues hablábamos hace unos días del extensor TagWith() de Entity Framework Core que, como recordaréis, venía de perlas para incluir un comentario en las consultas SQL enviadas al servidor de datos para poder identificarlas a posteriori:
LINQ
============================================
var query = context.Friends
.OrderByDescending(friend => friend.Age)
.Take(10)
.Select(friend =>
new { FriendName = friend.Name,
friend.Age,
CountryName = friend.Country.Name
})
.TagWith("Get top 10 older friends with country");

Generated SQL
============================================
-- Get top 10 older friends with country

SELECT TOP(@__p_0) [friend].[Name] AS [FriendName], [friend].[Age],
[friend.Country].[Name] AS [CountryName]
FROM [Friends] AS [friend]
LEFT JOIN [Countries] AS [friend.Country]
ON [friend].[CountryId] = [friend.Country].[Id]
ORDER BY [friend].[Age] DESC
Mientras escribía el post, pensaba que quizás sería interesante poder utilizar esta misma idea en Entity Framework 6, porque, al fin y al cabo, todos tenemos por ahí muchas aplicaciones en producción que continúan delegando en este marco de trabajo su acceso a datos. Tras una búsqueda en Google y no encontrar gran cosa, pensé que tampoco sería tan difícil de implementar algo que diera el apaño...

1. Enfocando la solución

Manos a la obra. Conceptualmente, lo que pretendemos hacer es bastante sencillo:
  • Queremos que, en cualquier punto de una consulta LINQ, un desarrollador pueda invocar el extensor TagWith() y especificar una etiqueta personalizada para la misma.

  • A la hora de generar el SQL de la consulta, tenemos que recuperar el valor de dicha etiqueta e insertarla a modo de comentario justo antes del SELECT, como en el ejemplo que veíamos anteriormente.
Seguro que existen muchas formas de implementar algo así, pero mi intención no es meterme en fregados importantes como podría ser la escritura de un proveedor personalizado, o bucear entre los oscuros misterios de la implementación interna de Entity Framework. Necesitamos algo más sencillo, quizás algo hacky, pero que funcione :)

Por ello, la solución propuesta consiste en:
  • Crear un método extensor TagWith() sobre IQueryable, que añadirá a la consulta una "marca" fácilmente identificable para detectar a posteriori que ésta incluye una etiqueta personalizada.

  • A continuación, usando los interceptores de Entity Framework 6, descubrir las queries en las que haya sido introducida la "marca" anterior, extraer la etiqueta y reformular la sentencia SQL para incluir dicha etiqueta como comentario.
Con algo más de detalle, y expresado de forma práctica, la cosa quedaría así:
// 1: Consulta inicial (C#)
var query = _ctx.Friends
.TagWith("Hello world!")
.Where(f => f.Id < 10);

// 2: La llamada a TagWith() añade un predicado predefinido a la consulta,
// de forma que es reescrita de la siguiente manera:
var query = _ctx.Friends
.Where(f=> "!__tag!" == "Hello world")
.Where(f => f.Id < 10);

// 3: Debido a lo anterior, el proveedor genera la siguiente consulta SQL:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Country_Id] AS [Country_Id]
FROM [dbo].[Friends] AS [Extent1]
WHERE (N'!__tag!' = N'Hello world!') AND ([Extent1].[Id] < 10)

// 4: En un interceptor de EF detectamos la subcadena "!__tag!", por lo que sabemos
// que se trata de una consulta etiquetada. Manipulando un poco la cadena,
// transformamos la consulta SQL en la siguiente, donde se incluye la etiqueta como
// comentario y ha desaparecido el predicado falso introducido con TagWith():
-- Hello world!
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Country_Id] AS [Country_Id]
FROM [dbo].[Friends] AS [Extent1]
WHERE ([Extent1].[Id] < 10)
Veamos ahora cómo implementar todo esto.

2. Implementación del extensor TagWith()

Esta parte es la más sencilla. El extensor TagWith() simplemente debe añadir al IQueryable<T> una nueva cláusula where fácilmente reconocible a posteriori:
public static class TagWithExtensions
{
public static string TagMarker = "!__tag!";

public static IQueryable<T> TagWith<T>(this IQueryable<T> query, string tag)
{
var tagConstant = Expression.Constant(TagMarker);
var tagValue = Expression.Constant(tag);
var equalsExpression = Expression.Equal(tagConstant, tagValue);
var predicate = Expression.Lambda<Func<T, bool>>(
equalsExpression, Expression.Parameter(typeof(T))
);
return query.Where(predicate);
}
}
Como se observa en el código anterior, generamos un predicado construyendo un árbol de expresión en el que comparamos dos constantes, "!__tag!" y la etiqueta indicada en la llamada al método.

El resultado de dicha comparación será siempre falso, pero en realidad esto no nos importa porque nunca irá al servidor de datos: eliminaremos este predicado de la sentencia SQL generada justo antes de ejecutarse. Veamos cómo.

3. Modificando la consulta

Entity Framework 6 incluye un interesante mecanismo que nos permite consultar y modificar el SQL generado por el proveedor antes de que sea enviado al servidor de datos: los interceptores de comandos.

Estos componentes, que implementan la interfaz IDbCommandInterceptor, permiten tomar el control antes de ejecutar una consulta para analizar o cambiar el SQL generado. Nosotros nos valdremos de ello para determinar si estamos ante una consulta etiquetada y, en su caso, modificarla para adaptarla a nuestros intereses.
public class QueryTaggerInterceptor : DbCommandInterceptor
{
private readonly ISqlTagger _sqlTagger;

public QueryTaggerInterceptor(ISqlTagger sqlTagger)
{
_sqlTagger = sqlTagger;
}

public override void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> context)
{
do
{
command.CommandText = _sqlTagger.GetTaggedSqlQuery(command.CommandText);
}
while (command.CommandText.IndexOf(
TagWithExtensions.TagMarker,
StringComparison.Ordinal) > -1);
}
}
Ese componente de tipo ISqlTagger que usamos en el interceptor simplemente contiene la operación GetTaggedSqlQuery(), que recibe la cadena de texto con la consulta SQL y la retorna modificada según la siguiente lógica:
  • Si la SQL no contiene la marca "!__tag!", es que estamos ante una consulta no etiquetada, por lo que la dejamos tal cual. En caso contrario, asumimos que la consulta está tageada, por lo que continuamos el procedimiento.

  • Extraemos la etiqueta indicada por el usuario en la llamada a TagWith().

  • Eliminamos de la SQL el predicado completo.

  • Y, por último, fijaos que el proceso se repite siempre qe sigamos encontrando marcas en la consulta, puesto que un desarrollador podría haber incluido más de una llamada a Tagwith() sobre la misma.
La definición de la interfaz ISqlTagger es la siguiente:
public interface ISqlTagger
{
string GetTaggedSqlQuery(string sql);
}
El motivo de utilizar una interfaz es que el formato de la consulta SQL podría variar de un proveedor a otro debido a matices sintácticos, y de esta forma podríamos soportar en el futuro distintas implementaciones. Por ejemplo, el siguiente código muestra cómo sería el taggeador para SQL Server:
public class SqlServerTagger: ISqlTagger
{
public string GetTaggedSqlQuery(string sql)
{
public string GetTaggedSqlQuery(string sql)
{
var indexOfTagConstant = sql.IndexOf(
TagWithExtensions.TagMarker, StringComparison.Ordinal
);
if (indexOfTagConstant == -1)
return sql;

var predicateStartIndex = indexOfTagConstant - 2;
var startOfTagIndex = predicateStartIndex + TagWithExtensions.TagMarker.Length + 8;

var predicateEndIndex = sql.IndexOf("'", startOfTagIndex, StringComparison.Ordinal);
var endOfTagIndex = predicateEndIndex - 1; // Remove the final single quote

var tag = sql.Substring(startOfTagIndex, endOfTagIndex - startOfTagIndex + 1);

var startsWithAnd = CmpStr(sql, predicateStartIndex - 5, "AND (");
var endsWithAnd = CmpStr(sql, endOfTagIndex + 2, ") AND");

string finalSql;
if (startsWithAnd)
{
// Predicate pattern: ... AND (N'__tag' = N'mytag')
finalSql = sql.Substring(0, indexOfTagConstant - 8)
+ sql.Substring(endOfTagIndex + 3);
}
else if (endsWithAnd)
{
// Predicate pattern: (N'__tag' = N'mytag') AND ...
finalSql = sql.Substring(0, indexOfTagConstant - 3)
+ sql.Substring(endOfTagIndex + 8);
}
else
{
// It is the only predicate, so remove the whole "Where" section
finalSql = sql.Substring(0, indexOfTagConstant - 8)
+ sql.Substring(predicateEndIndex + 1);
}

finalSql = AddTagToQuery(finalSql, tag);
return finalSql;
}

private string AddTagToQuery(string sql, string tag)
{
[...] // Inserts the tag in the SQL query
}

private static bool CmpStr(string str, int startIndex, string compare)
{
[...] // Detects if a substring is contained in another string
}
}

Por último, los interceptores podemos registrarlos en el sistema de varias formas: desde el archivo de configuración del proyecto, en la configuración del contexto de datos, o globalmente. Por simplificar, lo haremos de esta última manera, introduciendo el siguiente código en el arranque de la aplicación (Por ejemplo, el global.asax.cs si se trata de una aplicación web):
DbInterception.Add(new QueryTaggerInterceptor(new SqlServerTagger()));

4. ¡Quiero probarlo!

He colgado el proyecto completo en Github por si queréis echar un vistazo al componente o ver algunos ejemplos de uso (en los tests, básicamente). Y para facilitar aún más las cosas, he creado el paquete NuGet EF6.TagWith, que podéis usar sin tener que copiar y pegar nada ;)

Para utilizarlo sólo tenéis que instalarlo en un proyecto donde uséis Entity Framework 6:
PM> install-package EF6.TagWith
A continuación, debéis registrar el interceptor en la inicialización de vuestra aplicación, tal y como hemos visto antes:
DbInterception.Add(new QueryTaggerInterceptor(new SqlServerTagger()));
Y hecho esto, taggear algunas consultas y utilizar alguna herramienta como SQL Profiler para observar el resultado:
var query = ctx.Friends
.OrderByDescending(friend => friend.Age)
.Take(10)
.Select(friend => new { FriendName = friend.Name, CountryName = friend.Country.Name })
.TagWith("Get top 10 older friends with country");

SQL Profiler mostrando queries etiquetadas

Espero que os sea de utilidad :)

Publicado en: www.variablenotfound.com.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 367

junio 24, 2019 06:17

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin / Cross-platform

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Blog Bitix

Comprobar la seguridad de un sitio web que use SSL/TLS

junio 23, 2019 10:00

Aunque un sitio web no trate datos sensibles como tarjetas de crédito o datos personales es muy recomendable que use el protocolo seguro HTTPS para proporcionar cifrado entre el servidor y el navegador del usuario para dotar de confidencialidad a las comunicaciones a la vez que evitar modificaciones por terceras personas de los datos transmitidos. Además, el buscador Google lo tiene en cuenta para el SEO o posicionamiento en su buscador.

Para usar HTTPS lo difícil era conseguir un certificado firmado por una autoridad de confianza que los navegadores tengan instalada, la obtención y renovación de un certificado tenía un coste. Desde hace un tiempo la autoridad Let’s encrypt emite certificados digitales gratuitamente que proporciona uno en pocos minutos y de forma automatizada incluida la renovación para usar un protocolo seguro. Usar un certificado de Let’s encrypt en el servidor web nginx no es complicado.

Sin embargo, usar HTTPS simplemente no es suficiente y ha de configurarse el servidor web para que utilice algoritmos de cifrado fuertes y que no tengan problemas seguridad conocidos o hoy estén ya considerados débiles. Para analizar el nivel de seguridad proporcionado en las conexiones HTTPS de un servidor web se puede utilizar la herramienta Qualys SSL Labs. Por ejemplo, analizando la seguridad del protocolo HTTPS ofrecido por GitHub Pages basta con introducir el dominio a analizar.

El informe que proporciona incluye información sobre el certificado del servidor entre ella su tiempo de validadez y fecha de expiración, y si es de confianza para los navegadores y plataformas como Mozilla, Apple, Android, Java o Windows. Los datos de configuración como protocolos soportados, cipher suites y una simulación de handshake con una gran variedad de versiones de navegadores en diferentes plataformas y versiones incluyendo dispositivos móviles y de escritorio que permite conocer si algún dipositivo pudiera tener algún problema con la configuración de TLS en la conexión, también otros detalle del protocolo.

Informe de seguridad TLS

La herramienta proporciona una nota entre A y F siendo la A la mejor calificación posible. Como se observa en la captura para GitHub Pages la herramienta proporciona una calificación de A.

Hay múltiples combinaciones de algoritmos de cifrado o cipher suites usados en una conexión SSL/TLS. La primera parte de los siguientes se refieren a TLS, está el tamaño de la clave y el modo y el algoritmo de autenticación del mensaje. Algunas recomendaciones de uso es usar tamaños de clave de más de 128 bits, evitar usar RC4, DES y 3DES, preferir ECDHE y DHE ya que ofrecen forward secrecy que protege las comunicaciones pasadas aún habiéndose comprometida la clave privada del servidor.

  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_AES_256_CBC_SHA
  • TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256

Y algunas propiedades de los servidores web Apache HTTP y nginx que afectan a los algoritmos de cifrado soportados son las siguientes. Algunos navegadores antiguos puede que no soporten los últimos algoritmos de cifrado por lo que hay que permitir en el servidor web unos que sean considerados como seguros pero que también soporten los navegadores de los usuarios del sitio web.

Uan vez configurada la seguridad con TLS/SSL es recomendable redirigir el tráfico del protocolo HTTP no seguro al protocolo HTTPS seguro.

» Leer más, comentarios, etc...

Blog Bitix

Formatear un dispositivo de almacenamiento compatible con Windows, macOS y Smart TV en GNU/Linux

junio 21, 2019 05:00

Los dispositivos de almacenamiento se han de formatear con un sistema de archivos. Algunos de los sistemas de archivos están mejor soportados y son compatibles con una mayor número de sistemas y dispositivos, por tanto al formatear un dispositivo de almacenamiento ha de elegirse como sistema de archivos uno compatible, dependiendo del uso es más adecuado uno u otro. Para los dispositivos extraíbles la opción recomendable es exFAT o en su defecto NTFS o FAT.

GNU
GNOME

Cada sistema operativo posee uno o varios formatos de sistemas de archivos que soporta de forma nativa. Así por ejemplo Windows como sistema de archivos para el sistema se utiliza NTFS y FAT, FAT32 o exFAT para los medios extraíbles. En GNU/Linux para el sistema se suele utilizar ext4. Y en macOS se utiliza HFS+ o APFS. Cada unos posee unas propiedades y los más antiguos para evitar sus limitaciones han sido sustituidos por unos sistemas de archivos más modernos.

Los sistemas de archivos más compatibles son los que tradicionalmente se han utilizado por Microsoft dada su amplia cuota de mercado en los sistemas de escritorio, estos son FAT y FAT32. Sin embargo, estos poseen unas limitaciones que no los hacen adecuados si el tamaño de los archivos superan los 4 GiB o la capacidad del sistemas de almacenamiento supera los 2 TiB, que son precisamente los límites máximos de FAT32. Para superar estas limitaciones Microsoft ha desarrollado exFAT como nuevo sistema de archivos para los medios extraíbles con unos límites de 16 EiB (16 x 1024 PiB x 1024 TiB, 16.6M TiB) para los tamaños de archivo y 64 ZiB (64 x 1024 EiB, 65536 EiB) para el sistema de alamcenamiento son unos límites varios órdenes de magnitud más grandes (y considerarse infinitos) que las capacidades de la tecnología actual (aunque al ritmo del avance de la tecnología puede que se llegue a ellos dentro de unas décadas).

En GNU/Linux puede utilizarse FAT32, NTFS y exFAT tanto en modo lectura como escritura, pero Windows solo permite sus propios formatos nativos y ha de utilizarse alguno de los anteriores, macOS soporta FAT en modo lectura y escritura, NTFS en modo lectura y para exFAT soporta también lectura y escritura. Otros dispositivos como Smart TV suelen soportar alguno de los sistemas de archivos de Microsoft ya sea FAT32, NTFS y exFAT. Salvo que se quiera la máxima compatibilidad con FAT32 para con versiones antiguas de Windows o dispositivos con algunos años la opción más recomendable a usar es exFAT.

En GNU/Linux para formatear y utilizar particiones en NTFS hay que instalar el paquete ntfs-3g y para exFAT el paquete exfat-utils, estos son los paquetes para Arch Linux otras distribuciones tienen un paquete equivalente.

Para formatear una unidad ya sea una memoria USB o disco duro externo con formato exFAT en GNU/Linux con el entorno de escritorio GNOME se realiza con el programa Discos, aunque también es posible realizarlo desde la linea de comandos.

En la parte izquierda se encuentra los dispositivos conectados y reconocidos por el sistema. En mi caso el disco del sistema, un Samsung 970 EVO de 500 GB, un disco duro externo USB de 500 GB y una memoria USB de 16 GB sin ningún formato.

Unidades del sistema

Primero es importante identificar correctamente la unidad que se quiere formatear para no perder los datos al elegir por error otra. Se introduce el nombre del volumen que identifica al dispositivo y se elige el sistema de archivos, como opciones más comunes se ofrece ext4, NTFS y FAT pero pulsando en Otro aparecen más, entre ellos exFAT. Pulsando el botón Siguiente al cabo de unos segundos la partición queda formateada con exFAT y lista para usarse tanto en GNU/Linux como en Windows, macOS o un Smart TV.

Formatear dispositivo de almacenamiento

No está de más recordar que en el caso de desechar un dispositivo de almacenamiento es recomendable hacerle un formateo completo para que los datos que contenga no sean accedidos por la persona a la que se le entregue el dispositivo ya que incluso se pueden recuperar datos previamente eliminados incluso en una unidad corrupta. Por otro lado en GNU/Linux si se desea mayor seguridad se puede cifrar la partición con la opción LUKS.

» Leer más, comentarios, etc...

Picando Código

Tourmaline – Framework para bots de Telegram en Crystal

junio 19, 2019 11:30

Tourmaline es un framework desarrollado en el lenguaje de programación Crystal. Con la reciente versión 0.70, alcanzó soporte completo para la API de Bots de Telegram:

Framwork para la API (y ojalá pronto Cliente) de Bots de Telegram en Crystal. Basado fuertemente en Telegraf, esta implementación en Crystal permite que tu bot de Telegram sea escrito en un lenguaje tanto hermoso como rápido. Benchmarks vendrán pronto.

¡Ya podemos escribir nuestro bot de Telegram en Crystal!

Tourmaline – Framework para bots de Telegram en Crystal

Para empezar a usar Tourmaline, tenemos que agregarlo a los shards de nuestra aplicación en el archivo shard.yml.


dependencies:
tourmaline:
github: watzon/tourmaline
version: ~> 0.7.0

Para experimentar, creé un proyecto Crystal nuevo con crystal init app milton_bot, agregué Tourmaline en shard.yml y ejecuté shards install. Después creé un bot en Telegram para poder probarlo. De Actualizar un canal de Telegram automáticamente con WordPress:

Para crear un bot, existe un bot… En Telegram mismo debemos conversar con BotFather, quien nos provee instrucciones para crear un nuevo bot. Los comandos a ejecutar son: /start, /newbot, nombre para el bot, nombre de usuario para el bot (debe terminar en «bot») y listo. BotFather nos va a avisar que el bot ha sido creado y nos va a dar un token para acceder a la Bot API.

Para este experimento le di vida a milton_bot. Ya creado, obvuve el API token pidiéndoselo a BotFather desde /mybots (o con /token). Para poder enviarme un mensaje por Telegram, tuve que averiguar mi id de usuario hablándole a MyIdBot.

Con unas pocas líneas de códigoya podemos mandarnos mensajes:

require "tourmaline/bot"
class MiltonBot
  VERSION = "0.1.0"
  def initialize(api_token)
    @bot = Tourmaline::Bot::Client.new(api_token)
  end
  def hola
    @bot.send_message("MI_ID_DE_USUARIO", "Hola Fernando, soy milton")
  end
end
milton = MiltonBot.new("API_TOKEN")
milton.hola

MiltonBot

😱 🤖

También podemos hacer que envíe mensajes a un canal o grupo. Para poder mandar mensajes directos a otros usuarios, necesitamos su ID y que el usuario haya ejecutado /start en una conversación con nuestro bot.

Como ya tengo un canal de pruebas, agregué a milton_bot como administrador y con un par de líneas de código más puedo mandar mensajes al canal:

@bot.send_message("@picandocodigo_test", "Hello World, I'm milton")
@bot.send_message("@picandocodigo_test", "https://i.giphy.com/media/d3Kq5w84bzlBLVDO/giphy.gif")

Y el resultado:

Se pueden hacer varias cosas más con Tourmaline, pueden ver el código fuente y aprender más en watzon/tourmaline en GitHub. Tengo una idea específica con MiltonBot, espero en algún momento tener tiempo para darle más vida y publicar mi primer proyecto en Crystal.

» Leer más, comentarios, etc...

Picando Código

PyCon Latam 2019 – La conferencia de Python en América Latina

junio 18, 2019 06:40

PyCon Latam es una versión de la conferencia PyCon que tiene como objetivo reunir a los desarrolladores de Python de todos los países latinoamericanos, y servir como una plataforma para que interactúen con la comunidad de Python en general. Esta conferencia de 3 días comienza el 29 de Agosto y termina el 31 en Puerto Vallarta, México.

PyCon Latam 2019

Agenda

Está enfocada en que los conferencistas principales representen las diferentes áreas de la industria, así como diferentes partes de la sociedad. Desde nacionalidad y género hasta experiencia en la industria, esperan que al compartir su experiencia partícular concienticen, generen empatía y provean una base que resulte familiar al contexto.

Por ahora hay 3 oradores anunciados:

Lorena Mesa – Politóloga convertida en programadora, Lorena Mesa es ingeniera de datos en el equipo de sistemas de inteligencia de software de GitHub, Directora en la Python Software Foundation, y co-organizadora de PyLadies Chicago.

Manuel Kaufmann – En estos años, Manuel contribuyó a la traducción de la Guía Oficial del Libro de Django y los tutoriales de Django Girls, al proyecto de One Laptop Per Child, entre otros. Actualmente, Manuel trabaja en Read the Docs como desarrollador de software.

Tania Allard – developer advocate en Microsoft con un enfoque en la ciencia de datos y todo lo relacionado con la informática científica de código abierto. También es miembro de la RSE del Reino Unido y de la asociación Python del Reino Unido y fundadora y organizadora de PyLadies NorthWest UK.

Becas

Como parte de su compromiso con la comunidad de Python, la organización ofrece becas especiales para las personas que necesitan ayuda financiera para asistir a PyCon Latam. Pueden ver más información en este enlace, y empresas que quieran patrocinar las becas también contactarse para apoyar la iniciativa.

Lugar y entradas

La conferencia se va a realizar en el centro de convenciones del Hotel Friendly Vallarta. Están a la venta las entradas con un precio promedio todo incluido de menos de $300 USD por persona para toda la estadía: Sala, boleto de conferencia, comida, café, bebidas y botín de PyLatam, y se pueden elegir habitaciones singles, dobles, triples y cuádruples.

Con un enfoque todo incluido, no tendrá que preocuparse por pagar más por la comida o las bebidas. Sin embargo, se suspenderá el consumo de alcohol durante la conferencia (por obvias razones), pero la fiesta se reanuda al final de cada día! 🍹

Se ubica tan solo a:

  • 10 minutos del Aeropuerto Internacional.
  • 15 minutos del Centro Internacional de Convenciones.
  • 5 minutos del Centro de Puerto Vallarta y el malecón.

Por más información pueden visitar el sitio web de PyCon Latam o seguirlos en Twitter: @PyLatam.

» Leer más, comentarios, etc...

Picando Código

[Libro] THEM: Adventures with extremists por Ron Jonson

junio 17, 2019 03:30

Libro: Jon Ronson, Them

Jon Ronsom: Them – Adventures with extremists

Me encontré con este título de casualidad hace por lo menos un año. Estaba mirando libros en una tienda, en esos momentos en los que no buscamos nada en particular, pero paseamos por las estanterías levantando y ojeando libros al azar.

Me llamó la atención la tapa (que en ese momento era la de esta edición), y después de leer la descripción, me quedó guardado en la memoria:

THEM comenzó como un libro sobre distintos tipos de extremistas, pero cuando Jon pudo conocer a varios de ellos -Fundamentalistas islámicos, neo-Nazis, miembros del Ku Klux Klan- encontró que todos tenían una creencia similar: que una pequeña, misteriosa elite gobierna el mundo desde un cuarto secreto.

En THEM, Jon se pone en camino, con la ayuda de los extremistas, a ubicar ese cuarto. El viaje es tan horripilante como cómico, y por el camino Jon es perseguido por hombres con lentes oscuros, desenmascarado como judío en medio de un campo de entrenamiento Yihad, y presencia a CEOs internacionales y políticos participando en un ritual pagano estrafalario en los bosques del norte de California.

THEM es una exploración fascinante y entretenida del extremismo, en el cual Jon aprende algunas cosas alarmantes sobre el mundo al otro lado del espejo de «ellos» (them) y «nosotros». ¿Están en algo los extremistas? ¿O se ha convertido Jon en uno de ellos?

La semana pasada lo adquirí y empecé a leerlo (a pesar de tener ya una pila de libros sin leer o medio empezados). Me resultó tremendamente entretenido, al punto que lo leí en 2 días. Hacía años que no leía un libro en tan poco tiempo. Creo que ayudó haber empezado el viernes de tarde. Para el sábado de noche ya lo había terminado. Me atrapaba la intriga de un capítulo a otro, «a ver en qué se mete ahora», y me llamaba a volver al libro cada vez que tenía tiempo ocioso.

THEM no analiza las razones que llevan a la gente a ser extremistas o el por qué del odio hacia otros. Pero con esa idea en común del grupo que domina el mundo desde un cuarto secreto, relata las aventuras del autor pasando tiempo con distintos extremistas. Es bastante divertido, a la vez que enseña de personajes y eventos relacionados a grupos extremistas en la historia. También baja un poco a tierra la imagen de estos hombres en el sentido de que más allá de todo, siguen siendo seres humanos de carne y hueso (teóricamente).

Cuando algún hecho despertaba mi curiosidad y me sentía en la necesidad de aprender un poco más, siempre usé Tor para buscar más información. No quiero que el gobierno que espía mi tráfico en internet me agregue a alguna lista rara por estar buscando información sobre extremistas pensando que estoy en el equipo de alguno de ellos. Hasta ahí llega mi paranoia, que es un tema común en el libro, relatando teorías conspiratorias de un Nuevo Orden Mundial involucrando tanto a multimillonarios y políticos como a razas extraterrestres reptilianas. ¿Demasiado alocado o una conspiración de los miembros del grupo mismo para ridiculizar toda la teoría? Son el tipo de cosas se plantean en THEM.

El libro está disponible en Amazon.es y Amazon.com. Fue el primer libro que leí del autor, ni siquiera vi la película basada en su obra The Men Who Stare at Goats. Después de esto, la agrego a mi lista de películas para ver y también agrego para leer a futuro The Psychopath Test: A Journey Through the Madness Industry, lo que podría alimentar mi teoría de que la mayoría de los CEOs, políticos y empresarios son psicópatas.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 366

junio 17, 2019 09:21

Enlaces interesantes Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Koalite

Sobre checked exceptions y manejo de excepciones

junio 17, 2019 05:06

Hace unos días me «retaba» Iván en Twitter a escribir un post sobre las checked exceptions de Java, y lo cierto es que me parece un tema entretenido para escribir algo más que un par de tweets, así que aquí va mi opinión.

Checked exceptions en Java

Desde la perspectiva de un sistema de tipos estático esto de las checked exceptions parece una buena idea: hace más expresiva la declaración del método y permite hacer más validaciones en tiempo de compilación.

Todo acorde con lo que uno esperaría de un sistema de tipos así. Si quieres flexibilidad vete a JavaScript y disfruta de su anarquía libertad, ¿no?

Sin embargo, para mi, que no soy ningún experto en Java, resultan incómodas. Quizá sea la sintaxis de Java, quizá sea el abuso de excepciones para situaciones que no son nada excepcionales, como que falle una conversión de string a int.

Si a eso le unes que existe un tipo mágico de excepciones que no se chequean (las que derivan de RuntimeException), pues te queda una mezcla un poco rara: por una parte me obligas a declarar y capturar excepciones con una sintaxis poco amigable, y por otra dejas una la puerta abierta a lanzar excepciones no chequeadas que hacen que nunca pueda estar seguro de si un método va a lanzar o no una excepción, por lo que esa presunta seguridad de tipos de queda en eso: presunta.

Excepciones como gestión de errores

En mi opinión, si una función puede no completar la operación que se supone que debe hacer es mejor codificarlo en el propio tipo devuelto por la función. Siguiendo con el ejemplo de convertir un string en un int, dado que es algo que puede fallar, me parece más razonable tener una función cuyo resultado indique si la conversión se pudo realizar o no y, en caso de que se realizase, el entero obtenido, en lugar de lanzar una excepción si el string no tiene un formato válido.

Si asumimos que la función puede fallar, podemos codificar el fallo en el tipo de retorno y convertir lo que sería una función parcial en una función total. En muchos lenguajes (como muchos funcionales) esto se representaría con un tipo Either o Maybe (si te da igual el motivo del fallo) y sería lo más normal del mundo.

Cuando unes ese diseño con un lenguaje que permite utilizar pattern matching para el análisis de resultados, o incluso algo como el do notation de Haskell, que suena muy exótico en pero en C# puedes simular con el select/from y seguro que en Java tienes alguna alternativa para hacer algo similar, la cosa se vuelve bastante cómoda y gestionar los errores de forma normal en lugar de como algo excepcional se convierte en rutinario.

Subiendo el nivel semántico de la excepción

Volviendo al hilo de Iván, él plantea:

Los argumentos que he escuchado en su contra son principalmente dos, el primero es que exponen detalles de implementación en la interfaz (la firma del método).

El segundo motivo en su contra es que si se modifica ese método y cambia su implementación de forma que pueda lanzar nuevas excepciones, es necesario actualizar todos sus usos añadiendo los correspondientes try.

Ciertamente son argumentos habituales en contra de las checked exceptions, y la respuesta de Iván a esos argumentos es más que razonable:

Tras darle vueltas y pensar en ello, para mi ninguno de los dos argumentos son válidos. Imagina una interfaz llamada Mailer que pueda tener diferentes implementaciones según como se quiera enviar un mensaje. Típico servicio registrado en un contenedor.

El método sería Future SendEmail(EmailAddress address, EmailContent content) throws EmailException

Bajo mi punto de vista, la interfaz debe de exponer esta excepción, y si ocurre algún tipo de excepción en alguna de sus implementaciones, relanzarla como EmailException.

De esta forma, me aseguro que la excepción siempre será EmailException y no otra. Imagina que estoy escribiendo una implementación que hace una llamada HTTP. Si HttpClient tiene HttpBadCodeResponseException como checked, me obligo a capturarla y relanzarla como EmailException de forma que no se me olvide hacer ningún catch y SendEmail lance un IOException o HttpBadResponseException, que SÍ que expondrían detalles de implementación.

A la hora de utilizar el servicio, sólo tendría que capturar EmailException en vez de Exception por si en alguna implementación se me ha olvidado capturar la excepción que produce y me encuentro con un FileNotFoundException o algo similar.

Básicamente la idea es aumentar el nivel semántico del error. Si tienes un método/función que envía un email, el resultado puede ser que lo ha enviado con éxito o que ha fallado, y si es así, el motivo real (fallo en la respuesta HTTP, fallo en el formato de la dirección del destinatario, etc.) puede considerarse algo secundario que quedará encapsulado en un EmailException.

Visto así tiene mucho sentido y es mucho más informativo encontrarte con un EmailException que con un HttpBadResponseException, pero lo cierto es que a efectos prácticos soluciona el problema a medias.

Es cierto que evita el carácter «vírico» de las checked excepctions, y si una nueva implementación del servicio puede encontrarse con otro tipo de problema los consumidores del servicio Mailer no tienen que preocuparse por ella puesto que quedará encapsulada en una EmailException. Esto funciona muy bien si desde el principio ya hemos decicido que el método SendEmail puede lanzar una excepción.

Pero, ¿qué ocurre cuando al diseñar nuestro interfaz decidimos no incluir ninguna excepción porque en las implementaciones iniciales no vemos ningún caso problemático? Básicamente, hemos vuelto al punto de partida. Cuando encontramos la primera implementación que puede lanzar excepciones, debemos encapsularlas en una excepción (con mayor valor semático, eso sí), y eso nos obliga a cambiar todas las implementaciones el interfaces Mailer y todos los consumidores del interfaces.

Una opción sería hacer que, especulativamente, todos y cada uno de los métodos de nuestros interfaces lanzasen una excepción con valor semántico asociado al interfaz; pero, sinceramente, empezar a añadir posible excepciones por si acaso en el futuro alguna implementación del interfaz la lanza no me parece muy práctico.

Y, ojo, esto no es algo que se solucione con la alternativa más «funcional» que comentaba antes de utilizar un tipo Either o similar para representar el resultado de la operación. Estás en las mismas, o bien introduces desde el principio el tipo Either como valor de retorno de todas tus funciones «por si acaso» en un futuro alguna puede fallar, o en el momento en que lo introduzcas te va a tocar modificar el código que ya existe.

Conclusión

Admito que aquí hay la cosa va por barrios.

En cierto modo, me recuerda a la típica discusión de lenguajes dinámicos y estáticos. Yo me siento más cómodo con lenguajes estáticos y nunca me ha supueto un freno tener que lidiar con tipos, más bien al contrario, pero es cierto que la excepciones chequeadas, que podrían verse como un paso más hacia la comprobación estática de errores, me han supuesto fricción adicional a la hora de desarrollar.

En realidad, ni siquiera estoy convencido de que las excepciones sean una buena idea como mecanismo de gestión de errores frente al uso de valores de retorno al estilo go, pero sin entrar a debatir eso, sí tengo claro que deberían representar cosas excepcionales y que, en general, se tienden a usar en situaciones que tienen poco de excepcional.

Habitualmente intento no gestionar excepciones en el lugar en que se producen a menos que esté tratando con una librería que las lance por casi cualquier motivo y pueda hacer algo para recuperarme del error o al menos convertirlas a algo con mayor valor semántico (cosa que, sinceramente, ocurre menos veces de las que me gustaría).

Desde mi punto de vista, una excepción debería representar algo excepcional y, por tanto, algo que de lo que la aplicación no tiene muchas formas de recuperarse. Por ello, tiendo a gestionar las excepciones en la «frontera» de las aplicaciones (controladores de un API Web, manejadores globales de excepciones), y muchas veces me limito a dejar que muera el proceso (ya sea la petición concreta a un API o incluso la aplicación entera) y logear la información para evitar en problemas futuros.

No hay posts relacionados.

» Leer más, comentarios, etc...

Israel Perales

Nexus Repository

junio 16, 2019 05:54

Nexus Repository

Don't repeat yourself

Un principio del desarrollo del software muy simple, "no repitas código".

Gracias a este principio creo que muchos desarrolladores dieron forma a las librerias, prefiriendo encapsular un comportamiento deseado en un solo lugar.

Esto hace necesario administrar las librerias de una forma eficiente y fácil de utilizar en el equipo de trabajo.

Aunque podemos expresarlo en una frase tiene una complejidad muy grande al llevarlo a la practica.

Debemos ser capaces de soportar los diferentes formatos de los componentes con los que trabajamos, en mi caso utilizo Maven, NPM/Yarn, Bower y Docker.

Al buscar una solución para estos componentes llegué a la questión de ¿que será mas facil de manejar?, ¿una plataforma por cada formato o uno que englobe a todos?.

Fue así que llegue a Nexus Repository OSS.

The free artifact repository with universal support for popular formats.

Todo en uno, gratis y con soporte para mis componentes de trabajo y algunos extras , aquí una lista de los formatos soportados:

  • Bower
  • Docker
  • Git LFS
  • Maven
  • npm
  • NuGet
  • PyPI
  • Ruby Gems
  • Yum
  • Apt
  • Conan
  • R
  • CPAN
  • Raw (Universal)
  • P2
  • Helm
  • ELPA

Instalación

Como ya es costumbre usaremos Docker para su instalación, podemos encontrar la imagen oficial de Nexus en Docker Hub.

Vamos a correr los siguientes comandos:

mkdir nexus && cd nexus

mkdir nexus-data

sudo chown -R 200 nexus-data

docker run -d -p 8081:8081 --name nexus -v nexus-data:/nexus-data sonatype/nexus3

Y ahora solo debemos entrar a la liga http://localhost:8081

Nexus Repository

Para iniciar sesión usaremos las credenciales por defecto que son admin y admin123.

Para utilizar Nexus cada formato requiere su propia configuración de los repositorios privados, en este caso, configuraremos Maven como ejemplo.

Configuración Maven

Crearemos un archivo llamado settings.xml en el directorio .m2 que se encuentra en nuestro home como se muestra en el siguiente ejemplo.

<settings>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>http://localhost:8081/repository/maven-public/</url>
    </mirror>
  </mirrors>
  <profiles>
    <profile>
      <id>nexus</id>
      <repositories>
        <repository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
     <pluginRepositories>
        <pluginRepository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>nexus</activeProfile>
  </activeProfiles>
  <servers>
    <server>
      <id>nexus</id>
      <username>admin</username>
      <password>admin123</password>
    </server>
  </servers>
</settings>

Probaremos que funciona con un proyecto real:


git clone https://github.com/ripper2hl/dockerejemplos.git

cd dockerejemplos/java

mvn clean install

Si todo esta bien deberíamos algo así en la terminal:

[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< javadocker:javadocker >------------------------
[INFO] Building javadocker 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.pom
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.pom (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.jar
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.jar (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-assembly-plugin/2.2-beta-5/maven-assembly-plugin-2.2-beta-5.pom
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-assembly-plugin/2.2-beta-5/maven-assembly-plugin-2.2-beta-5.pom (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-plugins/16/maven-plugins-16.pom
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-plugins/16/maven-plugins-16.pom (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-assembly-plugin/2.2-beta-5/maven-assembly-plugin-2.2-beta-5.jar
.
.
.

En Nexus vemos que se crea un cache de los paquetes que descargamos y así también ayudamos a ahorrar ancho de banda.

Nexus Repository

Este ejemplo es especificamente del formato de maven, para configurar otros formatos, podemos seguir la guiá del blog oficial.

https://blog.sonatype.com/using-nexus-3-as-your-repository-part-1-maven-artifacts

Mantener el orden y tener nuestros componentes a la mano, nos dará el impulso de llevar el flujo de trabajo a otro nivel.

Tal vez existan mejores alternativas a Nexus pero me ha funcionado y aun no busco un reemplazo a esta herramienta.

» Leer más, comentarios, etc...

Blog Bitix

Metadatos e introspección en GraphQL

junio 15, 2019 10:00

Una API REST no ofrece introspección y por tanto hay que recurrir a un sistema de documentación que puede estar desactualizado y hay que mantener para conocer como usar la API y cuales son sus tipos y parámetros. Por el contrario GraphQL incorpora un sistema de introspección que permite conocer sus tipos y campos, a través del editor GrapiQL o si fuese necesario de forma automtizada con código.

GraphQL

Una de las cosas que me gustan de GraphQL sobre REST es que la API de un servicio se define en un esquema. Tanto las operaciones de consulta, de modificación con sus nombres de parámetros, tipos y si son requeridos o no. Esta información es básica para hacer un buen uso de esa API y conocer cual es su contrato. Además con la herramienta GraphiQL se pueden crear y realizar consultas con un pequeño IDE con asistencia de código. GraphQL genera los metadatos e ofrece la instrospección a partir únicamente de la definición del esquema del servicio sin ningún esfuerzo adicional por parte del creador del servicio.

En el ejemplo de esta serie de artículos sobre GraphQL he usado el siguiente esquema que utiliza como modelo de datos el de una librería.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
scalar LocalDate
type Book {
id: Long
title: String
author: Author
isbn: String
date: LocalDate
comments(after: String, limit: Long): CommentsConnection
batchedIsbn: String
batchedComments(after: String, limit: Long): CommentsConnection
}
type Magazine {
id: Long
name: String
pages: Long
}
type Comment {
id: Long
text: String
}
type Author {
id: Long
name: String
}
input BookFilter {
title: String
}
type CommentsConnection {
edges: [CommentEdge]
pageInfo: PageInfo
}
type CommentEdge {
node: Comment
cursor: String
}
type PageInfo {
startCursor: String
endCursor: String
hasNextPage: Boolean
}
union Publication = Book | Magazine
type Query {
books(filter: BookFilter): [Book]!
publications: [Publication]!
book(id: Long): Book!
authors: [Author]!
author(id: Long): Author!
}
type Mutation {
addBook(title: String, author: Long): Book
}
schema {
query: Query
mutation: Mutation
}

Si no se conoce el esquema qué operaciones, tipos y nombres ofrece la API GraphQL permite introspección y con únicamente el endpoint se puede averiguar esta información.

Por ejemplo, con la siguiente consulta se puede conocer qué tipos contiene el esquema de una API. Los que comienzan con dos barras bajas o __ son tipos parte del sistema de introspección. Entre los que están el que representa un libro y autor pero también está Query que es un punto de acceso a la API.

1
2
3
4
5
6
7
{
__schema {
types {
name
}
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
{
"data": {
"__schema": {
"types": [
{
"name": "Author"
},
{
"name": "Book"
},
{
"name": "BookFilter"
},
{
"name": "Boolean"
},
{
"name": "Comment"
},
{
"name": "CommentEdge"
},
{
"name": "CommentsConnection"
},
{
"name": "LocalDate"
},
{
"name": "Long"
},
{
"name": "Magazine"
},
{
"name": "Mutation"
},
{
"name": "PageInfo"
},
{
"name": "Publication"
},
{
"name": "Query"
},
{
"name": "String"
},
{
"name": "__Directive"
},
{
"name": "__DirectiveLocation"
},
{
"name": "__EnumValue"
},
{
"name": "__Field"
},
{
"name": "__InputValue"
},
{
"name": "__Schema"
},
{
"name": "__Type"
},
{
"name": "__TypeKind"
}
]
}
}
}

Conocer el tipo de las consultas de lectura y que consultas se pueden realizar inspeccionando el tipo Query.

1
2
3
4
5
6
7
{
__schema {
queryType {
name
}
}
}
1
2
3
4
5
6
7
8
{
__type(name: "Query") {
name
fields {
name
}
}
}
1
2
3
4
5
6
7
8
9
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
}
}
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"data": {
"__type": {
"name": "Query",
"fields": [
{
"name": "author"
},
{
"name": "authors"
},
{
"name": "book"
},
{
"name": "books"
},
{
"name": "publications"
}
]
}
}
}

Por la propiedad de GraphQL de que se pueden realizar varias consultas en una única petición se pueden obtener ambos resultados a la vez.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
__schema {
queryType {
name
}
}
__type(name: "Query") {
name
fields {
name
}
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
}
},
"__type": {
"name": "Query",
"fields": [
{
"name": "author"
},
{
"name": "authors"
},
{
"name": "book"
},
{
"name": "books"
},
{
"name": "publications"
}
]
}
}
}

Se puede obtener más en detalle los campos que contiene un tipo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
book: __type(name: "Book") {
name
fields {
name
type {
name
kind
}
}
}
author: __type(name: "Author") {
name
fields {
name
type {
name
kind
}
}
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{
"data": {
"book": {
"name": "Book",
"fields": [
{
"name": "author",
"type": {
"name": "Author",
"kind": "OBJECT"
}
},
{
"name": "batchedComments",
"type": {
"name": "CommentsConnection",
"kind": "OBJECT"
}
},
{
"name": "batchedIsbn",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "comments",
"type": {
"name": "CommentsConnection",
"kind": "OBJECT"
}
},
{
"name": "date",
"type": {
"name": "LocalDate",
"kind": "SCALAR"
}
},
{
"name": "id",
"type": {
"name": "Long",
"kind": "SCALAR"
}
},
{
"name": "isbn",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "title",
"type": {
"name": "String",
"kind": "SCALAR"
}
}
]
},
"author": {
"name": "Author",
"fields": [
{
"name": "id",
"type": {
"name": "Long",
"kind": "SCALAR"
}
},
{
"name": "name",
"type": {
"name": "String",
"kind": "SCALAR"
}
}
]
}
}
}

Incluso se puede inspeccionar los tipos del sistema de instrospección. Con las descripciones de los campos o parámetros de entrada si los tuviese.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
__type(name: "__Type") {
name
fields {
name
type {
name
kind
}
}
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
{
"data": {
"__type": {
"name": "__Type",
"fields": [
{
"name": "description",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "enumValues",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "fields",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "inputFields",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "interfaces",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "kind",
"type": {
"name": null,
"kind": "NON_NULL"
}
},
{
"name": "name",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "ofType",
"type": {
"name": "__Type",
"kind": "OBJECT"
}
},
{
"name": "possibleTypes",
"type": {
"name": null,
"kind": "LIST"
}
}
]
}
}
}

Conocer cuales son los campos de un tipo puede utilizarse para validar una API, comprobando que no se han eliminado campos necesarios. Es útil en el caso de querer automatizar esta validación de una API de GraphQL que se consuma ayudando a detectar de forma temprana problemas de compatibilidad al publicarse una nueva versión que no está bajo propiedad del que la usa.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

» Leer más, comentarios, etc...

Blog Bitix

Pruebas de carga y rendimiento de un servicio web con Apache Bench

junio 14, 2019 03:00

En algunos que un servicio devuelva los datos esperados no es suficiente, otros requisitos no funcionales o de términos de servicio son que sus tiempos de respuesta sean menores al especificado en sus requisitos, que sea capaz de soportar cierto número de peticiones concurrentes o de atender un número de peticiones por minuto. Para asegurar que el servicio es capaz de cumplir estos requisitos funcionales hay que utilizar herramientas que permitan evaluar su desempeño, una de ellas muy fácil de utilizar y que proporciona valiosa información es Apache Bench.

Apache

Para hacer pruebas de carga o medir el rendimiento de cualquier servicio que funcione mediante el protocolo HTTP hay multitud de herramientas. Una de las más sencillas de utilizar y con un informe con información interesante es Apache Bench o simplemente ab. Este comando se puede utilizar con simplemente tres parámetros el endpoint a probar, el número de peticiones en total a realizar (-n) y cuantas peticiones concurrentes al mismo tiempo (-c). Otos parámetros son los datos POST a enviar, cabeceras (-H) y cookies (-C) de las peticiones, tiempos de timeout (-s) o cerficado de cliente (-E) entre algunos otros. En vez limitar las pruebas a un número de peticiones las pruebas se pueden limitar a un tiempo determinado por ejemplo 60 segundos (-t).

Es una herramienta que se utiliza para medir el rendimiento de el servidor Apache pero utilizable para cualquier otro servicio por ejemplo una web o una API REST o GraphQL. Está disponible por supuesto para GNU/Linux pero también para macOS y para Windows.

Si quisiese medir el rendimiento en mi blog alojado en GitHub Pages podría hacerlo lanzando 1000 peticiones para que sea una muestra suficientemente amplia con 20 usuarios de forma concurrente que son los que en los momentos de más tráfico tiene mi blog. Mi conexión de internet es un ADSL que no llega a 1 MB/s de subida por lo que la conexión en cierta medida limite el test.

1
$ ab -n 1000 -c 20 https://picodotdev.github.io/blog-bitix/

El informe de resultado que ofrece ab al finalizar la prueba incluye el tiempo dedicado en la conexión, en el procesado, esperando y en total con los valores para cada uno de ellos con mínimo y máximo, de media y la mediana. El tiempo total empleado por la prueba, el protocolo SSL/TLS usado, los bytes devueltos en la petición, el número de peticiones servidas por segundo, el tiempo de media empleado de media por cada petición y de media teniendo en cuenta la concurrencia, la tasa de transferencia en la respuesta y finalmente el tiempo de respuesta según percentil que van que desde el 50 al 100, es decir, que el 50% de las peticiones se han respondido en el tiempo en milisegundos indicado. Si las hubiera también muestra las peticiones fallidas y las que han devuelto un código de respuesta distinto de 200.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking picodotdev.github.io (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: GitHub.com
Server Hostname: picodotdev.github.io
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256,2048,128
Server Temp Key: X25519 253 bits
TLS Server Name: picodotdev.github.io
Document Path: /blog-bitix/
Document Length: 28389 bytes
Concurrency Level: 20
Time taken for tests: 29.220 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 29007394 bytes
HTML transferred: 28389000 bytes
Requests per second: 34.22 [#/sec] (mean)
Time per request: 584.404 [ms] (mean)
Time per request: 29.220 [ms] (mean, across all concurrent requests)
Transfer rate: 969.45 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 149 349 149.7 303 1361
Processing: 122 225 75.5 207 586
Waiting: 53 122 68.8 108 481
Total: 329 574 174.0 512 1612
Percentage of the requests served within a certain time (ms)
50% 512
66% 534
75% 555
80% 744
90% 832
95% 915
98% 1104
99% 1171
100% 1612 (longest request)

Esta herramienta puede ser utilizada para par medir el rendimiento de cualquier servicio web. Un blog de Wordpress, una página de una organización, un endpoint de un servicio REST o GraphQL, etc… Es muy sencilla de utilizar y genera un informe corto pero con interesante información sobre el rendimiento. Si se hacen cambios se puede medir el antes y el después y comparar los resultados para observar de que modo han afectado al redimiento si de forma positiva o negativa y en que grado.

» Leer más, comentarios, etc...

Fixed Buffer

La potencia de la Reflexión (Parte 1)

junio 11, 2019 08:00

¡Ya estamos de vuelta por estos lares! Hoy vengo a contaros un problema que he tenido en el trabajo y como lo he solucionado con una herramienta no muy visible pero muy potente que nos ofrece .Net, la reflexión. Antes de nada, ¿qué es eso de la reflexión y como puedo usarla en C#?

Si nos fijamos en la definición:

La reflexión proporciona objetos (de tipo Type) que describen los ensamblados, módulos y tipos. Puede usar la reflexión para crear dinámicamente una instancia de un tipo, enlazar el tipo a un objeto existente u obtener el tipo desde un objeto existente e invocar sus métodos, o acceder a sus campos y propiedades. Fuente: Reflexión (C#)

Dicho con otras palabras más sencillas, la reflexión en C# es la capacidad que tenemos para desde el código, conseguir información sobre tipos (clases, librerías, objetos, etc…) y usar esa información para crear objetos dinámicamente y/o acceder a sus miembros sin crear una instancia tipada.

Vale, ¿con lo que he dicho no he conseguido aclararlo mucho más verdad? La verdad es que personalmente creo que es un concepto avanzado del lenguaje y que eso es lo que echa para atrás a muchos programadores. De hecho, si vemos su página de MSDN, vamos que tiene varias opciones:

Seguramente después de esto te sigas preguntando en que me ha podido valer la reflexión de c#. Vale, en mi caso, yo tenia una serie de clases que heredaban todas de una clase base, pero cada una tenia sus particularidades, algo como por ejemplo esto:

public class BaseClass
{
    public string ModeloMotor { get; set; }
}

public class Coche : BaseClass
{
    public bool Descapotable { get; set; }
}

public class Moto : BaseClass
{
    public bool Motor2Tiempos { get; set; }
}

public class Camion : BaseClass
{
    public bool EsVehiculoLargo{ get; set; }
}

Todo ello son posibles clases que recibimos de un método o una api, la cual nos devuelve un «BaseClass», si nos fijamos, todas están relacionadas entre ellas, pero cada una de las clases tiene sus particularidades. Aquí, tendríamos una opción clara, aprovecharnos del poliformismo y tener una clase base que lo contenga todo:

public class BaseClass
{
    public string ModeloMotor { get; set; }
    public bool Descapotable { get; set; }
    public bool Motor2Tiempos { get; set; }
    public bool EsVehiculoLargo{ get; set; }
}

public class Coche : BaseClass
{
    //...
}

public class Moto : BaseClass
{
    //...
}

public class Camion : BaseClass
{
    //...
}

De este modo, desde cada una de las clases hijas tendremos acceso a todas las propiedades. Pero claro, tampoco tiene mucho sentido hablar de un camión con un motor de dos tiempos o una moto que sea un vehículo largo.

Otra opción posible, es hacer casting a absolutamente todas las posibles clases hijas para ver si alguna coincide:

var moto = claseBase as Moto;
if (!(moto is null))
    Console.WriteLine(moto.Motor2Tiempos);
var coche = claseBase as Coche;
if (!(coche is null))
    Console.WriteLine(coche.Descapotable);
var camion = claseBase as Camion;
if (!(camion is null))
    Console.WriteLine(camion.Tara);
//.....

¿Os imagináis lo larga que se puede hacer la lista si tengo 20 clases diferente que heredan de clase base?

Aquí es donde entra en juego la reflexión, desde un objeto de tipo «BaseClass», mediante la reflexión de C#, podemos iterar las propiedades del objeto encapsulado y obtener su valor, por ejemplo, vamos a crear un método de extensión que nos permita obtener desde la clase base si es un vehículo largo:

static class Extensiones
{
    public static bool? EsVehiculoLargo(this BaseClass clase)
    {
        //Obtenemos todas las propiedades de la clase que nos pasan
        var properties = clase.GetType().GetProperties();
        //Iteramos las propiedades
        foreach (var propertyInfo in properties)
        {
            //Si alguna se llama 
            if (propertyInfo.Name == "EsVehiculoLargo")
            {
                //Retornamos el valor
                return Convert.ToBoolean(propertyInfo.GetValue(clase));
            }
        }
        //Si ninguna coincide, retornamos null
        return null;
    }
}

Si nos fijamos en el código, estamos creando un método extensor para «BaseClass». En él, lo que vamos a hacer es obtener todas las propiedades, pero no de «BaseClass», sino de la clase hija encapsulada dentro. Dentro de las propiedades, vamos a buscar la que tenga el nombre que nos interesa, y si hay alguna, vamos a obtener el valor pasándole al objeto PropertyInfo la instancia de donde tiene que obtener el valor.

Con esto, no solo podemos leer la propiedad, también podríamos escribirla llamando al método «SetValue»:

propertyInfo.SetValue(clase, true);
propertyInfo.SetValue(clase, false);

Pero eso no es todo lo que podemos conseguir, también podemos obtener información sobre su tipo, obtener el método get o set, los atributos que tiene, si es de lectura y/o escritura …

De este modo tan elegante, podemos acceder a la información de la clase hija sin tener que conocer el tipo exacto de la clase hija. En mi caso, pude conseguir consumir la api sin tener que hacer casting individuales (en mi caso yo tenía más de 60 clases hijas).

Pero no se queda ahí, ¡la reflexión en C# da para mucho más! Si con este ejemplo te ha picado el gusanillo sobre lo que puede ofrecer la reflexión, en las próximas entradas vamos a profundizar en diferentes casos, como crear un objeto de una clase de manera dinámica, buscar entre los atributos de un objeto, o llamar a métodos de objetos dinámicamente. De momento, dejo un enlace al código para poder probarlo y lo iremos ampliando.

**La entrada La potencia de la Reflexión (Parte 1) se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Variable not found

Índices y rangos en C# 8 (2 de 2)

junio 11, 2019 06:14

.NET CoreEn el post anterior vimos que la estructura Index, junto con alguna cortesía del compilador, permitía la especificación de índices en arrays de forma muy sencilla. Veíamos cómo podíamos acceder a elementos concretos utilizando su posición en la colección, tanto contando desde el principio como desde el final:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
Index fromStart = 2; // = Index.FromStart(2) - conversión implícita
Index fromEnd = ^2; // = Index.FromEnd(2)

Console.WriteLine(primes[fromStart]); // 5
Console.WriteLine(primes[fromEnd]); // 17
Sin embargo, puede que a Index por sí mismo tampoco le veáis demasiada utilidad... y así es. De hecho, su finalidad es más bien el dar soporte a rangos, una nueva característica de C#8 que nos permitirá referirnos a "porciones" de arrays o colecciones similares usando una sintaxis compacta e integrada en el lenguaje.

La estructura Range

La estructura Range ofrece una vía para la especificación de rangos en colecciones indexadas mediante el almacenamiento de sus índices iniciales y finales. O en otras palabras, Range permite definir un rango del interior de un array o similar mediante la indicación de un Index inicial y un Index final.

Veamos un ejemplo de uso:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var firstTwoItemsRange = new Range(Index.FromStart(0), Index.FromStart(2));
// O Más simple, utilizando conversiones implícitas:
// var firstTwoItemsRange = new Range(0, 2);
var firstTwoItems = primes[firstTwoItemsRange];
Console.WriteLine(string.Join(",", firstTwoItems)); // 2,3
Un aspecto sumamente importante en este punto, del que seguro que algunos os habéis dado cuenta, es el índice final en un objeto Range es no inclusivo (¡ojo con esto!). Por esa razón, el resultado mostrado por consola es "2,3" y no "2,3,5", a pesar de haber indicado como índices iniciales y finales 0 y 2, respectivamente.

Si quisiéramos incluir el último elemento, deberíamos especificar un índice superior por encima del mismo, como en el siguiente código, donde utilizamos Index.From() para obtener el índice del elemento que se encuentra virtualmente más allá del último ítem del array:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var lastThreeItemsRange = new Range(5, Index.FromEnd(0));
var lastThreeItems = primes[lastThreeItemsRange];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13,17,19
Como ya sabemos gracias al post anterior, podemos utilizar el edulcorante sintáctico que nos ofrece C# para crear rangos de forma más compacta efectiva, gracias al uso de las conversiones implícitas y el hat operator:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var lastThreeItemsRange = new Range(^3, ^0);
var lastThreeItems = primes[lastThreeItemsRange];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13,17,19

var allPrimesRange = new Range(0, ^0);
var allPrimes = primes[allPrimesRange];
Console.WriteLine(string.Join(",", allPrimes)); // 2, 3, 5, 7, 11, 13, 17, 19
Y como ocurría con la estructura Index, Ranage también ofrece algunos miembros estáticos para simplificar la codificación de rangos frecuentes, como Range.StartAt(), Range.EndAt() o Range.All:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var firstTwoItemsRange = Range.EndAt(2);
var firstTwoItems = primes[firstTwoItemsRange];
Console.WriteLine(string.Join(",", firstTwoItems)); // 2,3

var lastThreeItemsRange = Range.StartAt(5);
var lastThreeItems = primes[lastThreeItemsRange];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13, 17, 19

var allPrimesRange = Range.All;
var allPrimes = primes[allPrimesRange];
Console.WriteLine(string.Join(",", allPrimes)); // 2, 3, 5, 7, 11, 13, 17, 19

Añadimos más edulcorante: el operador rango ".."

Con lo que sabemos hasta este momento, ya podríamos definir rangos de una forma bastante efectiva y fácil de leer, pero, de nuevo, los diseñadores de C# no podían dejarlo ahí y han añadido un atajo para hacerlo aún más sencillo.

El operador ".." permite definir rangos de forma más compacta, usando índices separados por estos caracteres para determinar los límites de éstos. Como vemos en el siguiente código, estos índices son opcionales, lo que permite especificar muy fácilmente los límites superiores e inferiores del rango:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var thirdAndFourthItems = primes[2..4];
Console.WriteLine(string.Join(",", thirdAndFourthItems)); // 5,7

var firstTwoItems = primes[..2];
Console.WriteLine(string.Join(",", firstTwoItems)); // 2,3

var lastThreeItems = primes[^3..];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13, 17, 19

var allPrimes = primes[..];
Console.WriteLine(string.Join(",", allPrimes)); // 2, 3, 5, 7, 11, 13, 17, 19

Pero... ¿qué estamos haciendo realmente a obtener un rango de una colección?

Hasta ahora hemos visto cómo se definen los rangos, y cómo podemos usarlos para "extraer" un subconjunto de elementos de un array.

Pero en realidad no nos hemos detenido a pensar qué es lo que estamos haciendo realmente cuando obtenemos un rango. Es decir, la pregunta es: ¿estamos creando un nuevo array copiando los elementos del anterior? ¿O quizás estamos obteniendo un puntero a los elementos existentes inicialmente? Pues, como suele ocurrir, la respuesta es que depende.

Cuando obtenemos un rango desde un array, internamente se copiarán los elementos a un nuevo array. Esto podemos comprobarlo fácilmente con el siguiente código, donde podemos observar que la modificación de un elemento del rango no afecta a la colección original:
var primes = new [] { 2, 3, 5, 7, 11, 13, 17, 19 };

var firstTwoItems = primes[..2];
firstTwoItems[0] = 666;
Console.WriteLine(string.Join(",", firstTwoItems)); // 666,3

var allItems = primes[..];
Console.WriteLine(string.Join(",", allItems)); // 2, 3, 5, 7, 11, 13, 17, 19
Sin embargo, si lo aplicamos sobre zonas de memoria como las referenciadas por Span<T>, el rango representará un subconjunto indexado de las mismas, pero apuntando a los contenidos originales (es decir, no se creará una copia de los datos):
string ToCommaString(Span<int> values) => string.Join(",", values.ToArray());

var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
var span = primes.AsSpan();

var thirdAndFourthItems = span[2..4]; // Points to ...[5,7]...
thirdAndFourthItems[0] = 666;
Console.WriteLine(ToCommaString(thirdAndFourthItems)); // 666,7

var allItems = span[..];
Console.WriteLine(ToCommaString(allItems)); // 2, 3, 666, 7, 11, 13, 17, 19

var allOriginalItems = primes[..];
Console.WriteLine(string.Join(",", allOriginalItems)); // 2, 3, 666, 7, 11, 13, 17, 19
Toolbelt. Picture by Bert MarshallBueno, y creo que con esto ya hemos dado un buen repaso a Index y Range, así que lo dejamos aquí :)  Espero que os haya resultado interesante para comprender estos nuevos tipos y añadirlos como herramientas a nuestro cinturón de desarrollador.

Publicado en: www.variablenotfound.com.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 365

junio 10, 2019 11:01

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Arragonán

KPIs para equipos de desarrollo de software

junio 10, 2019 12:00

Como parte de mi trabajo en el último año, he intentado empujar la cultura de mejora continua en los diferentes equipos con los que he ido trabajando. Tanto en cuestiones de herramientas y habilidades técnicas, como en las de comunicación y coordinación, con ciertas restricciones y dependencias que caen fuera de nuestro margen de influencia.

Partimos del supuesto de que cuanto mejores sean las prácticas del equipo, mejor capacidad de entrega tendrá. Eso se traduce en una mayor adaptabilidad a los cambios, una mejor mantenibilidad del software con el paso del tiempo y posiblemente una mayor motivación del equipo con su trabajo.

Además de las típicas sensaciones subjetivas de efecto de mejora, teníamos que pensar en indicadores que nos permitieran ir observándolo realmente. En algún momento, también se empezaría a querer tener visibilidad desde fuera de los equipos, así que tocaba darle una vuelta y ordenar ideas.

Estuve preparando una presentación para explicar internamente cómo estábamos trabajando, hacia dónde creía que debíamos ir a través de una mejora continua, los distintos KPIs que podríamos observar y las necesidades que se cubrirían con una buena capacidad de entrega.

Diapositiva de una presentación con la visión de lo que, como equipo deberíamos cubrir: adaptabilidad respecto a negocio, entregar pronto, evitar retrabajo, una buena UX y evitar bugs en lo posible

“Dime cómo me mides y te diré cómo me comporto”

Desconozco el origen, pero con estas cosas siempre me acuerdo de este dicho. Hay que andar con ojo con qué indicadores (que no objetivos) vamos a medir. Además, tendremos que usar varios para compensar el fomento de comportamientos extraños que falsean esas métricas.

Con el uso normal de las herramientas, sólo acordando algunas convenciones, se puede facilitar la explotación de datos posterior para extraer métricas. Los orígenes de datos para los indicadores son:

  • El código
  • El repositorio y servidor de automatización
  • Las herramientas de gestión
  • El propio producto

Indicadores del código

Las herramientas de análisis estático de código nos dan números sobre deuda técnica que existe. No debemos perder de vista estos indicadores y dedicar tiempo en analizar y hacer limpieza específica de vez en cuando. Unas veces se resuelven con soluciones simples y otras esconden problemas de diseño que no resultan tan evidentes.

La cobertura de test es uno de los indicadores más habituales, lo más interesante en este caso es ver qué NO está cubierto. Y como se suele comentar, hay que andar con cuidado porque es un indicador fácil de falsear si se busca como objetivo.

En un momento dado, además, se podría utilizar mutation testing (comprobar que se rompe algún test al modificar código de producción) para tener un indicador de la calidad de los tests unitarios.

Indicadores del repositorio y servidor de automatización

Hay bastantes indicadores interesantes que se pueden sacar de los repositorios y servidores de automatización, aunque en su mayoría son dependientes de las convenciones de uso.

Sin embargo, un indicador siempre válido y que pienso que debería observarse es la frecuencia de integración.

La integración continua es una práctica (que no herramienta) tan popular como malinterpretada, ya que hasta que no se une el trabajo que ha hecho o está haciendo una persona con la rama principal de desarrollo y se completa una build, no la estamos realizando.

Hasta que no ha terminado correctamente la construcción de un artefacto de software, no sabemos si todo está correcto. A mayor frecuencia, feedback más temprano y menor incertidumbre.

En caso de usar ramas, también es interesante ver la duración de vida de las ramas. A más tiempo, mayor riesgo de conflictos u otros problemas al integrar.

Y si se utilizan pull/merge requests, también hay un puñado de indicadores que en un momento dado puedan sacarnos olores relacionados con la capacidad de entrega: cantidad de comentarios, tiempo que quedan abiertas, cantidad de rechazos…

Indicadores de las herramientas de gestión

Las herramientas de gestión, además de servir como radiador de información para saber la situación actual de la construcción del producto y ayudar a coordinar el trabajo, son una buena fuente de información de indicadores del proceso de trabajo.

Para observar la capacidad de todo el equipo en conjunto de hacer vertical slicing es muy útil conocer el tiempo de ciclo. A menor tiempo de ciclo, mayor es nuestra capacidad de entrega. Es el tiempo que se tarda desde que se empieza a trabajar en algo que aporte valor (por ejemplo, una historia de usuario) hasta que pasa a estar hecho.

Es habitual que en algún punto de las herramientas de gestión se pueden observar los distintos despliegues que se han realizado, donde podamos obtener la frecuencia de despliegue. Evidentemente a mayor frecuencia, mejor.

Aunque nuestro tiempo de ciclo fuera corto y la frecuencia de despliegue alta, sería posible que nuestro producto fuera frágil debido a bugs. Por eso el indicador de bugs detectados y resueltos por versión/despliegue es otra métrica a tener siempre en cuenta.

Dependiendo del momento y escenario en el que se encuentre un producto, también me parece muy interesante el indicador del lead time, el tiempo que pasa desde que se pide algo nuevo hasta que está desplegado en producción. Que vendría a ser consecuencia de los 3 anteriores indicadores y del tamaño de la pila de producto.

Como supongo que haya quien pueda echarlo de menos, omito intencionadamente los indicadores al respecto de las estimaciones, tipo story points por iteración. En mi opinión, tienen un componente muy subjetivo y variable para ser utilizado como indicador de cambio en la capacidad de entrega de un equipo.

Indicadores del propio producto

Además de otro tipo de instrumentación mucho más minuciosa que necesitan los miembros del equipo de gestión de producto o diseño, el equipo de desarrollo debería poder observar el número y porcentaje de uso por funcionalidad, que al final define el éxito o no del trabajo de todos.

Lo más interesante de este indicador es que combinado con otras métricas puede ayudar a tomar decisiones sobre la evolución del producto. Como por ejemplo, este escenario:

  1. Detectamos problemas muy graves de rendimiento en una parte del producto.
  2. Observamos que el porcentaje de uso de la funcionalidad afectada por esos problemas es residual.
  3. Decidimos no resolverlo de momento, pero lo reflejamos en la pila del producto como algo poco prioritario.
  4. Configuramos una alerta para detectar cierto aumento en el porcentaje de uso de esa funcionalidad.

Otro indicador importante es el crash rate del producto, cuántas veces se detecta un fallo o error por cantidad de uso, que nos permite saber lo estable que es un producto. Y mezclado con el indicador de números de uso por funcionalidad, nos permite detectar los puntos problemáticos.

Dependiendo del contexto del producto y del negocio, posiblemente podamos sacar también otros indicadores relevantes de operaciones y soporte que sean consecuencia del uso del producto.

Medir sin perder el foco

Estos son muchos KPIs distintos. Es interesante observarlos porque nos pueden servir para detectar olores sobre problemas y oportunidades de mejora, pero son demasiados para tratar de mejorar todo a la vez.

Para evitar diluirnos y no terminar mejorando en nada, deberíamos elegir y enfocarnos en un par de esos indicadores cada vez, dependiendo de la situación de cada equipo.

Y aunque sea tentador hacerlo, evitaría usar alegremente estos indicadores para marcar objetivos, así como para evaluar a equipos distintos.

No olvidemos que el propósito de medir estos indicadores es observar la evolución en el tiempo de un equipo trabajando en un producto y contexto determinado.

» Leer más, comentarios, etc...

Blog Bitix

Teses unitarios parametrizados con JUnit

junio 07, 2019 03:00

Hay múltiples lenguajes y librerías donde cada una publica nuevas versiones. Una vez elegida una esa decisión no tiene que ser para siempre si las circunstancias de un proyecto cambian o una nueva versión incorpora las funcionalidades por las que se eligió otra. Si se reconsideran de nuevo el conjunto de todos los parámetros la decisión puede ser distinta. Esto me ha ocurrido al evaluar de nuevo JUnit comparándolo con Spock, teniendo en cuenta que en JUnit 5 han incorporado los teses parametrizados y el lenguaje que utiliza cada una de ellas.

JUnit
Java

En mis preferencias de herramientas que elegiría para un proyecto basado en la plataforma Java estaba Spock, por la legibilidad de los teses con su lenguaje específico de dominio o DSL con sus diferentes secciones given, when, then. Otro motivo era la posibilidad de ejercitar un mismo test pero con diferentes parámetros para ejecutar todas las condiciones del sujeto bajo prueba con la sección where y las posibilidades de mocking incorporadas. Pero Spock usa el lenguaje Groovy. Es menos verboso, es dinámico pero que no posee igual la asistencia de código de los IDEs y por su naturaleza dinámica con posibilidad de errores de compilación no detectados hasta en tiempo de ejecución. En mis preferencias está el lenguaje Java así que he revisado si estás características de Spock son ofrecidas por JUnit desde la última vez que lo use.

El primer motivo de usar Spock sobre la legibilidad del test se puede suplir añadiendo un comentario de línea con la sección. El segundo motivo es que en JUnit también se pueden crear teses parametrizados con varios casos de prueba. Para los teses parametrizados se puede usar la anotación @ParameterizedTest con una serie de valores que en el test se reciben como un parámetro.

Aqui se compara el mismo test usando Spock y luego JUnit.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import spock.lang.Specification
class StringLengthCalculator {
int length(String string) {
return string.length()
}
}
class StringLengthCalculatorSpock extends Specification {
def "calculate string length"() {
given:
def calculator = new StringLengthCalculator()
expect:
def result = calculator.length(a)
then:
expected == result
where:
a | expected
"" | 0
"java" | 4
"groovy" | 5
"go" | 2
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.provider.Arguments;
public class StringLengthCalculator {
public int length(String string) {
return string.length()
}
}
public class StringLengthCalculatorTest {
@ParameterizedTest
@DisplayName("calculate string length")
@ValueSource(strings = { "", "java", "groovy", "go" })
void lengthOfStrings(String a) throws Exception {
// given
 StringLengthCalculator calculator = new StringLengthCalculator();
// expect
 int result = calculator.length(a);
// then
 Assertions.assertEquals(expected, result);
}
}

O si la parametrización es más compleja usando un método que devuelve una lista de parámetros en Junit.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import spock.lang.Specification
class Calculator {
int add(int a, int b) {
return a + b
}
}
class CalculatorSpock extends Specification {
def "calculate sum"() {
given:
def calculator = new Calculator()
expect:
def result = calculator.add(a, b)
then:
expected == result
where:
a | b | expected
1 | 3 | 4
2 | -1 | 1
0 | 6 | 6
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
public class CalculatorTest {
@ParameterizedTest
@DisplayName("calculate sum")
void lengthOfStrings(int a, int b, int expected) throws Exception {
// given
 Calculator calculator = new Calculator();
// when
 int result = calculator.add(a, b);
// then
 Assertions.assertEquals(expected, result);
}
static Stream<Arguments> lengthOfStrings() {
return Stream.of(
Arguments.arguments(1, 3, 4),
Arguments.arguments(2, -1, 1),
Arguments.arguments(0, 6, 6),
);
}
}

Con estas posibilidades de JUnit y para hacer mocking con Mockito realmente los dos motivos que tenía para usar Spock no son imprescindibles además de disponer de un lenguaje con buena asistencia de código en los IDEs. También para los teses igualmente se aplican las 10 razones que tengo para seguir usando Java.

» Leer más, comentarios, etc...

Picando Código

Actualización del validador de Cédula de Identidad Uruguaya

junio 05, 2019 09:01

Hace unos años creé mi primera gema en Ruby: Un validador de cédulas de identidad uruguaya. Poco más de un año después, escribí una versión en JavaScript. Desde entonces han surgido un montón de versiones más del validador de cédulas de identidad en distintos lenguajes. Recientemente actualicé el sitio web del proyecto: Validación Cédula Identidad Uruguaya

Validador de Cédulas de Identidad Uruguaya

La idea principal de las bibliotecas en Ruby, JavaScript y demás, es validar números de cédula ingresador por un usuario en un sitio/aplicación. La motivación de escribir la primera versión fue la cantidad de sitios uruguayos que lo hacen mal (ver entrada). Así que este sitio web utiliza el JavaScript y sirve para:

  • Ver si un número de cédula es válido
  • Obtener el dígito verificador de un número de cédula
  • Generar números de cédula al azar.

Además de una lavada de cara (o CSS y markup) al sitio, agregué las dos versiones más recientes del validador de cédulas: PHP y Go. La versión de Go la escribí el año pasado como ejercicio mientras aprendía Go para el trabajo. Qué lenguaje que no me gustó… Tanto que me había olvidado que escribí el validador de cédulas en Go, hasta que me puse a escribir esto.

Así que por el momento hay implementaciones open source para: Ruby, Python, JavaScript, jQuery, Node, PHP, y Go.
Si falta la versión en su lenguaje de programación favorito, y se animan a escribirlo y publicarlo bajo código libre ¡contáctenme que lo agrego!

» Leer más, comentarios, etc...

Picando Código

NotPinkCon 2019 – vuelve la conferencia de seguridad informática en Buenos Aires, Argentina

junio 05, 2019 07:36

NotPinkCon

NotPinkCon es un evento gratuito y abierto a todo público, donde se brindan conferencias técnicas en seguridad informática, impartidas por mujeres y dirigidas a estudiantes, profesionales y entusiastas del tema. Las charlas serán de nivel técnico variado, útiles tanto para quienes recién comienzan o aún no lo han hecho pero tienen interés en aprender, como para quienes tienen más experiencia.

El evento se realiza el 30 de agosto,  en Buenos Aires, Argentina.

El team de NotPinkCon está compuesto por un grupo de mujeres especialistas en seguridad informática, reconocidas tanto por sus investigaciones, como por sus presentaciones en diversos eventos alrededor del mundo y su gran ambición en lograr la igualdad de género en el área de la tecnología. Conocé al staff

Si querés ser oradora en NotPinkCon, está abierto el llamado a charlas con fecha límite el 15 de julio. Los temas para los que están buscando oradoras son: seguridad ofensiva y defensiva en todas las áreas de infosec. Por ejemplo: Seguridad en apps web, hacking móvil, hacking IoT, Reversing, escribir exploits, seguridad wireless, hacking de hardware, malware, ataques criptográficos y así. Ofrecen a las oradoras cubrir los gastos por el viaje hasta USD 1.500 y dos noches de alojamiento, entrada VIP, cena de oradoras y acceso a fiestas.

Ya podés registrar tu entrada, el evento es abierto a todo público, pero tiene una capacidad limitada.

Pueden seguir a @NotPinkCon en Twitter para estar al tanto de todas las novedades.

Objetivo de NotPinkCon

El principal objetivo de NotPinkCon es incentivar a más mujeres a participar como speakers en los eventos de seguridad informática. Creemos que todas tienen investigaciones interesantes para contar y queremos romper las barreras que les impidan hacerlo.

Para ello proponemos un escenario más confortante, que funcione como un escalón intermedio antes de dar el salto de presentarse en un escenario mixto, el cual -por la desafortunada desigualdad de género- podría resultar algo intimidante para comenzar.

Asimismo también queremos contar en nuestro escenario con oradoras experimentadas. Sin lugar a dudas, su experiencia en el escenario no solo será de ayuda para las nuevas speakers sino también de gran inspiración para la audiencia.

Nuestro segundo objetivo es incentivar a las mujeres a unirse al fascinante mundo de la seguridad informática. Hemos notado que el hecho de que las oradoras sean mujeres incrementa el interés del género, lo cual se ve reflejado positivamente en una audiencia de gran paridad entre hombres y mujeres.

En resumen, NotPinkCon tiene los siguientes objetivos:

  • Incentivar la participación de mujeres como speakers en los eventos.
  • Incrementar el interés de las mujeres por la Seguridad Informática.

Visitá el sitio web para ver más.

» Leer más, comentarios, etc...

Navegapolis

Una buena idea sin un buen "product owner" es sólo una ilusión

mayo 31, 2019 10:49

poUna buena idea no es un buen producto.

Faltaría añadir: "si no hay detrás un buen propietario de producto". Escuchando a Steve Jobs explicar porqué Apple dejó de hacer buenos productos cuando él se fue, igual es "agile-tendencioso" pensar que perdieron al product owner, pero si lo que dejaron fue a la idea con el equipo, quitando a quien tenía la visión, conocía el producto y analizaba de forma continua su evolución, quizá no fuera el product owner, pero se le parecía mucho.

 

 

 

 

» Leer más, comentarios, etc...

Navegapolis

Una buena idea sin un buen "product owner" no es un buen producto

mayo 31, 2019 10:49

poUna buena idea no es un buen producto.

Faltaría añadir: "si no hay detrás un buen propietario de producto". Escuchando a Steve Jobs explicar porqué Apple dejó de hacer buenos productos cuando él se fue, igual es "agile-tendencioso" pensar que perdieron al product owner, pero si lo que dejaron fue a la idea con el equipo, quitando a quien tenía la visión, conocía el producto y analizaba de forma continua su evolución, quizá no fuera el product owner, pero se le parecía mucho.

 

 

 

 

» Leer más, comentarios, etc...

Blog Bitix

Implementar la paginación eficientemente en consultas SQL con seek

mayo 31, 2019 04:00

Las aplicaciones que muestran listados probablemente necesiten mostrarlos paginados. Sin embargo, implementar la paginación correctamente para que sea eficiente no pasa por emplear las clausulas limit ni offset que habitualmente se utilizan sino con seek. Además de que limit y offset da lugar a resultados no deseados si entre obtención de página y página se insertan filas en páginas anteriores.

jOOQ
Java

La paginación habitualmente se implementa con las palabras reservadas limit y offset del lenguaje SQL pero esto es ineficiente ya que para llegar los resultados de las últimas páginas la base de datos ha de recuperar antes todos los resultados anteriores. Cuando hay varios cientos de miles o millones de filas en una tabla esto es ineficiente y hace que las consultas sean lentas y añadan una importante carga al servidor de base de datos que al final afecta al rendimiento de la aplicación o su capacidad para atender a gran número de peticiones.

Además del rendimiento, otro problema de la paginación con limit y offset es que si se insertan filas en una página anterior mientras se están recorriendo los resultados, uno o varios resultados al avanzar por las páginas en la típica tabla de resultados en una aplicación web podría aparecer dos veces, en un proceso automatizado sería aún peor ya que un resultado podría procesarse dos veces.

La paginación con limit y offset permite ir a una página directamente en una búsqueda pero en los casos que hay miles de resultados realmente a un usuario no le interesa ir a una determinada página cuando hay cientos de páginas, en un proceso automatizado ir a una página en concreto tampoco suele ser un requerimiento.

jOOQ con la cláusula seek permite hacer la paginación eficiente de una forma cómoda. La técnica se basa en ordenar los resultados por unas determinadas columnas y filtrar por condición where sobre las mismas columnas los resultados anteriores, los valores por los que se filtra en la condición son los obtenidos de la última página.

Haciendo seek la paginación es más eficiente ya que la base de datos no necesita recuperar los datos de las páginas anteriores ya que los descarta utilizando la cláusula where que al final es en lo que se traduce la clausula seek. Y se elimina el problema de que se inserten datos en páginas anteriores y alguno se pudiese aparecer dos veces en los resultados ya que manteniendo la ordenación de la conlsulta si se insertan filas en páginas anteriores no afectarán a las páginas siguientes.

1
2
3
4
5
6
7
8
9
import org.jooq.DSLContext;
...
private DSLContext context;
...
context.selectFrom(Tables.PRODUCTO)
.orderBy(PRODUCTO.CANTIDAD, PRODUCTO.ID)
.seek(3l, 2l)
.limit(10)
.fetchInto(Producto.class);

La SQL generada por jOOQ es la siguiente donde la clausula seek se añade como una condición en la cláusula where. El campo de la clausula seek coincide con el campo del criterio de ordenación, el operador mayor que en la condición coincide también con el orden ascendente del order by.

1
2
3
4
 select "PRODUCTO"."ID", "PRODUCTO"."NOMBRE", "PRODUCTO"."DESCRIPCION", "PRODUCTO"."CANTIDAD", "PRODUCTO"."FECHA"
from "PRODUCTO"
where ("PRODUCTO"."CANTIDAD", "PRODUCTO"."ID") > (cast(? as bigint), cast(? as bigint))
order by "PRODUCTO"."CANTIDAD", "PRODUCTO"."ID" limit ?

Para que el seek sea correcto los valores de las filas para los campos que forman parte del seek han de ser únicos por eso entre los campos de los ejemplos se incluye el identificativo de la fila, que siempre se incluye como último campo si hubiese más criterios de ordenación y valores para el seek.

Si además se tiene un índice para los campos que forman parte del seek el rendimiento será muy alto y la diferencia entre la primera y la última página mínimo, además se puede considerar independiente del número de filas de la tabla. Son varias las ventajas perdiendo solo la capacidad de navegar a cierta página pero en la mayoría de los casos esto es asumible.

En los siguientes interesantes artículos se comenta detalladamente como implementar la paginación eficientemente y se dan más detalles.

» Leer más, comentarios, etc...

Una sinfonía en C#

IoT bases: Introducción a MQTT

mayo 27, 2019 10:00

En este post vamos a comenzar a hablar sobre uno de los protocolos más utilizados para IoT, MQTT y conocer sus principales características, por qué es interesante para IoT, qué aporta, y cómo funciona.

https://www.penninkhof.com/wp-content/uploads/2015/03/MQTT.png

¿Qué es MQTT?

MQTT son las siglas de Message Queuing Telemetry Transport, es un protocolo pensado para telemetría, algunas cosas para remarcar:

  • Funciona sobre TCP.
  • Existen variantes sobre UDP y Bluetooth (por supuesto, con otras características de transporte).
  • Utiliza el patrón Publish/Suscribe.
  • Hace muy simple la comunicación entre múltiples dispositivos.
  • Diseñado para dispositivos pequeños y de bajo consumo de energía y ancho de banda.
  • Bidireccional.

Pubish/Suscribe

Bien, vamos por lo primero, decimos que MQTT es Publish/Suscriber, esto tiene varias implicaciones:

  • Si queremos recibir un mensaje tenemos que suscribirnos a un “tema”.
  • Si queremos publicar un mensaje lo hacemos a un “tema”.
  • No podemos enviar un mensaje directamente a otro cliente.
  • No podemos saber quién nos ha enviado un mensaje.
  • Si hay muchos dispositivos suscritos a un “tema” recibirán todos el mismo mensaje.
  • Si estamos suscritos a un “tema” recibimos todos los mensajes publicados por cualquier cliente a ese “tema”.

Por ejemplo, podemos suscribirnos al tema “casa” y cada vez que algún cliente publique un mensaje en ese tema lo recibiremos; del mismo modo, siempre que enviemos un mensaje tendremos que indicar el “tema” y este mensaje será recibido por todos los suscriptores (y nosotros no sabremos quiénes son).

Una de las ventajas de este modelo es que mucha responsabilidad no la gestionan los clientes y esto hace que sea simple desde el punto de vista del dispositivo.

El corazón de MQTT, el Broker.

Una cosa importante de señalar es cómo nos conectamos, como nos suscribimos y cómo publicamos; bien, los clientes MQTT nunca se conectan entre sí sino que se conectar a un Broker, que no es más que un intermediario que se encarga de conocer a todos los clientes y repartir los mensajes según las suscripciones. Hace algunas cosas más que iremos viendo.

Entonces, el Broker no es más que una aplicación que tiene que estar instalada y funcionando todo el tiempo, muchas de las ventajas de MQTT relacionadas con el bajo consumo de recursos tienen que ver con que el Broker es responsable de muchas cosas, y es donde reside la mayor parte de la complejidad del protocolo.

El diagrama de funcionamiento seria éste:

 

By Simon A. Eugster - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=70622928

Como vemos en el diagrama, el Client A se conecta al Broker y luego se suscribe al "tema” (Topic) temperature/roof, al lado derecho vemos el Client B que ya se encontraba conectado y publicó un mensaje sobre el mismo Topic, por lo tanto el Broker a ver que Client A se suscribe al Topic le entrega el mensaje. Más adelante Client A publica sobre el mismo Topic la temperatura actual (20º), recibe otro mensaje del Broker (30º), responde lo mismo y se desconecta.

Este diagrama podría perfectamente el funcionamiento de un termostado de una casa que recibe la configuración de temperatura de alguien más.

Nótese que el primer mensaje publicado por Client B dice “retain” esto es un flag especial que hace que ese mensaje quede retenido de modo que todos los nuevos suscriptores lo reciban ni bien se conectan el Topic, esto es especialmente útil cuando los clientes solo se conectan cuando necesitan interactuar con el Broker y de este modo ahorrar energía.

Topics

Los “temas” que nombramos antes se llaman Topics y se organiza en niveles, del siguiente modo:

sport/tennis/player1

De este modo podemos organizar los mensajes, ya que tenemos la posibilidad de suscribirnos a un Topic o a diferentes niveles gracias a los willcards, por ejemplo:

sport/# suscribe a todos los niveles debajo de sport

sport/#/ranking suscribe a todos los ranking de todos los deportes

por otro lado el + permite usar un willcard de un único nivel

sport/tennis/+/ranking suscribe a todos los rankings, es decir el + solo reemplaza un nivel

Hay mucho más detalles al respecto en la especificación.

Calidad de servicio QoS

Existen 3 niveles de calidad en MQTT 0,1 y 2.

Estos niveles determinan con que rigurosidad el Broker debe asegurarse que los mensajes han sido entregados correctamente.

  • 0: Se envían el mensaje y no se espera ninguna verificación (fire en forget).
  • 1: Se espera al menos un ACK de cada clientes que debe recibir el mensaje (si el cliente tarda en responder es posible que se envíe dos veces el mismo mensaje).
  • 2: Se garantiza que cada cliente recibe el mensaje una única vez.

Evidentemente a medida que subimos la calidad los mensajes son más costosos a nivel recursos de energía y tiempo, mayormente se usa el nivel 0 o 1, de hecho muchos Brokers y clientes no implementan el nivel 2.

El nivel de calidad se puede definir al conectarse al Broker (el cliente decide qué nivel de validad quiere) o en cada mensaje enviando (quien envía decide el nivel)

Retención de mensaje (retain message)

Como vimos en el diagrama, esto no es más que un flag que hace que un mensaje en particular (podemos agregarlo en todos, en tal caso siempre se considera el último mensaje por Topic) sea retenido por el Broker de modo de que al suscribirse un nuevo cliente el Topic reciba este mensaje como primer mensaje, esto es muy útil para valores por defecto y cosas o cuando quien publica mensaje lo hace cada cierto tiempo, entonces el mensaje queda ahí y todo el que se conecte al Topic lo recibirá por más que quien generó el mensaje no se encuentre operativo.

Sesiones

Lo último de lo que vamos a hablar es de sesiones, es otra característica avanzada y básicamente hace que el Broker le asigne un número de sesión a cada clente (que se conecte, independientemente de si se suscribe a un Topic) y recuerde este identificador de sesión, de modo que si el cliente se desconecta (otra vez, porque se apaga para ahorrar energía o ocurre algo) pueda informar su sesión y el Broker continue por donde quedó. Otra vez, no todos los Broker y clientes lo implementan.

En la próxima entrada vamos a ver qué software podemos usar para hacer las primeras pruebas con MQTT.

Nos leemos.

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin