Sunday, October 31, 2021

Angular 12 select box with default value, required validator and state-city dependency



import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-mycomponent',
  templateUrl: './mycomponent.component.html',
  styleUrls: ['./mycomponent.component.css']
})
export class MycomponentComponent implements OnInit {

  masterSexList = [{id : 1 , name : "Male"} , {id : 2 , name : "Female"}];
  masterStateList = [{id : 1 , name : "Maharashtra"} , {id : 2 , name : "Rajasthan"}];
  masterCityList  = [
      {id : 1 , name : "Mumbai" , stateId : 1},
      {id : 2 , name : "Pune", stateId : 1},
      {id : 3 , name : "Jaipur" , stateId : 2},
      {id : 4 , name : "Udaipur" , stateId : 2}
    ];
  cityList : any;
  frm  = this.fb.group({
    firstName : ["" ,
      [Validators.required , Validators.minLength(3) , Validators.maxLength(6)]
    ],
    lastName : ["" ,[Validators.required]],
    sex : [2 , [Validators.required]],  //Default value of 2 corresponds to Female
    state : ["" , [Validators.required]],
    city : ["" , [Validators.required]]
  });

  selectedStateId : number = 0;  
  selectedCityId : number = 0;
  selectedSexId : number = 0;

  constructor(private fb : FormBuilder) { }

  ngOnInit(): void {
    //THIS ALSO WORKS => you should pass the id of the object you want to be selected.
    //this.frm.get('sex')?.patchValue(1);
  }

  submit()
  {
    alert(
      this.frm.get('firstName')?.value + " " +
      this.frm.get('lastName')?.value + " " +
      this.selectedStateId + " " +
      this.selectedCityId + " " +
      this.selectedSexId
      );
  }

  onSexChange(sexChangeEvent: any)
  {
    this.selectedSexId = sexChangeEvent.target.value;
  }


  onCityChange(cityChangeEvent: any)
  {
    this.selectedCityId = cityChangeEvent.target.value;
  }

  onStateChange(stateChangeEvent: any)
  {
    this.selectedStateId = stateChangeEvent.target.value;
    if ( this.selectedStateId != undefined && this.selectedStateId != null
    && this.selectedStateId > 0)
      this.cityList = this.masterCityList.filter(x=>x.stateId == this.selectedStateId)
  }
}



<p>mycomponent works!</p>

<form [formGroup] = "frm" (ngSubmit)="submit()">
    <table>
        <tr>
            <td>
                <label>First Name</label>
            </td>
            <td>
                <input type="text" formControlName="firstName" />
            </td>
        </tr>

        <tr>
            <td>
                <label>Last Name</label>
            </td>
            <td>
                <input type="text" formControlName="lastName" />                
            </td>
        </tr>

        <tr>
            <td>
                <label>Sex</label>
            </td>
            <td>
                <select formControlName="sex" (change)="onSexChange($event)">
                    <option *ngFor="let sex of masterSexList; let  i = index" [value]="sex.id" >{{sex.name}}</option>
                </select>
            </td>
        </tr>

        <tr>
            <td>
                <label>State</label>
            </td>
            <td>
                <select formControlName="state" (change)="onStateChange($event)">
                    <option value="">Choose your state</option>
                    <option *ngFor="let state of masterStateList" [value]="state.id">{{state.name}}</option>
                </select>
            </td>
        </tr>

        <tr>
            <td>
                <label>City</label>
            </td>
            <td>
                <select formControlName="city" (change)="onCityChange($event)">
                    <option value="">Choose your city</option>
                    <option *ngFor="let city of cityList" [value]="city.id">{{city.name}}</option>
                </select>
            </td>
        </tr>

        <tr>
            <td colspan="2">
                <button type="submit"  [disabled] = "!frm.valid">Submit</button>
            </td>
        </tr>

    </table>
</form>






 

Angular 12 select box binding to an object list

 In this sample the city field binds with CityList which is a list of objects consisting of id and names.

Required field validator is applied.



import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-mycomponent',
  templateUrl: './mycomponent.component.html',
  styleUrls: ['./mycomponent.component.css']
})
export class MycomponentComponent implements OnInit {

  CityList  = [
      {id : 1 , name : "Mumbai"},
      {id : 2 , name : "Pune"},
      {id : 3 , name : "Delhi"},
      {id : 4 , name : "Calcutta"}
    ];
  frm  = this.fb.group({
    firstName : ["" ,
      [Validators.required , Validators.minLength(3) , Validators.maxLength(6)]
    ],
    lastName : ["" ,[Validators.required]],
    city : ["" , [Validators.required]]
  });

  selectedCityId : number = 0;

  constructor(private fb : FormBuilder) { }

  ngOnInit(): void {
  }

  submit()
  {
    alert(
      this.frm.get('firstName')?.value + " " +
      this.frm.get('lastName')?.value + " " +
      this.selectedCityId
      );
  }

  onCityChange(event: any)
  {
    this.selectedCityId = event.target.value;
  }

}



<p>mycomponent works!</p>

<form [formGroup] = "frm" (ngSubmit)="submit()">
    <table>
        <tr>
            <td>
                <label>First Name</label>
            </td>
            <td>
                <input type="text" formControlName="firstName" />
            </td>
        </tr>

        <tr>
            <td>
                <label>Last Name</label>
            </td>
            <td>
                <input type="text" formControlName="lastName" />                
            </td>
        </tr>


        <tr>
            <td>
                <label>City</label>
            </td>
            <td>
                <select formControlName="city" (change)="onCityChange($event)">
                    <option value="">Choose your city</option>
                    <option *ngFor="let city of CityList" [value]="city.id">
                    {{city.name}}</option>
                </select>
            </td>
        </tr>

        <tr>
            <td colspan="2">
                <button type="submit"  [disabled] = "!frm.valid">Submit</button>
            </td>
        </tr>

    </table>
</form>







Angular 12 select box with a simple string list binding


Most Basic angular select box which binds to a list of strings. It has a "Choose" option and a required validator applied.

 

<p>mycomponent works!</p>

<form [formGroup] = "frm" (ngSubmit)="submit()">
    <table>
        <tr>
            <td>
                <label>First Name</label>
            </td>
            <td>
                <input type="text" formControlName="firstName" />
            </td>
        </tr>

        <tr>
            <td>
                <label>Last Name</label>
            </td>
            <td>
                <input type="text" formControlName="lastName" />                
            </td>
        </tr>


        <tr>
            <td>
                <label>City</label>
            </td>
            <td>
                <select formControlName="city">
                    <option value="">Choose your city</option>
                    <option *ngFor="let city of CityList" [ngValue]="city">{{city}}</option>
                </select>
            </td>
        </tr>

        <tr>
            <td colspan="2">
                <button type="submit"  [disabled] = "!frm.valid">Submit</button>
            </td>
        </tr>

    </table>
</form>


import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-mycomponent',
  templateUrl: './mycomponent.component.html',
  styleUrls: ['./mycomponent.component.css']
})
export class MycomponentComponent implements OnInit {

  CityList  = ["Mumbai" , "Pune" , "Delhi" , "Calcutta"]
  frm  = this.fb.group({
    firstName : ["" , [Validators.required , Validators.minLength(3) , Validators.maxLength(6)]],
    lastName : ["" ,[Validators.required]],
    city : ["" , [Validators.required]]
  });

  constructor(private fb : FormBuilder) { }

  ngOnInit(): void {
  }

  submit()
  {
    alert(this.frm.get('firstName')?.value + " " + this.frm.get('lastName')?.value + " " + this.frm.get("city")?.value);
  }

}




Remember that in html, "value" attribute binds to a scalar value while "ngValue" attribute binds to an object.








Sunday, October 24, 2021

 frmabc = this.fb.group({

    firstName : ['', [Validators.required , Validators.minLength(4) , Validators.maxLength(10)]],
    lastName : ['' , [Validators.required , Validators.minLength(4) , Validators.maxLength(10)]],
    email : ['' , [Validators.required, Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$')]],
    age :['' , [Validators.required, Validators.pattern('^[0-9]+$'), Validators.max(100)]],
    password : ['', [Validators.required , Validators.minLength(4) , Validators.maxLength(10)]],
    confirmPassword : ['', [Validators.required , Validators.minLength(4) , Validators.maxLength(10)]]
  }, { validators: passwordMatchingValidatior });

Basic Angular Reactive Form Validation with Validation messages

 1. app.component.html 



<form [formGroup] = "frmabc" (ngSubmit) = "submit()" >
  <input type="text" formControlName="firstName" maxlength="10"/>
  <div *ngIf="frmabc.controls.firstName.invalid &&
  (frmabc.controls.firstName.dirty || frmabc.controls.firstName.touched)"
  class="alert">
 
  <div *ngIf="frmabc.controls.firstName.errors?.required">
    First Name is required.
  </div>
  <div *ngIf="frmabc.controls.firstName.errors?.minlength">
    First Name must be at least 4 characters long.
  </div>
 
 
</div>
<input type="text" formControlName="lastName"/>
<div *ngIf="frmabc.controls.lastName.invalid &&
(frmabc.controls.lastName.dirty || frmabc.controls.lastName.touched)"
class="alert">

<div *ngIf="frmabc.controls.lastName.errors?.required">
  Last Name is required.
</div>
<div *ngIf="frmabc.controls.lastName.errors?.minlength">
  Last Name must be at least 4 characters long.
</div>

</div>

<button type="submit" [disabled]= "!frmabc.valid">dfdsf</button>
</form>


2. app.component.ts 


import { Component} from '@angular/core';
import { FormsModule , FormBuilder , FormControl, Validators , FormGroup } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  title = 'angform1';
  // Patterns
  //readonly PAT_NAME = "^[a-zA-Z ]{2,20}$";
  //readonly PAT_EMAIL = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+[.][a-zA-Z]{2,4}$";
 
  frmabc = this.fb.group({
    firstName : ['', [Validators.required , Validators.minLength(4) , Validators.maxLength(10)]],
    lastName : ['' , [Validators.required , Validators.minLength(4) , Validators.maxLength(10)]]
  });
 
  constructor(private fb : FormBuilder) {}
  submit()
  {
    alert ( this.frmabc.get('firstName')?.value + " " + this.frmabc.get('lastName')?.value );
  }
 
}


NOTES : 

1. maxLength validator put in ts file will not by itself restrict the input to max length. To restrict the input to maxlength, use the maxlength attribute in .html also, in addition to the one in .ts. 

<input .... maxlength = "10" /> 

2.  If you get "possible null value" error, do not forget to use the "?" operator. 



Thursday, September 2, 2021

 https://joonasw.net/view/aspnet-core-di-deep-dive

Very good article on dependency injection

Wednesday, August 25, 2021

dev.to

 dev.to

Good  short no-nonsense useful articles


https://dev.to/palashmon/6-awesome-css-layout-generators-pbc

6 Awesome CSS Layout Generators


Web Development (3 Part Series)

1 Ultimate Cheatsheet Compilation

2 10 Awesome JavaScript Shorthands

3 6 Awesome CSS Layout Generators



https://dev.to/hb/10-fun-apis-to-use-for-your-next-project-2lco

10 Fun APIs to Use For Your Next Project



https://dev.to/hb/top-5-ides-code-editors-for-web-development-2mdo

Top 5 IDEs/Code Editors for Web Development



https://dev.to/hb/python-developer-roadmap-in-2021-2bmo

Python Developer Roadmap in 2021


https://dev.to/hb/30-machine-learning-ai-data-science-project-ideas-gf5

30 Machine Learning, AI, & Data Science Project Ideas


https://dev.to/hb/10-advanced-projects-to-build-in-2021-425o

10 Advanced Projects to Build in 2021




Friday, August 20, 2021

Resources

 


10 Resources for Learning LINQ

https://grantwinney.com/10-resources-for-learning-linq/


https://grantwinney.com/



https://docs.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/data-driven-crud-microservice

Creating a simple data-driven CRUD microservice



https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0

ASP.NET Core Middleware


https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-5.0

App startup in ASP.NET Core


https://docs.microsoft.com/en-us/aspnet/core/fundamentals/?view=aspnetcore-5.0&tabs=windows

ASP.NET Core fundamentals


https://docs.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-5.0

Introduction to ASP.NET Core Blazor


https://techcommunity.microsoft.com/t5/microsoft-learn-blog/how-to-stay-motivated-during-your-learning-journey/ba-p/1469345

How to stay motivated during your learning journey


https://adrientorris.github.io/microsoft/microsoft-virtual-academy-mva-learning-paths.html
Microsoft Virtual Academy introduices the Learning Paths

https://cloud.google.com/training
google cloud training

Angular

https://netbasal.com/

Mt Techie Bar angular youtube tutorials

=========================================================================
Angular On Coursera

[Project]
Build ATM User Interface using Routing in Angular
https://www.coursera.org/projects/build-atm-user-interface-routing-angular

[Project] [2 hrs]
Routing and Navigation Concepts in Angular
https://www.coursera.org/projects/routing-and-navigation-concepts-in-angular


[Project] [2 hrs] 
Angular tooling: Generating code with schematics
https://www.coursera.org/projects/angular-schematics

[Project][3.5 hrs]
Learn Angular Routing by building a Cocktails Application
https://www.coursera.org/projects/angular-routing


[Project][2 hrs]
Test your Angular web application using Jasmine and Karma
https://www.coursera.org/projects/test-angular-web-application-jasmine-karma

[Course]
Front-End Web UI Frameworks and Tools: Bootstrap 4
https://www.coursera.org/learn/bootstrap-4?specialization=full-stack-mobile-app-development#syllabus

[Course]
Full Stack Web Development with Angular Specialization
https://www.coursera.org/specializations/full-stack-mobile-app-development#courses
=========================================================================
React : 

[Course]
Developing Cloud Apps with Node.js and React
https://www.coursera.org/learn/node-js?specialization=ibm-full-stack-cloud-developer#syllabus

[Course]
Full-Stack Web Development with React Specialization
https://www.coursera.org/specializations/full-stack-react

[Project] [2hrs]
Build your personal webpage using React and Github Pages
https://www.coursera.org/projects/build-your-personal-webpage
=========================================================================
Docker 

[Project][1.5 hrs]
TensorFlow Serving with Docker for Model Deployment
https://www.coursera.org/projects/tensorflow-serving-docker-model-deployment

[Project][3 hrs]
Introduction to Docker: Build Your Own Portfolio Site
https://www.coursera.org/projects/introduction-to-docker-build-portfolio-site

[Project][2 hrs]
Build local development environments using Docker containers
https://www.coursera.org/projects/build-local-development-environments-using-docker-containers


[Project][3 hrs]
Docker Essentials & Building a Containerized Web Application
https://www.coursera.org/projects/docker-container-essentials-web-app

[Project][1 hr 15 min]
Introduction to Docker : The Basics
https://www.coursera.org/projects/docker-fundamentals

[Project][2 hrs]
Docker for absolute beginners
https://www.coursera.org/projects/docker-for-absolute-beginners

[Course]
Building Containerized Applications on AWS
https://www.coursera.org/learn/containerized-apps-on-aws#syllabus
=========================================================================
Domain Driven Architecture

https://academy.lightbend.com/courses/course-v1:lightbend+LRA-DomainDrivenDesign+v1/about
Lightbend free course


https://blog.knoldus.com/introduction-to-domain-driven-design-ddd/
Introduction to Domain Driven Design (DDD)

https://github.com/ddd-by-examples/library

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/ddd-oriented-microservice


Khalil Stemmler
Understanding Domain Entities [with Examples] - DDD w/ TypeScript
https://khalilstemmler.com/articles/typescript-domain-driven-design/entities/

https://khalilstemmler.com/articles/categories/domain-driven-design/
16 articles


https://airbrake.io/blog/software-design/domain-driven-design
Domain-Driven Design: What is it and how do you use it?


https://vaadin.com/learn/tutorials/ddd
Domain Driven Design Crash Course

Domain-driven Design Example
https://www.mirkosertic.de/blog/2013/04/domain-driven-design-example/
=========================================================================
Very important tutorial on sql server in docker

Restore a SQL Server database in a Linux Docker container
https://docs.microsoft.com/en-us/sql/linux/tutorial-restore-backup-in-sql-server-container?view=sql-server-ver15
=========================================================================
https://iq.opengenus.org/earn-money-from-github/

Wednesday, August 11, 2021

Entity Tracking in Entity Framework

Tracking is the feature of EF used to track changes in entities. 

If entities are changed, the changes are persisted to database during SaveChanges(), because those changes are tracked by EF.

Remember that Keyless entities are never tracked. 

Any query that returns an entity type is a tracking query. By default any query returning entity is called a tracking query and it is tracked. 

Even if a query anonymously returns an entity type, EF detects is and tracks the entity. 


To disable tracking on an entity returning query, use AsNoTracking()

    context.Blogs.AsNoTracking().ToList();

This behaviour can also be changed at context level: 

    context.ChangeTracker.QueryTrackingBehaviour = QueryTrackingBehaviour.NoTracking;


Identity Resolution: EF keeps track of tracked entities using the identity. It always maintains a single instance of tracked entities and returns the same instance when demanded, without hitting database. Thus when an entity is asked for, EF first checks whether entity with that identity already exists in context and returns the same if exists. This checking is done purely on the basis of identity property of the entity. 

This feature is called Identity resolution.

Non-tracking queries are not tracked, hence identity resolution is not done for them. Every time a no-tracking query is called, it results in a database hit. 


Now in EF5.0, we can have a non-tracking query with identity resolution. You need to use AsNoTrackingWithIdentityResolution() method for that. 

This creates a separate stand-alone tracker for this query and tracks it. After the query is enumerated fully, this stand-alone tracker goes out of scope and is garbage-collected. The context is not aware of this stand-alone tracking. 


What happens to tracking when the query contains a mix of tracked and keyless entities ?

From EF3.0 onwards, keyed entities in such mixed queries ARE tracked and keyless entities

are not tracked. 

Prior to EF3.0, the entire query results were untracked.

 https://www.softwareblogs.com/Home/Blog/how/DiffEF6andCore/entity-framework-6-entity-framework-core-difference-compare-examples


https://docs.microsoft.com/en-us/ef/efcore-and-ef6/


https://www.learnentityframeworkcore.com/

https://www.entityframeworktutorial.net/Types-of-Entities.aspx


https://chadgolden.com/blog/comparing-performance-of-ef6-to-ef-core-3

Thursday, August 5, 2021

Basic Client Authentication Using IdentityServer4 - The Client

 Let us now create the client. Out client will be a web api project which exposes weatherforecast api. 

1. Create a new asp .net core web api project: 





2. Main changes will be in startup.cs and a minor change is required in the controller file. 

We need to use JwtBearerDefaults structure in startup.cs. For that we need to install 

IdentityServer4.AccessTokenValidation package. Let us install this using nuget: 




3. In startup.cs, in the ConfigureServices method, add the following : 

        public void ConfigureServices(IServiceCollection services)

        {

            services.AddControllers();

            services.AddAuthentication(options => {

                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;

                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

            }).AddJwtBearer(op =>

            {

                op.Authority = "https://localhost:44368";

                op.Audience = "weatherapiresource";

            });


        }


4. In the Configure method, add  app.UseAuthentication();


        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

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

This completes your modifications in startup.cs. The only remaining thing is to add [Authorize] attribute in controller. 


5. Go to controllers\WeatherForecastController and  add [Authorize] attribute before the [HttpGet] attribute of Get() method. You will need to reference Microsoft.AspNetCore.Authorization for it. 

         [Authorize]

        [HttpGet]

        public IEnumerable<WeatherForecast> Get()

        {

            var rng = new Random();

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast

            {

                Date = DateTime.Now.AddDays(index),

                TemperatureC = rng.Next(-20, 55),

                Summary = Summaries[rng.Next(Summaries.Length)]

            })

            .ToArray();

        }


6. Build and Run the project. 

7. Notice that the project runs but gives an authorization error in the browser: 


The same error is given in postman too: 



8. This is because now the weatherforecast api requires a token to allow access to it. 

So let us generate a token using POSTMAN by calling /connect/token endpoint and then pass it to get request. 

To pass a token to get request in POSTMAN, open the "Authorization" tab of the request: 


Then open the "Type" drop down and select "Bearer Token" 


In the "Token" input box that appears on the right side, paste your token. 

And resend the GET request by pressing the "Send" button. 


9. Provided the token is not old and expired, you should now get the response of weatherforecast get api. 





Basic Client Authentication Using Identity Server 4 - The Server

 The aim of this tutorial is to use Identity Server 4 for client authentication. We will build two different VS projects, one will be server and other will be client. The server will host IdentityServer4 and do the task of authentication. The authentication will be based on client credentials, hence user , user id and password will not be required. 

Building the Identity Server

1. Create a new asp.net core web application project (the screen shots pertain to Visual Studio 2019). 




2. Once the project is created, add nuget packages IdentityServer4 and IdentityServer4.EntityFramework. 



3. Open startup.cs. 
At the end of  startup.cs add a new public class IS4Data. 
We will put the methods required by IS4 in this class. 

public class IS4Data
    {
        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope>
             {
                 new ApiScope(name: "read",   displayName: "Read your data."),
                 new ApiScope(name: "write",  displayName: "Write your data."),
                 new ApiScope(name: "delete", displayName: "Delete your data."),
                 new ApiScope(name: "allow_weather_api", displayName: "Api Scope.")
             };
        }
        public static IEnumerable<ApiResource> GetApiResourses()
        {
            return new List<ApiResource>
            {
                 new ApiResource()
                        {
                        Name = "weatherapiresource",   //This is the name of the API
                        //Description = "This is the invoice",
                        Enabled = true,
                        DisplayName = "Weather API Resource",
                        Scopes = new List<string> { "allow_weather_api"},
                        }

        };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    ClientId = "ABC Company Inc.",
                    AllowedGrantTypes = {GrantType.ClientCredentials },
                    ClientSecrets =  {
                        new Secret("ThisIsASecretString".Sha256())
                    }
                    ,
                    AllowedScopes = { "allow_weather_api" }
                }
            };
        }

    }


4. In the same file startup.cs, add the following code at the end of the ConfigureServices  method: 

            services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryApiResources(IS4Data.GetApiResourses())
                .AddInMemoryApiScopes(IS4Data.GetApiScopes())
                .AddInMemoryClients(IS4Data.GetClients());

5. In the same file startup.cs, add a single statement in Configure method: 
            app.UseIdentityServer();

6. Run the project. This will take you to the browser on the weatherforcast api output. 

7. To test the server, use the .well-known/openid-configuration api. Supposing the project is running on localhost on the port 44368, you can browse to the URL  

https://localhost:44368/.well-known/openid-configuration


This will give an output like : 
{"issuer":"https://localhost:44368","jwks_uri":"https://localhost:44368/.well-known/openid-configuration/jwks","authorization_endpoint":"https://localhost:44368/connect/authorize","token_endpoint":"https://localhost:44368/connect/token","userinfo_endpoint":"https://localhost:44368/connect/userinfo","end_session_endpoint":"https://localhost:44368/connect/endsession","check_session_iframe":"https://localhost:44368/connect/checksession","revocation_endpoint":"https://localhost:44368/connect/revocation","introspection_endpoint":"https://localhost:44368/connect/introspect","device_authorization_endpoint":"https://localhost:44368/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["read","write","delete","allow_weather_api","offline_access"],"claims_supported":[],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}

If opened in POSTMAN with a GET request, the output will look like 
{
    "issuer""https://localhost:44368",
    "jwks_uri""https://localhost:44368/.well-known/openid-configuration/jwks",
    "authorization_endpoint""https://localhost:44368/connect/authorize",
    "token_endpoint""https://localhost:44368/connect/token",
    "userinfo_endpoint""https://localhost:44368/connect/userinfo",
    "end_session_endpoint""https://localhost:44368/connect/endsession",
    "check_session_iframe""https://localhost:44368/connect/checksession",
    "revocation_endpoint""https://localhost:44368/connect/revocation",
    "introspection_endpoint""https://localhost:44368/connect/introspect",
    "device_authorization_endpoint""https://localhost:44368/connect/deviceauthorization",
    "frontchannel_logout_supported"true,
    "frontchannel_logout_session_supported"true,
    "backchannel_logout_supported"true,
    "backchannel_logout_session_supported"true,
    "scopes_supported": [
        "read",
        "write",
        "delete",
        "allow_weather_api",
        "offline_access"
    ],
    "claims_supported": [],
    "grant_types_supported": [
        "authorization_code",
        "client_credentials",
        "refresh_token",
        "implicit",
        "urn:ietf:params:oauth:grant-type:device_code"
    ],
    "response_types_supported": [
        "code",
        "token",
        "id_token",
        "id_token token",
        "code id_token",
        "code token",
        "code id_token token"
    ],
    "response_modes_supported": [
        "form_post",
        "query",
        "fragment"
    ],
    "token_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post"
    ],
    "id_token_signing_alg_values_supported": [
        "RS256"
    ],
    "subject_types_supported": [
        "public"
    ],
    "code_challenge_methods_supported": [
        "plain",
        "S256"
    ],
    "request_parameter_supported"true
}




You can also get token by calling /connect/token endpoint

The required credentials are grant_type, client_id and client_scecret






All the code chages are in startup.cs only and below is the complete startup.cs file. 


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

namespace Server
{
    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.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryApiResources(IS4Data.GetApiResourses())
                .AddInMemoryApiScopes(IS4Data.GetApiScopes())
                .AddInMemoryClients(IS4Data.GetClients());
        }

        // 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.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();
            app.UseIdentityServer();

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


    public class IS4Data
    {
        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope>
             {
                 new ApiScope(name: "read",   displayName: "Read your data."),
                 new ApiScope(name: "write",  displayName: "Write your data."),
                 new ApiScope(name: "delete", displayName: "Delete your data."),
                 new ApiScope(name: "allow_weather_api", displayName: "Api Scope.")
             };
        }
        public static IEnumerable<ApiResource> GetApiResourses()
        {
            return new List<ApiResource>
            {


                new ApiResource()
                        {
                        Name = "weatherapiresource",   //This is the name of the API
                        //Description = "This is the invoice",
                        Enabled = true,
                        DisplayName = "Weather API Resource",
                        Scopes = new List<string> { "allow_weather_api"},
                        }


        };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    ClientId = "ABC Company Inc.",
                    AllowedGrantTypes = {GrantType.ClientCredentials },
                    ClientSecrets =  {
                        new Secret("ThisIsASecretString".Sha256())
                    }
                    ,
                    AllowedScopes = { "allow_weather_api" }
                }
            };
        }

    }
}

How to check local and global angular versions

 Use the command ng version (or ng v ) to find the version of Angular CLI in the current folder. Run it outside of the Angular project, to f...