Friday links 420

1

Angular – Routing

https://angular.io/tutorial/toh-pt5

2

forkJoin · learn-rxjs

https://www.learnrxjs.io/operators/combination/forkjoin.html

3

GDC Vault – 1,500 Slot Machines Walk into a Bar: Adventures in Quantity Over Quality

https://www.gdcvault.com/play/1025766/1-500-Slot-Machines-Walk

4

Adding a new teller | queuing theory example

https://www.johndcook.com/blog/2008/10/21/what-happens-when-you-add-a-new-teller/

5

nosir/cleave.js: Format input text content when you are typing…

https://github.com/nosir/cleave.js

6

ASP.NET Core 3.0 Exception Handling – Simple Talk

https://www.red-gate.com/simple-talk/dotnet/net-development/asp-net-core-3-0-exception-handling/

7

If the Moon Were Only 1 Pixel – A tediously accurate map of the solar system

http://joshworth.com/dev/pixelspace/pixelspace_solarsystem.html

CORS and Programmer Types

There are many types of programmers  – and I would think that I can give an example about how they think differently. 

My example will be with CORS  – that means, accepting requests  from other site(s) . In ASP.NET  , there is a simple way to accept anything ( see tutorial at https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-5.0 )

.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()

And this will be , usually, enough. But , later on, you want to know WHO calls your API – so you add

.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials()

And you think that it is ok – but, at running, an error occurs.

The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time. Configure the CORS policy by listing individual origins if credentials needs to be supported.

What do you do  ?  Depending on your answer , you are a different kind of programmer. Think – and read below ( or add to the comments )

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

1. The Search Programmer

This will google/bing  the error and find ( eventually – or not !  ) https://jasonwatmore.com/post/2020/05/20/aspnet-core-api-allow-cors-requests-from-any-origin-and-with-credentials   . He can fall back to 3 type.

2. The DIY /NIH  Programmer

This will study the CORS protocol for many days . Then he will make his code to solve the problem.

3.. The Framework / Documentation  Programmer

This will think – there have to be a way- the people that have made the framework will be better. So he will read all the functions and he will see https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.cors.infrastructure.corspolicybuilder.setisoriginallowed?view=aspnetcore-5.0 .  He can fall back to 1 type if he cannot find an answer.

What kind of programmer are you ? ( I am the 3rd kind )

What I should return from WebAPI ?

 

There are several approaches when returning code from WebAPI .  Let’s frame the problem: say we have a Person controller with 2 actions:

– a GET {id}  – that retrieves the Person with id

– a POST {Peron}  – that tries to verify the validity of the Person and then saves to database.

We will answer to some questions:

1.What we will return if the person with id does not exists ?

2. What happens with the validation about the Person  ?
Let’s see the Person class.

public class Person: IValidatableObject
{
    public int ID { get; set; }
    public string  Name { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        yield return new ValidationResult("not valid not matter what");
    }
}

 

First approach: Do nothing

What I mean is just return the Person , even if it is null.

1.1. The GET

[HttpGet("{id}")]
public Person GetPerson(int id)
{
var p = RetrieveFromDatabase(id);
return p;
}

The problem is that when we return null from an action, there are no response data from the HTTP call-  and the javascript should handle it .

 

1.2 The POST – no problem . [ApiController] should handle the validation and return BadRequest if not validate

 

Second approach:  Return standard HttpCodes

2.1 The GET


[HttpGet("{id}")]
public ActionResult<Person> GetPerson404(int id)
{
    var p = RetrieveFromDatabase(id);
    if (p == null)
        return NotFound($"{nameof(Person)} with {id} are not found");

    return p;

}

We intercept the null and return standard 404 HttpCode . In that case, I strongly suggest to add a message to the end-caller – to know that the reason is not that the http call was a problem, but the Person was not found

2.2 The POST – no problem . [ApiController] should handle the validation and return BadRequest if not validate

 

Third approach: Return OK with a custom ReplyData class

The ReplyData can look this way

public class ReplyData<T>
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public T ReturnObject { get; set; }
}

2.1 The GET

This action is over simplified – the code just returns the ReplyData from the database.

[HttpGet("{id}")]
public ReplyData<Person> GetWithReply(int id) {

    return RetrieveWithReplyFromDatabase(id);
}

private ReplyData<Person> RetrieveWithReplyFromDatabase(int id)
{
    try
    {
        Person p = null;//retrieve somehow
        if (p == null)
        {
            var r = new ReplyData<Person>();
            r.Success = false;
            r.Message = "Cannot find person with id " + id;
            return r;
        }
        else
        {
            var r = new ReplyData<Person>();
            r.Success = true;
            r.ReturnObject = p;

            return r;
        }
    }
    catch(Exception ex)
    {
        var r = new ReplyData<Person>();
        r.Success = false;
        r.Message = ex.Message;
        return r;
    }
}

2.2 The POST

This is somehow complicated – the [ApiController] , if not valid, return a 400 BadRequest . So we should modify this with a custom middleware to return the same ReplyData

public class From400ValidationToReply : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
    try
    {
        var originalStream = context.Response.Body;
        var bufferStream = new MemoryStream();
        context.Response.Body = bufferStream;
        await next(context);
        bufferStream.Seek(0, SeekOrigin.Begin);
        if (context.Response.StatusCode != 400)
        {
            await bufferStream.CopyToAsync(originalStream);
            return;
        }
        var reader = new StreamReader(bufferStream);
        var response = await reader.ReadToEndAsync();
        if (!response.Contains("errors"))
        {
            await bufferStream.CopyToAsync(originalStream);
            return;
        }

        context.Response.StatusCode = 200;

        var r = new ReplyData<Person>();
        r.Success = false;
        r.Message=response;
        var text = JsonSerializer.Serialize(r);
        var bit = Encoding.UTF8.GetBytes(text);
        context.Response.ContentLength = bit.Length;
        await originalStream.WriteAsync(bit);

    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw;
    }
}

And the problem is that will affect all the request pipeline….

Conclusion:
I would choose 2 . But … your choice… ( and , for an advanced discussion, please read https://blog.ploeh.dk/2013/05/01/rest-lesson-learned-avoid-hackable-urls/ )

Automate the generation of the e-book

What I have found important in composing the e-book with examples was automating the work of generating the e-book. I have had a json file ( 10 years ago I have used xml ) and some specific folders with the chapters content. I have had also a template for each example chapter – that was ensuring all chapters look the same.

As an example , you can look  at https://github.com/ignatandrei/RSCG_Examples/tree/main/ApplicationVersion  and at  https://github.com/ignatandrei/RSCG_Examples/tree/main/CopyConstructor

This is generated automatically with

var m = new MultiGenerator(folder);
             await m.GeneratePost();
             await m.GenerateReadMeForEach();
             await m.GenerateFrontReadMe();
             await m.GenerateForImages(Path.Combine(folder, “docs”, “images”));
             await m.GenerateForEmail();
         }

Also, pandoc ( see previous post ) it will generate the book from markdown.

Code as picture and PowerAutomateDesktop

I want to make an e-book from RCSG . For this I need to put the code into the book in a nice form. However , the Word document and the PDF and the HTML all have the ideas of formatting the code. How one can accommodate various formats ? Short answer: picture. What if I take a screenshot of the code , as it is formatted in VS / VS Code ? 

But how ? One idea is to open VS and take screenshot  = but it is too resource intensive.

Other idea is internet- I found 2 sites: https://ray.so/  and https://carbon.now.sh/  . What if I use those sites  ?

The obvious solution is Selenium . The other is iMacros . I was thinking to another solution – Power Automate Desktop . Even in preview, it can record the browser actions ( open, goto url, click buttons) and even execute javascript :

function ExecuteScript() {
document.getElementsByClassName(‘jsx-2004545632 ‘)[0].click();
document.getElementById(‘export-clipboard’).focus();
document.getElementById(‘export-clipboard’).click();

  }

 

So I have made a flow to execute https://carbon.now.sh/   and another to save the image in paint ( and crop )

You can find the end result here : https://ignatandrei.github.io/RSCG_Examples/#rscg-number-1—thisassembly

Making a book from (examples) markdown

I want to make a book from my RSCG examples  https://github.com/ignatandrei/RSCG_Examples . The easy way is to transform the .md files into separate chapters.

I have tried initially node with  markdown-pdf and  pdf-merger-js , but I have been going then full speed with pandoc . To generate both Word and Index.md I have used this:

pandoc.exe -d pandocHTML.yaml -o ../docs/index.md -t gfm

pandoc.exe -d pandocHTML.yaml -o ../docs/index.docx

The pandocHTML.yaml is here:

from: gfm

# reader: may be used instead of from:

# to: docx

# output-file:  index.docx

# to: gfm

# output-file:  ../docs/index.md

# writer: may be used instead of to:

# leave blank for output to stdout:

reference-doc: customWord.docx

# leave blank for input from stdin, use [] for no input:

input-files:

– ../book/about.md

– ../book/whatIs.md

– ../ApplicationVersion/README.md

– ../Enum/README.md

– ../JsonToClass/README.md

– ../CopyConstructor/README.md

– ../DTOMapper/readme.md

– ../SkinnyControllers/README.md

– ../DP_Builder/readme.md

– ../MetadataFromObject/README.md

– ../DynamicMocking/readme.md

– ../MethodDecorator/README.md

– ../PartiallyFunction/readme.md

– ../IFormattable/readme.md

– ../DP_Decorator/README.md

– ../PropertyExpressionGenerator/readme.md

– ../book/others.md

– ../book/conclusion.md

# or you may use input-file: with a single value

# defaults:

# – customWord.docx

standalone: true

self-contained: true

# metadata values specified here are parsed as literal

# string text, not markdown:

metadata:

title: RSCG Examples

author:

  – Andrei Ignat

# Note that these take files, not their contents:

include-before-body: []

include-after-body: []

include-in-header: []

resource-path: [“.”]

# ERROR, WARNING, or INFO

verbosity: INFO

log-file: log.json

table-of-contents: true

toc-depth: 3

# number-sections: true

# a list of offsets at each heading level

number-offset: [0,0,0,0,0,0]

# toc: may also be used instead of table-of-contents:

shift-heading-level-by: 1

section-divs: true

identifier-prefix: foo

title-prefix: “”

strip-empty-paragraphs: true

# lf, crlf, or native

eol: lf

strip-comments: false

indented-code-classes: []

ascii: true

default-image-extension: “.jpg”

fail-if-warnings: false

dump-args: false

ignore-args: false

trace: false

As you see , I have put the .md files to be processed. Another thing that I have the need is to have a custom word template – to put page breaks after Heading 2. You can download from https://github.com/ignatandrei/RSCG_Examples/blob/main/print/customWord.docx

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.