Retarder–Fixed and Random strategy and Tests-part 4

Now I want to make a second strategy : the random strategy between a min and a max value. This is only to minimize the impact on the extension that uses the Random – it is good to be hidden inside. For Random Strategy it is better to re-use the Fixed Strategy – and this is the code:

using System;
using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public class StrategyAwaitRandom : IStrategyAwait
    {
        StrategyAwaitFixed sf;
        public StrategyAwaitRandom(int min,int max)
        {
            if (min > max)
            {
                var x = min;
                min = max;
                max = min;
            }
            var random = new Random();
            var awaitTime = random.Next(min, max);
            sf = new StrategyAwaitFixed(awaitTime);
        }
        public Task<IStrategyAwait> AwaitDelayAfter()
        {
            return sf.AwaitDelayAfter();
        }

        public Task<IStrategyAwait> AwaitDelayBefore()
        {
            return sf.AwaitDelayBefore();
        }
    }
}

 

Also, I want to make some tests to verify the awaits. So I choose https://github.com/LightBDD/LightBDD and my tests looks like this:

using LightBDD.Framework;
using LightBDD.Framework.Scenarios;
using LightBDD.XUnit2;
using System;
using System.Threading.Tasks;
using Xunit;
[assembly: LightBddScope]

namespace NetCoreRetarderTest
{
    //https://github.com/LightBDD/LightBDD/wiki/Formatting-Parameterized-Steps

    [FeatureDescription(

   @"This will test just how 

will await a determined number of time
")]
    public partial class TestStrategyAwait
    {
        
        [Scenario]

        [Label("wait times fixed")]

        [Trait("Category", "Wait")]
        [InlineData(3)]
        [InlineData(1)]
        [InlineData(0)]
        [InlineData(-1)]

        public async Task WaitTime(int nrSeconds)

        {
           
            await Runner.RunScenarioAsync(

                    _ => When_Choosing_Fixed_Await_For_NRSECONDS_Seconds(nrSeconds),

                    _=> And_Await(),

                    _=> Then_Time_Ellapsed_Will_Be_NRSECONDS_Seconds(Math.Max(nrSeconds, 0))
                    )

                ;

        }
        [Scenario]

        [Label("wait times random")]

        [ScenarioCategory("Wait")]
        [InlineData(3,1)]
        [InlineData(1,2)]
        [InlineData(0,1)]
        [InlineData(-1,5)]

        public async Task WaitTimeRandom(int min,int max)

        {

            await Runner.RunScenarioAsync(

                    _ => When_Choosing_Random_Await_Between_MIN_And_MAX_Seconds(min,max),

                    _ => And_Await(),

                    _ => Then_Time_Ellapsed_Will_Be_Between_MIN_And_MAX_Seconds(Math.Max(min, 0),Math.Max(max,0))
                    )

                ;

        }
    }
}

 
You can find the code at https://github.com/ignatandrei/NetCoreRetarder/commits/version4_tests

Retarder – Making a strategy to wait–part 3

Now I want to make the await configurable. To remind , first is just a random delay ( or static delay). The others are

  1. Delay execution of some endpoints, based on how frequent are their uses

  2. Delay execution based on headers / query string /  routes
  3. Delay execution based on client IP
  4. Delay execution based on the response

The best design pattern for this is Strategy  https://en.wikipedia.org/wiki/Strategy_pattern . Also, because I want to apply multiple await types ( maybe in order, maybe to compose) , a simple Command Pattern will be enough.

So, let’s see how it looks now:

using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public interface IStrategyAwait
    {
        Task<IStrategyAwait> AwaitDelay();
    }
}

So now let’s see the implementation of a StrategyAwaitFixed

using System;
using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public class StrategyAwaitFixed : IStrategyAwait
    {
        private readonly int milliseconds;

        public StrategyAwaitFixed(int milliseconds)
        {
            this.milliseconds = milliseconds;
        }

        public async Task<IStrategyAwait> AwaitDelay()
        {
            Console.WriteLine($"waiting {milliseconds}");
            
            if (milliseconds < 1)
                return null;

            await Task.Delay(milliseconds);
            
            Console.WriteLine($"finished waiting {milliseconds}");
            
            return null;
        }
    }
}

Now the Retarder Middleware should apply the awaiter one by one

using Microsoft.AspNetCore.Http;
using System;
using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public class RetarderMiddleware : IMiddleware
    {
        private IStrategyAwait strategyAwait;

        public RetarderMiddleware(IStrategyAwait strategyAwait)
        {
            this.strategyAwait = strategyAwait;
        }

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            while (strategyAwait != null)
                strategyAwait = await strategyAwait?.AwaitDelay();
            
            await next(context);
            
        }
    }
}

The code from middleware extension is modified as generating a new random each time:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;

namespace NetCoreRetarderCore
{
    public static class RetarderExtensions
    {
        public static void AddRetarder(this IServiceCollection serviceCollection)
        {
            serviceCollection
                .AddTransient<IStrategyAwait, StrategyAwaitFixed>(
                    it =>
                    {
                        var random = new Random();
                        var awaitMilliseconds = random.Next(1, 1000);
                        return new StrategyAwaitFixed(awaitMilliseconds);
                    }
                );
            serviceCollection.AddTransient<RetarderMiddleware>();
        }
        public static void UseRetarder(this IApplicationBuilder app)
        {
            app.UseMiddleware<RetarderMiddleware>();
        }

    }
}

Full Code Source at https://github.com/ignatandrei/NetCoreRetarder/commits/version3_Making_a_strategy_to_wait

Retarder- reorganizing the project to easy use- part 2

Now it is the moment to start reorganizing the project to be easy to use by other programmers. I want, instead of registering the services manually, to can use .AddRetarder and .UseRetarder. So I create a new project, NetCoreRetarderCore.csproj , and move there the RetarderMiddleware . The only new thing is the extension class RetarderExtensions

 

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;

namespace NetCoreRetarderCore
{
    public static class RetarderExtensions
    {
        public static void AddRetarder(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddTransient<RetarderMiddleware>();
        }
        public static void UseRetarder(this IApplicationBuilder app)
        {
            app.UseMiddleware<RetarderMiddleware>();
        }

    }
}

 

This let’s me reorganize the Startup as

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NetCoreRetarderCore;

namespace NetCoreRetarder
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddRetarder();
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();
            app.UseRetarder();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

You can find code at https://github.com/ignatandrei/NetCoreRetarder/commits/version2_reorg

Retarder- idea and POC–part 1

I was thinking about a new project in .NET Core  – Retarder ( Delayer  seems to be better , but I digress ). What about a middleware in .NET Core, that delays execution of any request with 1 second ( or something between 1 millisecond and 1 second )? That way , if someone wants to improve the project , just remove the Retarder !

Other ( better ) uses are :

  1. Delay execution of some endpoints, based on how frequent are their uses
  2. Delay execution based on headers / query string /  routes
  3. Delay execution based on client IP
  4. Delay execution based on the response

 

But for the moment just stick with the static delay of all requests.

Seems to be a good idea to play with middleware. I started a new .NET Core 3.1 project and started coding .

You can find the first version at tag : https://github.com/ignatandrei/NetCoreRetarder/commits/version_1_just_data

The code for Retarder middleware is

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NetCoreRetarder
{
    public class RetarderMiddleware : IMiddleware
    {

        public RetarderMiddleware()
        {
        }

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var random = new Random();
            //var awaitMilliseconds =random.Next(10000, 100000);
            var awaitMilliseconds = random.Next(1, 1000);
            Console.WriteLine($"awaiting {awaitMilliseconds}");
            await Task.Delay(awaitMilliseconds);
            Console.WriteLine($"***awaited {awaitMilliseconds}");

            await next(context);
        }
    }
}


 

The code for using Retarder is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace NetCoreRetarder
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //what if we comment this ?
            //services.AddSingleton<RetarderMiddleware>();
            services.AddTransient<RetarderMiddleware>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();
            //app.UseMiddleware(typeof(RetarderMiddleware));
            app.UseMiddleware<RetarderMiddleware>();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Some questions :

1.  What is the difference if we have services.AddSingleton  vs services.AddTransient for the middleware ?

2. What if we comment both ?

3. What if we do call later ( or before ) app.UseMiddleware ?

Covid Data–more enhancements to GUI–and Post Mortem–part 6

Once we have all data from the backend  set , it is a matter of time to have more display options. For example, I have added % of the current day vs previous day. Also a display into table . https://ignatandrei.github.io/WFH_Resources/covidDataTable .

 

Post Mortem

  • Is  better to grab data from the source , if possible
  • Is  better to have a CI / CD in place, with the source control
  • GitHub uses are tremendous –  hosting sites that do not need a backed, store data as database, running integrations, seeing how many users see your app
  • I do regret some choices that were imposed by the API and I pay now.  A month of programming can save 1 day of architecture
  • I am not so proud about how it looks the code. I just have written, without thinking at nothing but the GUI. So in some parts is cluttered
  • The project can go on and on , adding more display ( see https://github.com/ignatandrei/wfH_Resources/issues  if you want to participate)

Covid Data -CI/ CD–part 5

Now I need that every data ( either from the github .md file, either gathered from https://github.com/CSSEGISandData/COVID-19/ ) be updated into the site.

Github Actions to the rescue!

I have created 2 different workflows : one for gathering the data from the https://systems.jhu.edu/research/public-health/ncov/ , the other for compiling the .md files and the Angular app. Thinking more, I should have had the Angular compiled into another workflow that was called after each one – but this is more AzureDevOps , than GitHub Actions.

1. Workflow for .md and for Angular

You can find the source at https://github.com/ignatandrei/WFH_Resources/blob/master/.github/workflows/blank.yml . It uses a Docker file to compile the Angular: https://github.com/ignatandrei/WFH_Resources/blob/master/makeData/compile.txt and a Docker batch to run the docker file and gather the results : https://github.com/ignatandrei/WFH_Resources/blob/master/makeData/compile.bat

Uses all the previous js and TypeScript files to transform and load data. Copies the data into the docs folder ( to be public available ) and then commits the data back to Github

It is run on both push at a cron job

on:

push:

pull_request:

schedule:

– cron: ‘5 4 * * *’

2. Workflow for gather last country data for Covid

You can find the source at https://github.com/ignatandrei/WFH_Resources/blob/master/.github/workflows/allnewdata.yml .It uses also a Docker file to compile data and run https://github.com/ignatandrei/WFH_Resources/blob/master/makeData/addNew.txt 

And a docker compile file https://github.com/ignatandrei/WFH_Resources/blob/master/makeData/addNew.bat to gather the data from docker.

Also the workflow commits data to github.  It is run at a cron job:

on:

schedule:

– cron: ‘5 2 * * *’

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.