Category: TILT

TILT- TimeZone -part 22

To see if two TILTS are in the same day, it is not enough to see if the difference is less than one day. Could be less than 1 day in UTC – but in Europe/Bucharest can be the next day ( suppose that I put one TILT at 23:59:59 local time – the next TILT could be in 2 seconds )

In JavaScript one can have the IANA codes ( https://en.wikipedia.org/wiki/List_of_tz_database_time_zones ) by

Intl.DateTimeFormat().resolvedOptions().timeZone

In C# there I am receiving this and convert to TimeZoneInfo

var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
if (tz == null)
    return NotFound("cannot find timezone:" + timeZone);
var ser = tz.ToSerializedString();

Also, now we can verify to see that the time zone difference is ok

var tz = TimeZoneInfo.FromSerializedString(timezone);
if (tz == null)
    throw new TimeZoneNotFoundException(" cannote deserialize " + timezone);

var localTimeNow= TimeZoneInfo.ConvertTimeFromUtc(dateNowUTC, tz);

And the fact that I was thinking later – that means

  1. Database modification ( alter table , update )

  2. Backend verifications modified

  3. Sending data modified

  4. Tests added

And , of course, does not work in production . Added Hellang.Middleware.ProblemDetails to can debug more easy the error ( instead of 500 )

That comes from updating the existing database with default values

So reverting to the default timezone ( e.g. Europe/Bucharest) and now the tests are failing into CI ( that has not the time zone , being a stripped linux)

Adding to the CI

- name: Setup timezone
    uses: zcong1993/setup-timezone@master
    with:
    timezone: Asia/Shanghai

and now it works

Tools used

Visual Studio

Visual Studio Code

SSMS

Hellang.Middleware.ProblemDetails

zcong1993/setup-timezone@master

TILT-calendar- part 18

To see the progress , it is good to have a calendar.Discovered angular-calendar – to show events.

The exemaple at https://mattlewis92.github.io/angular-calendar/#/kitchen-sink is pretty self explanatory.

However, another 3 hours passed to have the display.

The ( quasy – final ) result can be seen below ( taken from http://tiltwebapp.azurewebsites.net/AngTilt/tilt/public/ignatandrei )


Tools

VSCode

angular-calendar

TILT-caching- part 17

If you want to read about, see https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside

So , the public TILTS must be cached. Also, when a user makes a new TILT, the cache should be destroyed

The code for Memory Cache is very simple:

  1. Add to services
builder.Services.AddMemoryCache();
  1. Add an MemoryCache cache to the constructor
public MyTilts(IMemoryCache cache, 
  1. add caching to the function
 if (cache.TryGetValue<TILT_Note_Table[]>(urlPart, out var result))
            {
                return result;
            }
            
//code to retrieve
cache.Set(urlPart, ret,DateTimeOffset.Now.AddMinutes(10));
return ret;
  1. add destroy cache
await insert.InsertTILT_Note(noteOrig);
note.CopyFrom(noteOrig);
cache.Remove(url);   
  1. Modify tests to see this feature

TILT- cosmetic–part 16

The user must know something about the site. One of easy way is to display an intro – and I have choosed https://github.com/shipshapecode/shepherd

Also

  • display number of chars when putting a TILT

  • display a text area instead of an input
  • display links to my TILTS

Added also https://codescene.io/ . Can be valuable – but I should understand more how to use it.

Display latest date when it was deployed / compiled with AMS and the repo name. Added also links to Swagger and Blockly Automation

To see the progress , it is good to have a calendar.Discovered angular-calendar – to show events.

The exemaple at https://mattlewis92.github.io/angular-calendar/#/kitchen-sink is pretty self explanatory.

However, another 3 hours passed to have the display.

The ( quasy – final ) result can be seen below ( taken from http://tiltwebapp.azurewebsites.net/AngTilt/tilt/public/ignatandrei )


Tools

Tools Used

https://github.com/shipshapecode/shepherd

VSCode

https://github.com/ignatandrei/RSCG_AMS

Visua Studio

angular-calendar

TILT- GUI–part 15

Now the easy way for me is to create a web interface with Angular.

Nothing too complicated. This is how it looks for the TILTS at https://tiltwebapp.azurewebsites.net/AngTilt/tilt/public

Now , if I am going to a TILT Url , I can see the TILTs in the browser: https://tiltwebapp.azurewebsites.net/AngTilt/tilt/public/ignatandrei

For creating new TILTS , I can use the web interface https://tiltwebapp.azurewebsites.net/AngTilt/tilt/my

I enter a user name and a pasword and I can start adding the TILTS.

Tools Used

VSCode

Angular

TILT–CodeCoverage–part 14

The code coverage should be simple – just run tests and that’s all, right ?

For a reason, the coverlet console , dotnet-coverage ,dotnet test do not work – means that code coverage generated was 0% ( I have verified with dotnet-reportgenerator-globaltool )

The only that worked was altCover and this is the code into the yml:

dotnet test --no-build src/backend/Net6/NetTilt/NetTilt.sln  /p:AltCover=true /p:AltCoverAssemblyExcludeFilter="Humanizer|Moq|NUnit3|System|DB2GUI|AltCover|NetTilt.Tests" 

It was a good exercise. And now I have this: codecov

And for some tests written already, the code coverage is 46 %. A good one, I think …

Tools used

https://codecov.io

Moq

NUnit3

AltCover

Visual Studio

TILT–Analyzing code–part 13

I want a static analysis tool that can be used to check the quality of the code. SonarCloud.io is a great tool for this.

You can find the instructions for the tool here: https://sonarcloud.io/dashboard/index/organization/sonarcloud/project/sonarcloud-project-id

It was easy to set up – and it is free. You can see the results here: https://sonarcloud.io/summary/overall?id=ignatandrei_TILT

This is the result

Two were a false positive

var c = this.User?.Claims.ToArray();
if ((c?.Length ?? 0) == 0)
    return -1;
return c.Length;

and one was a problem ( comparing bool with null).

There was a security hotspot – based on Configuring in EF . No problem here.

Solving most of code smells were ok.

I have added also a badge Maintainability Rating

Tools used

https://sonarcloud.io

GitHub Actions

TILT- Tests- part 12

First, it is a decision between NUnit and XUnit. I took this time NUnit. Also, I take LightBDD to show data.

Let’s say I want to verify the rule that the user cannot make more than 1 TILT per day.

In order to do 1 TILT per day

  1. User must be authenticated and have have an URL registered into the database

  2. Code must verify that is no TILT for today ( i.e UTC Date)
  3. Code must insert the TILT into the database

I can work with a real database, but let’s see how we can Mock it – and run without database

This is the setup

namespace NetTilt.Tests;

[FeatureDescription(@"Test add a new TILT")]
[Label("FakesMocks")]
public partial class TestAdd
{
    ServiceProvider? serviceProvider;
    
    [SetUp]
    public void Setup()
    {

    }
    [Scenario]
    public async Task NewTilt() //scenario name
    {
       await Runner
            .AddSteps(_ => Given_No_TILT_For_URL())
            .AddAsyncSteps(_ => Then_Can_Add_A_New_TILT())
            .RunAsync();
    }
    [Scenario]
    public async Task Existing_TILT_Start_Of_The_Day() 
    {
        var minutes = DateTime.UtcNow.Subtract(DateTime.UtcNow.Date).TotalMinutes;
        minutes--;
        await Runner
              .AddSteps(_ => Given_Today_Is(DateTime.UtcNow))
             .AddSteps(_ => Given_Exists_One_TILT_ForDate(DateTime.UtcNow.AddMinutes(-minutes)))
             .AddAsyncSteps(_ => Then_Can_NOT_Add_A_New_TILT())
             .RunAsync();
    }
    [Scenario]
    [TestCase(2)]
    [TestCase(20)]
    [TestCase(1)]
    public async Task ExistingTILT_DaysAgo(int days) //scenario name
    {
        await Runner
              .AddSteps(_ => Given_Today_Is(DateTime.UtcNow))
             .AddSteps(_ => Given_Exists_One_TILT_ForDate(DateTime.UtcNow.AddDays(-days)))
             .AddAsyncSteps(_ => Then_Can_Add_A_New_TILT())
             .RunAsync();
    }
    [Scenario]
    [TestCase(1,false)]
    [TestCase(2, false)]
    [TestCase(20, false)]
    [TestCase(100, false)]
    [TestCase(200, false)]
    [TestCase(4*60, false)]
    [TestCase(5 * 60, false)]
    [TestCase(6 * 60, false)]
    [TestCase(7 * 60, false)]
    [TestCase(8 * 60, false)]
    [TestCase(9 * 60, false)]
    [TestCase(10 * 60, false)]
    [TestCase(11* 60, false)]
    [TestCase(12 * 60, false)]
    [TestCase(23 * 60, false)]
    [TestCase(23 * 60+58, false)]
    [TestCase(24 * 60 + 1, true)]
    public async Task ExistingTILT_MinutesAgo(int minutes, bool canAdd) //scenario name
    {
        var run =  Runner
              .AddSteps(_ => Given_Today_Is(DateTime.UtcNow))
              .AddSteps(_ => Given_Exists_One_TILT_ForDate(DateTime.UtcNow.Date.AddDays(1).AddMinutes(-minutes)));

        if (canAdd)
            run = run
             .AddAsyncSteps(_ => Then_Can_Add_A_New_TILT());
        else
            run = run
             .AddAsyncSteps(_ => Then_Can_NOT_Add_A_New_TILT());

        await run.RunAsync();
    }
    [Test]
    public async Task TestAddOneTilt()
    {
        var myTilt= serviceProvider.GetRequiredService<IMyTilts>();
        var data= await myTilt.AddTILT(new TILT_Note_Table(), null);
        Assert.IsNotNull(data);
    }
}

And this is the output of the tests in markdown

Results of tests (Passed:65)

TestAdd

Existing TILT Start Of The
Day

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
00:01:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT DaysAgo [days:
“2”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “04/29/2022
06:52:44”]
Passed
3 THEN Can Add A New TILT Passed

ExistingTILT DaysAgo [days:
“20”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “04/11/2022
06:52:44”]
Passed
3 THEN Can Add A New TILT Passed

ExistingTILT DaysAgo [days:
“1”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “04/30/2022
06:52:44”]
Passed
3 THEN Can Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“1”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
23:59:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“2”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
23:58:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“20”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
23:40:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“100”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
22:20:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“200”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
20:40:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“240”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
20:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“300”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
19:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“360”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
18:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“420”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
17:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“480”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
16:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“540”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
15:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“600”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
14:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“660”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
13:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“720”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
12:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“1380”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
01:00:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“1438”] [canAdd: “False”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “05/01/2022
00:02:00”]
Passed
3 THEN Can NOT Add A New TILT Passed

ExistingTILT MinutesAgo [minutes:
“1441”] [canAdd: “True”]

Number Name Status Comments
1 GIVEN Today Is [date: “05/01/2022
06:52:44”]
Passed
2 AND Exists One TILT ForDate [date: “04/30/2022
23:59:00”]
Passed
3 THEN Can Add A New TILT Passed

NewTilt


Number Name Status Comments
1 GIVEN No TILT For URL Passed
2 THEN Can Add A New TILT Passed

Tools used

Nunit

LightBdd

Microsoft.Extensions.DependencyInjection

Microsoft.NET.Test.Sdk Moq

NUnit

Visual Studio

TILT- BlocklyScenario–part 10

Scenario for adding and retrieving the own TILTs

I needed a way to show to the front-end programmer how he should call the API.

One is to retrieve all the TILTS urls and the other is to add a new TILT, once authenticated

I have used NetCore2Blockly nuget package and configured with demoBlocks ( wwwroot\BlocklyAutomation\assets\showUsage\demoBlocks )

For authentication, this is the link https://tiltwebapp.azurewebsites.net/BlocklyAutomation/automation/loadexample/authenticate

For adding a new TILT , this is how you do it https://tiltwebapp.azurewebsites.net/BlocklyAutomation/automation/loadexample/newPost :

For see all public TILTS, this is how you do it ( https://tiltwebapp.azurewebsites.net/BlocklyAutomation/automation/loadexample/publicTilts )

Tools Used

NetCore2Blockly Visual Studio

TILT–Authentication and Authorization–part 9

This is the difficult part. I decide that , for the moment, I do not need in the application any fancy authorization – just a simple url + secret ( a.k.a password).

I decided also to use JWT – it is a simple way to add authentication + authorization to the application.

Let’s see the login code

            var data = await search.TILT_URLSimpleSearch_URLPart(SearchCriteria.Equal, url).ToArrayAsync(); ;
            if (data == null)
                return null;

            if (data.Length != 1)
                return null;

            var item = data[0];
            if (item.Secret != secret)
                return null;

            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(SecretKey);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                    {
                    new Claim(TokenId ,item.ID.ToString() ),
                    new Claim(ClaimTypes.Role, "Editor")
                    }),

                Expires = DateTime.UtcNow.AddMinutes(5), 
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            var ret = tokenHandler.WriteToken(token);
            return ret;

Nothing fancy – just SecretKey must be set in the appsettings.json file – and be enough large – otherwise an error occurs

For adding the TILTs to the own url , a custom authentication should be made. The code is not so simple, so I show here:

builder.Services.AddAuthorization(options=>

    options.AddPolicy("CustomBearer", policy =>
    {
        policy.AuthenticationSchemes.Add("CustomBearer");
        policy.RequireAuthenticatedUser();
    }));
builder.Services.AddAuthentication()
           .AddJwtBearer("CustomBearer",options =>
           {
              
               options.RequireHttpsMetadata = false;
               options.SaveToken = true;
               options.TokenValidationParameters = new TokenValidationParameters
               {
                   ValidateIssuerSigningKey = true,
                   IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
                   ValidateIssuer = false,
                   ValidateAudience = false
               };
               options.Events = new JwtBearerEvents()
               {
                   OnMessageReceived = ctx =>
                   {
                       if (!(ctx?.Request?.Headers?.ContainsKey("Authorization") ?? true))
                       {
                           ctx.NoResult();
                           return Task.CompletedTask;
                       };
                       var auth = ctx.Request.Headers["Authorization"].ToString();
                       if (string.IsNullOrEmpty(auth))
                       {
                           ctx.NoResult();
                           return Task.CompletedTask;
                       }
                       if (!auth.StartsWith("CustomBearer ", StringComparison.OrdinalIgnoreCase))
                       {
                           ctx.NoResult();
                           return Task.CompletedTask;
                       }

                       ctx.Token = auth.Substring("CustomBearer ".Length).Trim();
                       return Task.CompletedTask;

                   }
               };
           });

and the controller will be

    [Authorize(Policy = "CustomBearer", Roles = "Editor")]
    [HttpPost]
    public async Task<ActionResult<TILT_Note_Table>> AddTILT([FromServices] I_InsertDataApplicationDBContext insert, TILT_Note_Table note)
    {
        //TB: 2022-04-30 to be moved into a class - skinny controllers
        var c = this.User?.Claims.ToArray();
        var idUrl = auth.MainUrlId(c);
        if (idUrl == null)
        {
            return new UnauthorizedResult();
        }
        note.IDURL = idUrl.Value;
        note.ID = 0;
        note.ForDate = DateTime.UtcNow;
        var noteOrig = new TILT_Note();
        noteOrig.CopyFrom(note);
        await insert.InsertTILT_Note(noteOrig);
        note.CopyFrom(noteOrig);
        return note;

    }

The

        //TB: 2022-04-30 to be moved into a class - skinny controllers

is a time bomb comment- it will generate an error when compiling on the date.

Also, I have added CORS

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "AllowAll",
                      policy =>
                      {
                          policy.AllowAnyHeader().AllowAnyMethod().AllowCredentials().SetIsOriginAllowed(it => true);
                      });
});
//code
var app = builder.Build();
//code
app.UseCors("AllowAll");

in order for the front-end ,no matter where it is , to work.

I have put SetIsOriginAllowed(it => true) in the policy – works with credentials.

Tools Used

RSCG_TimeBombComment

QueryGenerator Visual Studio

BoilerplateFree – to generate interface from an existing class

System.IdentityModel.Tokens.Jwt – to generate the token and decrypt the token Visual Studio

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.