Category: Uncategorized
Registering an Angular App with Azure Portal
Login with Azure
You may add your development and live URLs when deployed. Although I suggest you register different applications for these especially if you are accessing secured APIs.
.net Core 2.2 API logging for front-end in Serilog
The setup is a Front-end and a Back-end. Logging for Back-end in Serilog is quite straight forward. Things got a bit hairy when it was required to supply an API for logging Front-end errors and distinguish between the two in the Log table. In this post will show how this was achieved.
The project is same like the one in this post, however this is simpler so creating a repository for this is not required.
Create a LogController, this will be used to log from the front-end. Setup the action how you wish to send your errors and level of detail.
// POST: api/v1/Log
[HttpPost]
public void Post([FromBody] string value)
{
Log.Information(value);
}
Now to distinguish between back-end generated logs, an Enricher is used.
public class LocationEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
try
{
if(logEvent.Properties["RequestPath"].ToString().Contains(@"/api/v1/Log/"))
logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Location", "FrontEnd"));
else
logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Location", "BackEnd"));
}
catch
{
// ignored
}
}
}
What this does is check that this is coming from /api/v1/Log path and if so sets “Location” property to Frontend, else Backend.
.UseSerilog((context, config) =>
{
config.ReadFrom.Configuration(context.Configuration);
config.Enrich.WithLocation();
})
Add the column to the appconfig.json
"columnOptionsSection": {
"customColumns": [
{
"ColumnName": "Location",
"DataType": "nvarchar",
"DataLength": 20,
"AllowNull": true
}
]
}
This is pretty much it, easy eh?
One might get more sophistication in the logging from Front-end and distinguish the type of logic.
// POST: api/v1/Log
// Do some magic here ;)
[HttpPost]
public void Post([FromBody] string value)
{
Log.Information(value);
Log.Debug(value);
Log.Error(value);
Log.Warning(value);
Log.Fatal(value);
}
But that is outside of the scope of this post.
Angular 2+ Notification System
Angular 2+ Modals
Although this article makes some valid points in building modals it doesn’t provide a working example.
So here is the working example on Stackblitz of what is explained. some things were renamed to make better sense.
I do not agree with everything that was done. Accessing the DOM directly in DomService is not the ideal and can come up with consequences.
The correct way to access elements is by using a template variable as below
<input type="text" #testElem>
Then get the reference of the element
@ViewChild('testElem') el:ElementRef;
Then access your native element as below
this.el.nativeElement.style.background = "red";
More about it here
There are more elaborate and costomizable ways to create modals. This was just an exercise in rolling your own solution.
Angular 7 Progress Indicator Component
I came across the following progress indicator for jquery and wanted to try to recreate it as an angular component.
<ol class="ProgressBar">
<li #lis class="ProgressBar-step" *ngFor="let step of steps; let i = index">
<svg class="ProgressBar-icon"><use attr.href="{{svgUrl}}"/></svg>
<span class="ProgressBar-stepLabel">{{step}}</span>
</li>
</ol>
The only modifications done to the HTML were adding a template variable #lis and iterating on the steps to be displayed.
xlink:href has been deprecated so it was replaced with attr.href and the svg was saved in a file and the path to the file is passed to the component. Styling is pretty much identical.
import { Component, OnInit } from '@angular/core';
import { ElementRef, QueryList, ViewChildren, Renderer2} from '@angular/core';
import { Input } from '@angular/core';
@Component({
selector: 'app-progress-indicator',
templateUrl: './progress-indicator.component.html',
styleUrls: ['./progress-indicator.component.scss'],
})
export class ProgressIndicatorComponent {
@Input() public steps: string[];
@Input() public svgUrl:string;
@ViewChildren('lis') lis: QueryList<ElementRef>;
private elems: ElementRef<any>[];
constructor(private renderer: Renderer2) {
}
advance() {
this.elems = this.lis.toArray();
var count = this.lis.filter(x => x.nativeElement.classList.contains('is-current')).length;
if (count > 0) {
let index = 0;
for (let i = 0; i < this.elems.length; i++) {
if (this.elems[i].nativeElement.classList.contains('is-current')) {
index = i;
break;
}
}
if (index < this.elems.length) {
if(index < this.elems.length-1){ // if last one done remove current
this.renderer.removeClass(this.elems[index].nativeElement, "is-current");
}
this.renderer.addClass(this.elems[index].nativeElement, "is-complete");
}
if (index +1 < this.elems.length) {
this.renderer.addClass(this.elems[++index].nativeElement, "is-current");
}
} else {
this.renderer.addClass(this.lis.first.nativeElement, "is-current");
}
}
previous(){
this.elems = this.lis.toArray();
var count = this.lis.filter(x => x.nativeElement.classList.contains('is-current')).length;
if (count > 0) {
let index = 0;
for (let i = 0; i < this.elems.length; i++) {
if (this.elems[i].nativeElement.classList.contains('is-current')) {
index = i;
break;
}
}
if (index >= 0) {
this.renderer.removeClass(this.elems[index].nativeElement, "is-current");
this.renderer.removeClass(this.elems[index].nativeElement, "is-complete");
}
if(index > 0){
this.renderer.addClass(this.elems[--index].nativeElement, "is-current");
this.renderer.removeClass(this.elems[index].nativeElement, "is-complete")
}
}
}
}
The component uses @ViewChildren to access the li tags and Renderer2 to do safe modifications to the classes of each element.
<app-progress-indicator
[steps]='steps'
[svgUrl]='"./assets/svg/checkmark-bold.svg#checkmark-bold"'>
</app-progress-indicator>
Usage is as above steps should be an array of strings.
public steps = ["Test","Review","Result","Mistakes"];
Note that QueryList is not accessible by index nor one could iterate on it for ES5 so toarray() was used to get things done. ES6 makes it possible to iterate directly.
JavaScript Copy Text in ClipBoard
The Following lets a button click cause text data to be pushed inside the clipboard. This is because direct access to clipboard from JavaScript is not possible for security reasons.
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN"> <HTML> <HEAD> <TITLE> Copy Command </TITLE> </HEAD> <BODY> <script> //<![CDATA[ function copy(){ var textarea = document.createElement("textarea"); var str = ['One,One', 'Two,Tow', 'Three,Three', 'Four,Four', 'Five,Five', ].join('\n'); textarea.value=str; textarea.setAttribute('id',"t1"); document.body.appendChild(input); var input = document.getElementById("t1"); input.focus(); input.select(); document.execCommand('copy'); document.body.removeChild(input); } //]]> </script> <button onclick="copy();" id="copy">copy</button> </BODY> </HTML>
Tested on IE, FF and Chrome.
Using GraphAPI From WebAPI
I will Polish this post later, meanwhile:
Go In Azure Register an APP
- Get Client ID:
- Generate Key
- Permissions to other application: Read Directory Data ( Very important else nothing will work)
Packages Required:
https://www.nuget.org/packages/Microsoft.Azure.ActiveDirectory.GraphClient/2.0.5
https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/2.14.201151115
Web.config
<appSettings><add key=”ida:ClientID” value=”a69ce710-2edc-4cf2-bf40-ded19bb28e08″ />
<add key=”ida:AppKey” value=”KEY GENERATED FROM AZURE” />
<add key=”ida:GraphUrl” value=”https://graph.windows.net” />
<add key=”ida:authString” value=”https://login.windows.net/stephangaleagmail.onmicrosoft.com” />
<add key=”ida:graphURL” value=”https://graph.windows.net/stephangaleagmail.onmicrosoft.com” /></appSettings>
Getting an instance of ADClient
private static async Task<string> GetAppTokenAsync()
{
AuthenticationContext authenticationContext = new AuthenticationContext(ConfigurationManager.AppSettings[“ida:authString”], false);
ClientCredential clientCred = new ClientCredential(ConfigurationManager.AppSettings[“ida:ClientId”], ConfigurationManager.AppSettings[“ida:AppKey”]);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(ConfigurationManager.AppSettings[“ida:GraphUrl”], clientCred);
return authenticationResult.AccessToken;
}
Uri serviceRoot = new Uri(ConfigurationManager.AppSettings[“ida:graphURL”]);
ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot,
async () => await GetAppTokenAsync());
Looking up a user
var upn = “test@stephangaleagmail.onmicrosoft.com”;
var userLookup = adClient.Users.Where(
user => user.UserPrincipalName.Equals(
upn, StringComparison.CurrentCultureIgnoreCase)).ExecuteSingleAsync();
User user1= (User)await userLookup;
Gulp + Livereload Occupied port Issue
Sometimes when using Gulp with Livereload when one wishes to stop it and restarting it Ctrl+c and gulp still holds the port occupied resulting in the following error.
… Uhoh. Got error listen EADDRINUSE :::35729 …
Error: listen EADDRINUSE :::35729
at Object.exports._errnoException (util.js:856:11)
at exports._exceptionWithHostPort (util.js:879:20)
at Server._listen2 (net.js:1234:14)
at listen (net.js:1270:10)
at Server.listen (net.js:1366:5)
at Server.listen (/var/www/angle.local/master/node_modules/tiny-lr/lib/serve r.js:162:15)
at Function.exports.listen (/var/www/angle.local/master/node_modules/gulp-li vereload/gulp-livereload.js:68:12)
at Gulp.<anonymous> (/var/www/angle.local/master/gulpfile.js:282:16)
at module.exports (/var/www/angle.local/master/node_modules/orchestrator/lib /runTask.js:34:7)
at Gulp.Orchestrator._runTask (/var/www/angle.local/master/node_modules/orch estrator/index.js:273:3)
You already have a server listening on 35729
You should stop it and try again.
A quick solution is to kill the process by port with the following command
kill -9 $(lsof -t -i :35729)
Installing Webmin on CentOS 7 and configure firewall
Some days ago I described the steps involved in configuring a CentOS WebServer VM with virtual hosts here manually. In this post I will describe the steps involved to install Webmin .
Creating a repository file for Webmin
cd /etc/yum.repos.d/ sudo vi webmin.repo
[Webmin] name=Webmin Distribution Neutral #baseurl=http://download.webmin.com/download/yum mirrorlist=http://download.webmin.com/download/yum/mirrorlist enabled=1
Esc :wq
Install Webmin GPG key
rpm --import http://www.webmin.com/jcameron-key.asc
Update Repositories
yum check-update
Install Webmin
yum -y install webmin
Add to startup
chkconfig webmin on
Start the service
service webmin start
Enable webmin port in firewall
firewall-cmd --permanent --add-port=10000/tcp firewall-cmd --reload
Open in browser
https://192.168.101.39:10000/ where 192.169.101.39 is your VM IP (ip addr)
Use same credentials used to admin the VM