Hablemos sobre .Net y Azure

Mes: septiembre 2021

Unitesteando con EntityFramework y SQLite

Esta semana se llevo a cabo un evento increíble de la.cominidad de ASP.NET en Español donde tuve el placer (y honor) de hablar un poquito sobre Testing unitario.

En este caso vimos como escribir buenos tests para todas aquellos servicios (clases) que requieren un DbContext y para asegurarnos que nuestros tests se comporten siguiendo los mismos patrones que sigue ASP.NET.

Comenzamos escribiendo un test de integración contra una base de datos real, luego refactoeizamos el código para utilizar una base “en memoria” y finalmente utilizamos SQLite.

ASP.NET en Español – Unitesteando con EntityFramework y SQLite

Feature Flags personalizados

Hola a todos! Ya llevamos una serie de artículos dedicados a la implementación de Feature Flags. Entre otras cosas, vimos como utilizar algunos filtros condicionales que el framework nos provee y cómo aplicar middlewares y filtros MVC.

En el post de hoy veremos uno de los requerimientos más utilizados y sin los cuales los Feature Flags no serían tan poderosos: Feature Flags personalizados.

Contexto

Cuando analizamos los Feature Flags condicionales vimos como podemos aplicar algún tipo de lógica, que puede ser una ventana de tiempo (TimeWindowFilter) o un porcentaje de peticiones (PercentageFilter), para activar o desactivar funcionalidades. Claro quu si bien agregan valor, no son suficientes.

En ocasiones necesitamos tener un mayor control sobre como determinar si una funcionalidad se encuentra activa o no, incluso podemos necesitar consultar servicios externos o una base de datos.

Requerimiento

Como parte del desarrollo de nuestra estrategia de expansión de nuestro negocio eCommerce, queremos ofrecer una lista de productos especialmente seleccionado con un descuento especial promocional. Este descuento estará disponible solo los días viernes.

Implementación

Armar nuestro propio filtro requiere implementar la interface IFeatureFilter, la cuál define solo el método EvaluateAsync. Aquí podremos escribir nuestra lógica de negocio y retornar un valor booleano indicando si la funcionalidad se encuentra activa o no:

public interface IFeatureFilter : IFeatureFilterMetadata
{
    Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context);
}

Los pasos necesarios para completar nuestro filtro son:

  1. Implementar la interface IFeatureFilter
  2. Registrar el filtro
  3. Agregar la configuración
  4. Modificar la vista

IFeatureFilter

La idea de nuestro filtro es retornar true si el día de la semana es viernes, de lo contrario retornar false. Para hacerlo un poco más flexible, vamos a parametrizar el día de la semana de forma que podamos utilizarlo para otras promociones.

Crearemos una clase WeeklyOffersFeatureFilter e implementaremos la interface IFatureFilter. El código debería verse mas o menos así:

[FilterAlias("WeeklyOffersFilter")]
public class WeeklyOffersFeatureFilter : IFeatureFilter
{
    public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
    {
        var dayOfWeek = context.Parameters.GetValue<string>("DayOfWeek");
        var result = DateTime.Now.DayOfWeek.ToString() == dayOfWeek;

        return Task.FromResult(result);
    }
}

Analicemos los aspectos importantes de nuestro filtro:

  • [FilterAlias("WeeklyOffersFilter")]:
    Este atributo es necesario para indicarle al framework el nombre del filtro a utilizar. Es decir, “para el Feature Flag WeeklyOffers utilizar el filtro cuyo alias es WeeklyOffersFilter. Tiene que ser el mismo que utilizaremos en la configuración en el archivo appsettings.json.
  • context.Parameters.GetValue<string>("DayOfWeek"):
    Mediante el contexto podemos leer los parámetros establecidos en la configuración. En este caso estamos obteniendo el parámetro "DayOfWeek".
  • result:
    Simplemente comparamos el día de la semana actual contra el parámetro.

Registrar el filtro

Como siempre, solo tenemos que registrarlo utilizando el método de extensión AddFeatureFilter<>() en la clase Startup:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // ...

        services.AddFeatureManagement()
            .AddFeatureFilter<WeeklyOffersFeatureFilter>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ...
    }
}

Configuración

Finalmente necesitamos generar una nueva configuración en el archivo appsettings.json:

{
  // ...
  "FeatureManagement": {
    "WeeklyOffers": {
      "EnabledFor": [
        {
          "Name": "WeeklyOffersFilter",
          "Parameters": {
            "DayOfWeek": "Friday"
          }
        }
      ]
    }
  }
}

Lo mas importante aqui es la linea 7, donde indicamos el nombre del filtro a utilizar, que tiene coincidir con el valor del atributo [FilterAlias("WeeklyOffersFilter")] que utilizamos cuando implementamos la interface.

Vista

El único paso que nos queda pendiente es modificar la vista, esto ya lo hemos hecho en post anteriores. En nuestro caso modificamos la vista del menú principal (_Layout.cshtml) para incluir una nueva entrada cuando nuestro Feature Flag WeeklyOffers se encuentre activa.

<feature name="WeeklyOffers">
  <li class="nav-item">
    <a asp-controller="Home" asp-action="NewFeature">
        Ofertas semanales
    </a>
  </li>
</feature>

Conclusión

Poder implementar filtros personalizados nos da mucha flexibilidad a la hora de habilitar funcionalidades. Por ejemplo, si nuestra funcionalidad depende de otro servicio que puede no estar funcionando o no estar activo aún, podríamos ejecutar un recuest a dicho servicio y devolver true si y solo si el request es exitoso.

En el próximo post veremos como combinar Feature Flags con Azure App Configuration para guardar nuestras configuraciones de forma centralizada y segura.

Feature Flags: Middlewares y filtros MVC

De a poco estamos descubriendo todo el potencial que los Feature Flags tienen parea ofrecernos, aprendimos los conceptos principales, como usar Feature Flags condiciones y como manejar Features Flags inactivas.

En esta ocasión veremos como aplicar aplicar filtros MVC y middlewares solo si determinada funcionalidad se encuentra activa.

Filtros MVC

Los filtros MVC nos permiten agregar funcionalidad en forma muy versátil y dinámica como parte del procesamiento de una petición o request. Una de las principales características es que podemos agregar funcionalidad sin necesariamente modificar un controlador o acción.

Los Feature Flags nos van a permitir aplicar filtros MVC solo si una funcionalidad determinada se encuentra activa.

Requerimiento

Tenemos un filtro MVC AddBetaHeaderFilter que agrega una cabecera especial a la petición indicando que la versión en ejecución es "Beta". De esta forma otros componentes pueden realizar modificaciones a la petición según la versión.

public class AddBetaHeaderFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, 
        ActionExecutionDelegate next)
    {
        context.HttpContext
            .Request
            .Headers
            .Add("X-Version", "Beta");

        await next();
    }
}

Necesitamos aplicar este filtro solo y solo si el Feature Flag "Beta" se encuentra activo.

Implementación

Por suerte para nosotros solo necesitamos agregar el filtro a los filtros generales de MVC utilizando el helper AddForFeature<>. Para esto modificaremos la llamada a services.AddControllersWithViews() del método ConfigureServices de la clase Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
        options.Filters.AddForFeature<AddBetaHeaderFilter>("Beta"));

    services.AddFeatureManagement();
    // More services...
}

Middlewares

Los middlewares son muy similares a los filtros MVC ya que nos permiten manipular las peticiones y las respuestas. A diferencia de los filtros, estos no nos brindan acceso al contexto de MVC.

También podemos aplicar middlewares al pipeline utilizando Feature Flags.

Requerimiento

Continuando con el ejemplo anterior, tenemos un middleware AddBetaHeaderMiddleware que agrega la misma cabecera “Beta” que agregamos con el filtro MVC de la sección anterior.

public class AddBetaHeaderMiddleware
{
    private readonly RequestDelegate _next;

    public AddBetaHeaderMiddleware(RequestDelegate next) =>
        _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        context
            .Request
            .Headers
            .Add("X-Version", "Beta");

        await _next(context);
    }
}

Necesitamos aplicar este middleware solo y solo si el Feature Flag "Beta" se encuentra activo.

Implementación

Para aplicar este filtro, tendremos que modificar el método Configure de la clase Startup, donde utilizaremos el método de extensión ya provisto por el framework UseMiddlewareForFeature<>().

public void Configure(
    IApplicationBuilder app,
    IWebHostEnvironment env)
{
    // More middlewares...

    app.UseMiddlewareForFeature<AddBetaHeaderMiddleware>("Beta");

    app.UseEndpoints(endpoints => {/**/});
}

El orden de los factores sí altera el producto

Tener en cuenta que los middlewares se ejecutan en el orden en el que se agregan. De modo tal, si en el ejemplo anterior registramos el middleware después de app.UseEndpoints(), el Feature Flag no funcionaría.

Conclusión

Paso a paso vamos descubriendo todo el potencial de los Feature Flags y como utilizarlos para lograr un desarrollo realmente ágil e incremental. En este post aplicamos middlewares y filtros.

En el próximo post iremos un poquito mas a fondo e implementaremos un Feature Flag personalizado.

© 2021 Facu The Rock

Tema por Anders NorenArriba ↑