tag:blogger.com,1999:blog-76792004902611666012024-03-13T13:31:25.424-04:00Mitul Suthar's Coding BlogMitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.comBlogger97125tag:blogger.com,1999:blog-7679200490261166601.post-51976399510753421392021-10-15T12:02:00.001-04:002021-10-15T12:02:33.536-04:00DevOps Links for 15/10/2021<p><a href="https://github.blog/2021-10-04-beta-github-releases-improving-release-experience/" target="_blank">New GitHub Releases Public Beta</a></p><span id="docs-internal-guid-ddee51ab-7fff-a0e4-556e-26f327a3a2de"><div><span>GitHub releases allows repository maintainers to release versions of their software. It accompanies notes, version of the software, contributors, etc. When I see releases page of some OSS projects, I feel like so many smart people contributed to this release and how much effort goes into writing these. This new version of GitHub Releases will help maintainers with auto-generated release notes, new UI refresh, using GitHub Actions using APIs you can integrate this feature and more. Go check out the preview. You will have to turn on this feature.</span></div><div><span><br /></span></div><a href="https://github.blog/2021-10-07-github-advisory-database-now-powers-npm-audit/" target="_blank">GitHub Advisory Database now powers npm audit</a><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">As a web developer, you would have come across npm. It is the package manager for your web projects. npm is command line tool which you can use to install, search, remove packages from your projects. npm audit allows you to scan project's dependencies for security vulnerabilities. GitHub Advisory Database stores all the security vulnerabilities and it is maintained by GitHub. With this new update npm audit feature will utilize GitHub Advisory database behind the scenes. Dependabot also uses this database. </p><div><span><br /></span></div><div><br /></div><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://damienaicheh.github.io/github/actions/2021/10/07/reuse-workflows-composite-action-en.html" target="_blank">Reuse your workflows across multiple projects using composite actions</a></p><div><br /></div><div>In GitHub Actions, you execute different actions to build/release/test your project. When you are working projects that use the same steps over and over again, you may want to build something that is reusable across multiple projects. Just like we extract repeatable code into its own function so it can be reused in multiple places. Composite actions accomplish exactly this but for actions. This post explains how you can create a composite action in one master repository and then use that composite action in multiple projects. It is cool. </div><div><br /></div><div><a href="https://www.winopsdba.com/blog/azure-cloud-container-build-scan-publish.html" target="_blank">How to scan a container for vulnerabilities and publish results as a part of Azure DevOps CI/CD pipeline</a></div><div><br /></div><div>As part of your DevSecOps practices, you want to scan your code assets for known vulnerabilities. That includes your code, internal as well as external dependencies such as libraries, packages, and container images. In this post, I learned of so many things. tfsec is tool to do static analysis of your terraform code (<a href="https://www.winopsdba.com/blog/azure-cloud-terraform-validate-and-sast.html" target="_blank">post</a>). <a href="https://github.com/aquasecurity/trivy" target="_blank">Trivy </a>is tool you can use to scan vulnerabilities in your docker container. Another great post. You can check out the <a href="https://github.com/WinOpsDBA/DBAinTheCloud" target="_blank">repository </a>for more information.</div><div><br /></div><div><i style="background-color: white; box-sizing: border-box; color: #212529; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 15.4px;">Tip: You can follow me on <a href="https://twitter.com/mitulsuthar" style="background-color: transparent; box-sizing: border-box; color: #2288bb; text-decoration-line: none;" target="_blank">twitter </a>to get instant updates whenever I tweet about DevOps</i></div></span>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-71288738385294994442021-09-30T12:20:00.002-04:002021-09-30T12:20:36.284-04:00DevOps Links for 30/9/2021<p><a href="https://techcommunity.microsoft.com/t5/itops-talk-blog/so-github-has-a-cli-let-s-take-a-look/ba-p/2728010?WT.mc_id=DOP-MVP-4025064" target="_blank">GitHub CLI</a></p><p>Do you know that you can work with GitHub through the command line? But isn't it already the case when I do <i>git commit</i>? No that is Git CLI. GitHub - the website where you host repos and collaborate with developers to create pull requests and issues, also has a CLI. This post explains on how to get started with GitHub CLI. You can download the CLI from <a href="https://t.co/CeG590VrSi?amp=1" target="_blank">cli.github.com</a>.</p><p><a href="https://dev.to/omiossec/using-scope-with-arm-templates-20co" target="_blank"> Using scope with ARM templates to deploy across subscriptions.</a></p><p>Do you know ARM templates? Well ARM templates allow you to deploy your Azure resources using a JSON template. You can define resources using JSON and build templates that can be reused. This post explains how you can use scope with ARM templates to deploy resources across subscriptions. Normally when you learn about ARM, you deploy a template into a resource group. Well what if you want to deploy something across subscriptions? That's when you use scope. Go check out the article in more detail.</p><p><a href="https://www.telerik.com/blogs/introduction-github-codespaces" target="_blank">GitHub Codespaces</a></p><p>My first encounter with GitHub Codespaces was when I delivered a Microsoft Cloud Immersion workshop. Getting started with Codespaces was a breeze. Onboarding developers is fun and fast. Just point them to the repo and off they go. Codespaces are like a dev vm with everything setup for you to get developing.</p><p><a href="https://t.co/TVnmZjrzQ3?amp=1" target="_blank">DevOps Periodic Table</a></p><p>Lots of tools in the DevOps space and this one is a creative way to visualize them.</p><p><a href="https://devblogs.microsoft.com/devops/level-up-your-skills-with-bicep/" target="_blank">Getting started with Bicep</a></p><p>Bicep is the new language that you can use to deploy your resources into Azure. It has a CLI and during the deployment stage, the Bicep CLI converts the bicep file into an ARM template. It makes writing IAAC code little bit easier. </p><p><i style="background-color: white; box-sizing: border-box; color: #212529; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 15.4px;">Tip: You can follow me on <a href="https://twitter.com/mitulsuthar" style="background-color: transparent; box-sizing: border-box; color: #2288bb; text-decoration-line: none;" target="_blank">twitter </a>to get instant updates whenever I tweet about DevOps</i></p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-25219522850671123762021-09-23T08:37:00.000-04:002021-09-23T08:40:32.092-04:00DevOps Links for 23/9/2021<p><span style="font-size: 11pt; white-space: pre-wrap;"><a href="https://github.com/bregman-arie/devops-exercises" target="_blank"><span style="font-family: inherit;">DevOps Exercises </span></a></span></p><p><span style="font-family: inherit;"><span style="font-size: 11pt; white-space: pre-wrap;">Everything that you can imagine related to DevOps can be found in this GitHub Repository. </span><span style="font-size: 14.6667px; white-space: pre-wrap;">Most comprehensive list of DevOps exercises, questions and answers on DevOps. </span><span style="font-size: 11pt; white-space: pre-wrap;">Enjoy.</span></span></p><span id="docs-internal-guid-c93954c8-7fff-51c3-5875-a35d2a329a0a"><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><a href="https://media.webteam.puppet.com/uploads/2021/07/Puppet-State-of-DevOps-Report-2021.pdf?_ga=2.816369.1192464680.1631802508-2090156983.1631802508" target="_blank"><span style="font-family: inherit;">State of the DevOps Report 2021</span></a></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: inherit;"><br /></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: inherit;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">I created a </span><a href="https://twitter.com/mitulsuthar/status/1438930045892968450" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">twitter thread</span></a><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> if you want to read more. Vast majority of the organizations are stuck in the middle of their DevOps journey. They haven't been able to bridge the gap between their organizational silos and achieve meaningful organizational change.</span></span></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">Mix of blockers for low-evolution DevOps teams include resistance to change, legacy architecture, shortage of skills, limited or lack of automation, and unclear goals or objectives.</span></span></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">Teams that are good at DevOps have strong identities, clear responsibilities with a high degree of autonomy over their own function and have well-defined interaction paradigms and communication channels with other teams. I think this is true of any high performing team.</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;"><br /></span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">High performing teams use Automation and Cloud to their advantage better than others.</span></span></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://devblogs.microsoft.com/devops/intro-of-devops-dojo/" style="font-family: inherit; font-size: 11pt; white-space: pre-wrap;" target="_blank">Introduction to DevOps Dojo</a><span style="font-family: inherit; font-size: 11pt; white-space: pre-wrap;">.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: inherit;"><br /></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">My favorite piece of information in the above Dojo article is the quote from Satya Nadella. </span></span></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;"></span></span></p><blockquote><span style="font-family: inherit;">"I want our best engineers to work on our engineering systems, so that we can later on come back and build all the new concepts we want." - Satya Nadella</span></blockquote></span><p></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">As an engineer, when you recommend making improvements to your internal systems, then very few managers understand the importance of prioritizing that work. These improvements are either brushed off as - not enough time, not billable work, it doesn’t add any value, and it is not a high priority. </span></span></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">GitHub Actions Tip: Create <a href="https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging" target="_blank">ACTIONS_STEP_DEBUG </a>secret and set it to true to view debug logs when your action runs.</span></span></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://github.com/actions/virtual-environments/blob/ubuntu20/20210913.1/images/linux/Ubuntu2004-README.md" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">Ever wonder what's installed on your GitHub Action Runner?</span></span></a></p><span style="font-family: inherit;"><br /></span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: inherit;">GitHub Actions Runner is a virtual machine that runs your different actions in the pipeline. These actions typically depend upon some software to be installed on this machine. Out of the box it comes with a long list of pre-installed software. It has CLIs for major cloud providers, package managers, build tools, and more. When your GitHub Action executes, in the log you can find a link that points to a GitHub page that lists all the software that is installed on that particular VM. For a sample, you can check the <a href=" https://github.com/actions/virtual-environments/blob/ubuntu20/20210913.1/images/linux/Ubuntu2004-README.md" target="_blank">Ubuntu 20.04.3 LTS list here</a>.</span></span></p><div><br /></div><div><i>Tip: You can follow me on <a href="https://twitter.com/mitulsuthar" target="_blank">twitter </a>to get instant updates whenever I tweet about DevOps. </i></div>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-14059811350796295912021-09-16T07:39:00.004-04:002021-09-23T08:40:10.267-04:00DevOps Links for 16/9/2021<p>Many people don't know the difference between Git and GitHub and it is a constant source of confusion for first timers. This post is a simple getting started post on Git and GitHub. </p><p><a href="https://techcommunity.microsoft.com/t5/microsoft-365-pnp-blog/how-to-get-started-with-github-and-git/ba-p/2736609?WT.mc_id=DOP-MVP-4025064" target="_blank"> <span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">How to get started with GitHub and Git</span></a></p><span id="docs-internal-guid-69430e7f-7fff-75c7-74b7-ce0395e47fe7"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">SQL Injection is still there in the top 10 OWASP list. Broken Access Control is at the top of OWASP 2021 list which you can find below. </p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><a href="https://t.co/7VgUyOjqzQ?amp=1" target="_blank">Here is the OWASP Top 10 for 2021</a></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p>A different take on branching strategy. In my opinion, you need a high degree of trust and keep changes small and adopt a strategy that works for your team.<br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><a href="https://martinfowler.com/articles/ship-show-ask.html" target="_blank">Branching Strategy - Ship / Show / Ask</a></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">All the Azure DevOps features visualized using Mind Map.</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: Arial; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;"><a href="https://abhijitjana.net/2021/09/12/azure-devops-in-a-nutshell-using-mind-map/" target="_blank">Azure DevOps In a Nutshell Mind Map</a></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">I am trying to find interesting questions on GitHub Actions on Stackoverflow. This is one of the highly voted questions on GitHub Actions.</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><a href="https://stackoverflow.com/questions/58033366/how-to-get-current-branch-within-github-actions" target="_blank">How to get current branch within GitHub actions</a></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span id="docs-internal-guid-c5b569ad-7fff-70f8-f03c-ef22f6412645"></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">This post lists four key metrics - Deployment Frequency, Lead Time for Changes, Change Failure Rate, Time to Restore Service, that you need to track to measure your DevOps performance in your organization.</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: Arial; font-size: 10pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Use Four Keys metrics like change failure rate to measure your DevOps performance | Google Cloud Blog</span></a></p><div><br /></div><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><i>Tip: You can follow me on <a href="https://twitter.com/mitulsuthar" target="_blank">twitter </a>to get instant updates whenever I tweet about DevOps. </i></p><div><br /></div></span>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-15075489581513596472021-09-09T07:25:00.003-04:002021-09-23T08:40:19.143-04:00DevOps Links for 9/9/2021If you are learning GitHub Actions, then this is a good place to start.<br /><br /><a href="https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions">Introduction to GitHub Actions</a> <br /><br /><div><br /></div><div>Currently, I am deploying different kinds of .NET application to Azure using GitHub Actions. You can find more information on how to deploy to Azure App Service below. <div><br /></div><div><a href="https://docs.github.com/en/actions/guides/deploying-to-azure-app-service">Deploy to Azure App Service using GitHub Actions</a> <br /><br /><br /></div><div>An interesting post on GitHub Actions Limitations and Gotchas. The <i>workflow_dispatch</i> feature needs major improvements. If you don't know what that is, then you will keep guessing what this feature is and how to discover it. This feature is to manually trigger the GitHub Actions.</div><div><div><br /></div><div><a href="https://www.cbui.dev/github-actions-limitations-and-gotchas/">GitHub Actions Limitations and Gotchas</a> <br /><br /><i>Tip: You can follow me on <a href="https://twitter.com/mitulsuthar" target="_blank">twitter </a>to get instant updates whenever I tweet about DevOps. </i><br /><span id="docs-internal-guid-a9f0413f-7fff-e0fb-01a9-84977032cb5d"><div><br /></div></span></div></div></div>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-41048648162325435992020-04-27T14:47:00.000-04:002020-04-27T14:47:49.180-04:00What is Helm for Kubernetes?<div dir="ltr" style="text-align: left;" trbidi="on">
In this post, I would like to talk about what is <a href="https://helm.sh/" target="_blank">Helm </a>and why do we need it, installing and uninstalling a chart and difference between a repo and a hub.<br />
<br />
For this post, I am also assuming you are familiar with <a href="https://kubernetes.io/" target="_blank">Kubernetes </a>on a high level.<br />
<br />
Before we dive into the details, first, let’s understand what does the word Helm mean in English.<br />
<br />
<blockquote class="tr_bq">
<i>“Helm is a lever or wheel controlling the rudder of a ship for steering.” – Merriam Webster</i></blockquote>
<br />
In a ship, you might have seen a wheel like mechanism used by the captain to steer the ship as shown below.<br />
<br />
<div style="text-align: center;">
<img alt="" src="data:image/png;base64,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" /></div>
<br />
The logo for Helm (in Kubernetes context) as seen on <a href="http://helm.sh/">Helm.sh</a> website is shown below. We can now somewhat connect the dots with respect to logos.<br />
<br />
<div style="text-align: center;">
<img alt="" src="data:image/png;base64,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" /></div>
<br />
If you want to setup wordpress inside a Kubernetes Cluster, then you will have to find relevant docker images for the wordpress front end and mysql database docker image and then setup networking, configuration, secrets, load balancing, etc., by installing multiple .yaml files.<br />
<br />
After you have setup everything and everything is working, you will feel like you don’t want to touch your setup. But life doesn’t end there and eventually, you will have to worry about things listed below.<br />
<br />
1. Delete deployments<br />
<br />
2. Setup another wordpress instance for another customer<br />
<br />
3. Update your images with new wordpress or mysql images<br />
<br />
4. Rollback installation manually<br />
<br />
and more.<br />
<br />
Wouldn’t it be nice, if we didn’t had to worry about any of those .yaml files?<br />
<br />
Wouldn’t it be nice, if we could leverage wordpress expert’s knowledge of installing and configuring wordpress into a cluster?<br />
<br />
What if we could just execute few commands to install, uninstall and upgrade a software?<br />
<br />
That’s what Helm does for Kubernetes. Helm helps you steer your software into your cluster.<br />
<br />
You can execute helm commands against your K8S cluster such as<br />
<br />
<span style="color: #e06666;">helm search hub bitnami/wordpress</span><br />
<br />
<span style="color: #e06666;">helm install my-wordpress bitnami/wordpress</span><br />
<br />
<span style="color: #e06666;">helm uninstall my-wordpress </span><br />
<br />
These commands would look familiar to you if you are familiar with apt-get or chocolatey or brew.<br />
<br />
Helm is just like apt-get, chocolatey or brew, a Package Manager.<br />
<br />
From the Helm website - “Helm is the package manager for Kubernetes. It is the best place to find, share and install software for Kubernetes”<br />
<br />
It is the package manager for your Kubernetes Cluster and<b><u> not for your machine</u></b>.<br />
<br />
You can install helm on your machine by following your operating system specific instructions as shown on the helm site - https://helm.sh/docs/intro/install/<br />
<br />
<br />
Helm utilizes the same Kubernetes APIs to install software into K8S cluster.<br />
<br />
A package manager is responsible for installing, uninstalling, and upgrading software packages into destination from a remote/local package repository.<br />
<br />
Likewise, Helm works against a repository hosted locally or remotely. A repo can be hosted by anyone. For example, Google has its own helm repository. Bitnami hosts its own repository.<br />
<br />
A repository contains many software packages. Each package has multiple versions.<br />
<br />
Within helm context, a package is called a <b>Chart</b>. From now on we will refer packages as <b>Charts</b>.<br />
<br />
By default, helm doesn’t know about any repository. If you want to use a particular repository, then you have to first add that repository to helm.<br />
<br />
<span style="color: #e06666;">helm repo add bitnami https://charts.bitnami.com/bitnami</span><br />
<br />
After you have done that, you can install any software from bitnami repository.<br />
<br />
<span style="color: #e06666;">helm install my-wordpress bitnami/wordpress</span><br />
<br />
When you execute the above command, you are telling helm to install the wordpress chart from the bitnami repository.<br />
<br />
You can uninstall a chart by<br />
<br />
<span style="color: #e06666;">helm uninstall my-wordpress</span><br />
<br />
A small recap – Helm is a package manager that works against one or more repositories hosted by anyone to install/uninstall/upgrade a chart into a Kubernetes Cluster.<br />
<br />
Are you with me so far? If yes, then let’s continue.<br />
<br />
Since, repositories can be hosted by anyone (some of them could be private or public), how to find and search charts within these repositories?<br />
<br />
Do we have to add every single repository out there?<br />
<br />
How do we discover these repositories?<br />
<br />
Helm Hub is a central location to easily find charts that are hosted outside the helm project.<br />
<br />
When you execute the command <span style="color: #e06666;">helm search –help</span>, you are presented with two options search hub or search repo. In the below comands, we are searching for wordpress chart against the hub and then against the repository.<br />
<br />
<span style="color: #e06666;">helm search hub wordpress</span><br />
<br />
<span style="color: #e06666;">helm search repo wordpress</span><br />
<br />
What happens when you install a chart (remember we called it package initially)?<br />
<br />
A Chart is nothing but collection of files that describe a related set of Kubernetes resources. For example, a wordpress chart will have all the .yaml files required to install wordpress. In addition to that it will have metadata information about the chart itself.<br />
<br />
When you install a chart, it creates a new release into your K8S cluster. A release is like an instance of a resource. For example, when you install wordpress chart using the install command mentioned earlier, it will create a new release by the name my-wordpress. That release is unique to this cluster.<br />
<br />
You can install a chart multiple times to create multiple releases. For example, if you can install wordpress chart 3 times, then you will have 3 wordpress instances configured—all with their own unique urls, usernames and passwords. By executing the below command, we will have 3 releases installed in our cluster.<br />
<br />
<span style="color: #e06666;">helm install my-wordpress1 bitnami/wordpress</span><br />
<br />
<span style="color: #e06666;">helm install my-wordpress2 bitnami/wordpress</span><br />
<br />
<span style="color: #e06666;">helm install my-wordpress3 bitnami/wordpress</span><br />
<br />
Clear?<br />
<br />
Take two, let’s say you install mysql chart 3 times in your cluster, then you have 3 mysql database instances configured in your cluster.<br />
<br />
You can execute helm list command to see what is installed in your cluster.<br />
<br />
Finally, you can uninstall a release by executing the command <span style="color: #e06666;">helm uninstall my-wordpress</span><br />
<br />
I hope this helped your understand helm a bit better.<br />
<div>
<br /></div>
</div>
Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-65859958899571697912019-07-17T11:51:00.001-04:002019-07-24T17:17:48.793-04:00Passing multiple parameters in an Angular Route using RouteLink<p>In Angular, let’s say you have a route defined like this. </p><p>{ path : 'user/:userId/building/:buildingid, component: UserScheduleDetailsComponent, pathMatch : 'full'}<p>and you want to navigate the user to this component by using this route. I couldn’t easily find a way to pass these parameters by using routelink. <p>So this is how you do it.<p><a [routerLink]="[ '/user/', userId, 'building', buildingId]">I am a link</a><p>The userid is public property in the .ts file of the controller. There is just one gotcha. In the middle, there are these hard coded strings, for eg. building, do not put ‘/’ in the beginning or at the end of ‘building’. Only the first part of this route can have ‘/’ in it.Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-22373732435768104932019-06-16T12:11:00.000-04:002019-06-16T12:50:09.032-04:00Unit Testing ASP.NET Core Web API using XUnit and FakeItEasy<p>Let’s take a one ASP.NET Core Web API method and add unit tests using XUnit and FakeItEasy.</p>
<p>My environment: Visual Studio 2019 Enterprise, ASP.NET Core 2.2, FakeItEasy, AutoFixture and XUnit.</p><p><strong>Source code</strong> </p>
<p>I have a Before and After version of the source code hosted on <a href="https://github.com/mitulsuthar/blog-code-repository/tree/master/AspNetCoreUnitTestingDemo">github</a>. In the After folder, you can view the completed solution.</p><p><strong>System Under Test</strong></p>
<p>There is a ProductsController with one HTTP GET method returning a list of products and a unit test project. There is a ProductService injected into ProductsController that returns the Products. In the following section, we would like to add tests around the code and make it more production ready. One of the goals of this post is to show how often we just start like the code snippet as shown below and then when the code goes into production, there are all sorts of conditions that we have to account for. We will add all those conditions but let’s do it by adding tests for each condition.</p>
<pre class="brush: csharp;">[Route("api/[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
[HttpGet]
public ActionResult<IEnumerable<Product>> Get()
{
return _productService.GetProducts();
}
}
</pre>
<p>Let’s write our first test that would validate that the Get() method shown above.</p>
<p>I like to start with a simple empty method with just the name of the test method split into three parts. This helps me understand under given condition how should the code behave. The three parts are explained as follows: </p><p>1.Actual Name of the method being tested – <strong>Get</strong></p>
<p>2.Condition – <strong>WhenThereAreProducts</strong></p>
<p>3.Expected Outcome - <strong>ShouldReturnActionResultOfProductsWith200StatusCode</strong></p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereAreProducts_ShouldReturnActionResultOfProductsWith200StatusCode()
{
}
</pre>
<p>We will add all the dependencies that are required for ProductsController, below is the code that does that.</p>
<pre class="brush: csharp;">using AutoFixture;
using System;
using UnitTestingDemo.Controllers;
using UnitTestingDemo.Services;
using Xunit;
using FakeItEasy;
namespace UnitTestingDemo.Tests
{
public class ProductControllerTest
{
//Fakes
private readonly IProductService _productService;
//Dummy Data Generator
private readonly Fixture _fixture;
//System under test
private readonly ProductsController _sut;
public ProductControllerTest()
{
_productService = A.Fake<IProductService>();
_sut = new ProductsController(_productService);
_fixture = new Fixture();
}
[Fact]
public void Get_WhenThereAreProducts_ShouldReturnActionResultOfProductsWith200StatusCode()
{
//Arrange
//Act
//Assert
}
}
}
</pre>
<p>In the above code, I have added comments that explain what each line of code does. The public constructor is responsible for setting up our private objects. The system under test is called as _sut, so in all the methods it is easier to locate the system under test. In the unit test, we have three sections, Arrange, Act and Assert. I like to put these comments, so it is easier to scan different pieces of logic.</p>
<p>Next, we will add code to Arrange and Act as shown below,</p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereAreProducts_ShouldReturnActionResultOfProductsWith200StatusCode()
{
//Arrange
var products = _fixture.CreateMany<Product>(3).ToList();
A.CallTo(() => _productService.GetProducts()).Returns(products);
//Act
var result = _sut.Get();
//Assert
}
</pre>
<p>In the above code, we create 3 fake products using _fixture and then using FakeItEasy we define that when GetProducts() is called it should return those three fake products. And then we call the _sut.Get().</p>
<p>In the Assert part, we want to make sure that our method _productService.GetProducts() was called and it returned a result that is of type ActionResult and it returns a 200 status code. If it doesn’t return that status code, then it should fail and then we will refactor our ProductsController code.</p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereAreProducts_ShouldReturnActionResultOfProductsWith200StatusCode()
{
//Arrange
var products = _fixture.CreateMany<Product>(3).ToList();
A.CallTo(() => _productService.GetProducts()).Returns(products);
//Act
var result = _sut.Get() as ActionResult<IEnumerable<Product>>;
//Assert
A.CallTo(() => _productService.GetProducts()).MustHaveHappenedOnceExactly();
Assert.IsType<ActionResult<IEnumerable<Product>>>(result);
Assert.NotNull(result);
Assert.Equal(products.Count, result.Value.Count());
}
</pre>
<p>In the above code, in the Assert section, we are making sure that _productService.GetProducts() must have been called only once, the type of the result is of type ActionResult<ienumerable><product>, the result is not null and the count of products returned are the same as we created in the Arrange section. Using this approach, we am not able to validate the status code of the result. In order to test the status code, we will have to modify the Controller code.</product></ienumerable></p>
<p>Lesson learned, just use ActionResult in the method signature and instead of returning directly a List<products>, return an <strong>Ok(products)</strong>. The OkObjectResult contains status code. Using ActionResult only makes it easier to test for different status codes.</products></p>
<p>Controller Method is now modified as below and the test is now modified to test for a valid 200 status code.</p>
<pre class="brush: csharp;">[HttpGet]
public ActionResult Get()
{
return Ok(_productService.GetProducts());
}
[Fact]
public void Get_WhenThereAreProducts_ShouldReturnActionResultOfProductsWith200StatusCode()
{
//Arrange
var products = _fixture.CreateMany<Product>(3).ToList();
A.CallTo(() => _productService.GetProducts()).Returns(products);
//Act
var result = _sut.Get() as OkObjectResult;
//Assert
A.CallTo(() => _productService.GetProducts()).MustHaveHappenedOnceExactly();
Assert.NotNull(result);
var returnValue = Assert.IsType<List<Product>>(result.Value);
Assert.Equal(products.Count, returnValue.Count());
Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
}
</pre>
<p>Now that we have all the Asserts statements passing, we ask ourselves another question, Is this the only test case to test against? Can there be more test cases that we haven’t accounted for? What if there is an unhandled exception or when there is no product found? </p><p>Let’s add another test that covers the test case of an unhandled exception. Before you look at the following code, ask yourself, what should be the expected outcome when there is an exception being thrown by the ProductService? It should probably return a 500 error code and possibly log the error.</p>
<p>First, let’s begin by writing an empty test with a descriptive name as shown below.</p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereIsUnhandledException_ShouldReturn500StatusCode()
{
//Arrange
//Act
//Assert
}
</pre>
<p>
Next, we define the behavior of ProductService to throw an exception whenever GetProducts is called. If an exception is thrown, then we want to ensure that 500 HTTP Status Code is returned from the web service. The following test will fail, since we are not handling that case properly.</p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereIsUnhandledException_ShouldReturn500StatusCode()
{
//Arrange
A.CallTo(() => _productService.GetProducts()).Throws<Exception>();
//Act
var result = _sut.Get() as StatusCodeResult;
//Assert
A.CallTo(() => _productService.GetProducts()).MustHaveHappenedOnceExactly();
Assert.NotNull(result);
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
}
</pre>
<p>
Let’s modify the Get method to handle unhandled exception by putting the processing logic into a try and catch block.
After modify the code as shown below, you can run the test again and this time it would pass.</p>
<pre class="brush: csharp;">[HttpGet]
public ActionResult Get()
{
try
{
return Ok(_productService.GetProducts());
}
catch (Exception ex)
{
}
return StatusCode(StatusCodes.Status500InternalServerError);
}
</pre>
<p>We would like to add some kind of logging in the catch exception part. Logging using ILogger is the way to go, however, unit testing using ILogger is a bit problematic, because you have to use Adapter pattern to create your own logger that uses ILogger. For this part, I created a simple Logger called MyLogger with just a Log method to demonstrate unit testing.</p>
<p>The MyLogger.cs code is shown below.</p>
<pre class="brush: csharp;">using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnitTestingDemo.Services
{
public interface IMyLogger
{
void Log(string message, Exception ex);
}
public class MyLogger : IMyLogger
{
public void Log(string message, Exception ex)
{
//Log to database or use application insights.
}
}
}
</pre>
<p>
The ProductsController.cs is modified to log exception as shown below.
</p>
<pre class="brush: csharp;">using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using UnitTestingDemo.Models;
using UnitTestingDemo.Services;
namespace UnitTestingDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
private readonly IMyLogger _logger;
public ProductsController(IProductService productService, IMyLogger logger)
{
_productService = productService;
_logger = logger;
}
[HttpGet]
public ActionResult Get()
{
try
{
return Ok(_productService.GetProducts());
}
catch (Exception ex)
{
_logger.Log($"The method {nameof(ProductService.GetProducts)} caused an exception", ex);
}
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
</pre>
<p>
The Startup.cs modified as shown below
</p>
<pre class="brush: csharp;">public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddTransient<IProductService, ProductService>();
services.AddSingleton<IMyLogger, MyLogger>();
}
</pre>
<p>
We now modify our unit test to in the following ways to make it pass,</p><p>1. We renamed the method to include logging piece.</p><p>2. Mocked behavior of MyLogger.cs class</p><p>3. Asserting that MyLogger’s Log method must have been called when there was an exception.</p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereIsUnhandledException_ShouldReturn500StatusCodeAndLogAnException()
{
//Arrange
A.CallTo(() => _productService.GetProducts()).Throws<Exception>();
A.CallTo(() => _logger.Log(A<string>._, A<Exception>._)).DoesNothing();
//Act
var result = _sut.Get() as StatusCodeResult;
//Assert
A.CallTo(() => _productService.GetProducts()).MustHaveHappenedOnceExactly();
A.CallTo(() => _logger.Log(A<string>._, A<Exception>._)).MustHaveHappenedOnceExactly();
Assert.NotNull(result);
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
}
</pre>
<p>Let’s add another test case to account for when there are no products found, then we would like to return a 404 Not Found result.</p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereAreNoProductsFound_ShouldReturn404NotFoundResult()
{
//Arrange
//Act
//Assert
}
</pre>
<p> Let’s write a failing unit test and then add the condition in our Controller Get method.
</p>
<pre class="brush: csharp;">[Fact]
public void Get_WhenThereAreNoProductsFound_ShouldReturn404NotFoundResult()
{
//Arrange
var products = new List<Product>();
A.CallTo(() => _productService.GetProducts()).Returns(products);
//Act
var result = _sut.Get() as NotFoundResult;
//Assert
A.CallTo(() => _productService.GetProducts()).MustHaveHappenedOnceExactly();
Assert.NotNull(result);
Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode);
}
</pre>
<p>
Modify the Get method as follows
</p>
<pre class="brush: csharp;">[HttpGet]
public ActionResult Get()
{
try
{
var products = _productService.GetProducts();
if (products?.Count > 0)
{
return Ok(products);
}
return NotFound();
}
catch (Exception ex)
{
_logger.Log($"The method {nameof(ProductService.GetProducts)} caused an exception", ex);
}
return StatusCode(StatusCodes.Status500InternalServerError);
}
</pre>
<p>
Finally, if you are using Swagger then adding the Produces attribute will result into better documentation. I know this isn’t related to unit testing but it is a nice to have.
</p>
<pre class="brush: csharp;">[HttpGet]
[ProducesResponseType(typeof(List<Product>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public ActionResult Get()
{
try
{
var products = _productService.GetProducts();
if (products?.Count > 0)
{
return Ok(products);
}
return NotFound();
}
catch (Exception ex)
{
_logger.Log($"The method {nameof(ProductService.GetProducts)} caused an exception", ex);
}
return StatusCode(StatusCodes.Status500InternalServerError);
}
</pre>
<p>
We have accounted for all the test cases to make this code ready for Production. </p><p>If you can think of any test case that I haven’t accounted for, then please let me know in the comments section below. The final version of the test can be found <a href="https://github.com/mitulsuthar/blog-code-repository/blob/master/AspNetCoreUnitTestingDemo/After/UnitTestingDemo/UnitTestingDemo.Tests/ProductControllerTest.cs">here</a>.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-74119404518285973252019-05-09T11:06:00.001-04:002019-05-09T11:06:26.891-04:00PowerApps and Flow Deployment Issues<p>In this article, we take a look at Flow deployment issues when it comes deploying multiple PowerApps applications. The issues are encountered when you have the following setup. </p><ul><li>You have multiple applications within the same environment. For instance in the same environment, you have a PowerApp called AppDEV, AppQA, AppUAT and AppPROD</li><li>You have applications connected to their respective SQL Servers. </li><li>You have a separate Flow for each App and they are named as FlowDEV, FlowQA, FlowUAT, and FlowPROD</li><li>Each Flow will use a different connection such as SQLConnectionDEV, SQLConnectionQA, SQLConnectionUAT and SQLConnectionPROD.</li></ul><p>To deploy the application, please follow procedure listed below:</p><ul><li>Export the application and on the Export package screen under Related Resource for the app, for each resource and for Import Setup option, select either <strong>Update</strong> or <strong>Create as New</strong> option.</li><li>Import the application and on the Import package screen under Related Resource for the app, for each resource and for the Import Setup option, select either <strong>Update</strong> or<strong> Create as New</strong> option.</li></ul><p>Ideal deployment scenario, </p><blockquote><p>Export AppDEV app and import it into AppQA app. The connections for AppDEV and AppQA are pointing to their respective connections including Flow. </p></blockquote><p><strong>Deployment 1. </strong></p><p>When we export the app for the first time, we select the <strong>Update</strong> option for the SQL Connector and <strong>Create as New</strong> for the Flow, since the Flow doesn’t exist in QA. </p><p>Expected Behavior</p><p>1. AppQA should be connected to SQLConnectionQA </p><p>2. FlowQA should be connected to SQLConnectionQA.</p><p>Actual Behavior</p><p>1. AppQA is connected to SQLConnectionQA </p><p>2. FlowQA is connected to SQLConnectionQA.</p><p>So this is good, as everything is as expected. Alright, follow along as new requirements have come up and require a new deployment.</p><p><strong>Deployment 2.</strong></p><p>Now assume that we have the environment setup as shown below. </p><p>1. AppDEV and FlowDEV connected to SQLConnectionDEV</p><p>2. AppQA and FlowQA connected to SQLConnectionQA.</p><p>When we export the application, our gut instinct is that since SQL Connector and Flow both exists as QA we need not to select “Create as New” for the Flow. So we select <strong>Update</strong> option during Export and Import of the Application. </p><p>Expected Behavior</p><p>1. AppQA should be connected to SQLConnectionQA </p><p>2. FlowQA should be connected to SQLConnectionQA.</p><p>Actual Behavior</p><p>1. AppQA is connected to SQLConnectionQA </p><p>2. <font color="#ff0000"><strong>FlowQA is now renamed to FlowDEV and is to SQLConnectionQA</strong>.</font></p><p>Our environment now looks like as follows:</p><p>1. AppDEV and FlowDEV connected to SQLConnectionDEV</p><p><font color="#ff0000">2. <strong>AppQA and FlowDEV connected to SQLConnectionQA</strong>.</font></p><p>To recap, if you didn’t notice, we now have two Flows by the same name called FlowDEV on pointing to SQLConnectionDEV and another pointing to SQLConnectionQA but by just looking at the name we don’t know which Flow is which. </p><p>The issues now start to compound. </p><p><strong>Deployment 3. </strong></p><p>On the Import screen, on the Import Setup option, click on the Update option. When presented with the Flow to select you will be presented with two flows by the same name FlowDEV. At this point you do not know which Flow is pointing to SQLConnectionQA. Accidentally, you clicked on the wrong Flow and then you deploy. </p><p>The issues happen because you do not know which Flow is getting used by your application. If you delete the wrong flow then the PowerApp starts misbehaving. Again if more people create additional flows and it becomes a huge mess trying to chase down the bug.</p><p><strong>Solution</strong></p><p>The solution to this problem we have come up with is to during the Import process always select “Create as New” option and then follow a naming convention. When you have multiple flows created by this method, you can delete the older flows.</p><p><strong>The naming convention</strong><p>Follow the naming convention if you are doing a deployment on 2019-05-12 at 10:47.<p><Environment>_NameoftheFlow_20190512_1047. <p><Environment> is either DEV, QA, UAT or PROD.<p><b>What are the benefits of using this naming convention?</b><p>The different parts of the name provide different benefits and they are as follows:<p>Environment – When multiple flow is helps determine the Flow is targeting which environment. However, always verify on Flow details page regarding which connecting string is being used. <p>Name of Flow – Provide a distinct name that will be unique across different applications. This will provide us some hints with the purpose of the Flow. <p>Current Date – Add date formatted as 20190512 (YYYYMMDD). It helps to identify when the Flow was created and when it is safe to delete the Flow. For instance, if a newer Flow exists for the same environment then a cleanup can be performed. <p>Current Time – Add current time formatted as 1053 (HHmm). It helps to distinguish Flows that were created the same day. <p>You can check flows that were created older than the most recent one and delete them. So far this approach has worked for us and you can suggest if you have an alternate way of deploying PowerApps with Flows in this kind of setup. </p><p><strong>What are the disadvantages of this approach?</strong></p><p>The Flow execution history will be lost if you are concerned with that. However, if everything was successful then after certain period of time probably you do not care about the execution runs. </p><p>It also has an administration overhead as you have to remember stuff and it is definitely not DevOps friendly approach. </p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-81985016873963572892019-02-21T22:51:00.001-05:002019-02-21T22:51:48.065-05:00A22-Use Azure DevOps to build a docker image and push to private repository<p>In this post, I want to explain the steps I took to create a docker image and push to a private docker hub repository using Azure DevOps. In the <a href="http://mscodingblog.blogspot.com/2019/02/a21-adding-docker-support-to-aspnet.html">previous post</a>, we looked at adding docker support to an existing ASP.NET Core SPA application that uses Angular 7.</p><p>It took me 17th attempts to get the build to work. In hindsight, it is always easy to say, that I could have read the logs or documentation but by just following the docs or the web interface of Azure DevOps, it isn’t obvious for a new user to figure this out. You can argue that Azure DevOps is great, or this and that, but it has its quirks and issues. I have been using it since it was called TFS 2010, so when I said a new user, I meant a new user from the standpoint of creating and publishing docker images. </p><p>Initially, I started with just two tasks in the build pipeline, Build an image and Push an image. It didn’t work because I didn’t built and push using <strong>dotnet build</strong> and <strong>dotnet publish</strong> tasks. After adding those two tasks, it still didn’t work because the files weren’t available inside docker container. To fix that, I had to make sure that <strong>dotnet publish</strong> was using the –output src as an argument. It is because in the docker file for the project, I am switching the working directory to /src. You can take a look at the docker file <a href="https://github.com/mitulsuthar/A100/blob/master/src/A100/Dockerfile">here</a>.</p><p>After doing that it still didn’t work and because I had to make sure that the build context was point to the folder that had dockerfile. Who came up with the name build context? Seriously. They could have named it “<strong>folder containing dockerfile</strong>”? The tool tips are the worst in Azure DevOps Builds Tasks. When you are stuck in a situation none of these tooltips make any sense. For instance, I am trying to figure out what does <strong>build context</strong> means. Is it a variable? Some build folder? And the tooltip says, “Path to build context”. But what does context mean? Crazy. </p><p>Alright, on to the next hurdle I overcame. Access denied. This one I tried multiple times. First of all, I added Docker Login and it didn’t work. Then I add a separate step to add tag. It still didn’t work. Because the tag you are supposed to provide has to match your docker hub repository name. Even after adding the same tag that docker is expecting it didn’t work. The reason was that Azure DevOps uses $(Build.BuildNumber) or something as image name and that same image name is being used when it tries to push to docker repository. As the last step that made it work was adding image name in the build tasks to be the same as docker is expecting. <strong><yourid>/<repositoryname>:tagname.</strong> </p><p>I ensured that all the docker build tasks are using the same name <strong><yourid>/<repositoryname>:tagname.</strong> Finally, I was able to push a docker image to docker hub. What a relief! </p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com1tag:blogger.com,1999:blog-7679200490261166601.post-87133023981924581752019-02-18T18:29:00.001-05:002019-02-18T18:29:27.390-05:00A21-Adding docker support to ASP.NET Core SPA Application<p>This post is about my experience adding docker support to the A100 website. You can find the github repo <a href="https://github.com/mitulsuthar/a100">here</a>.</p><p><em>Disclaimer: I am a beginner to docker and Linux, and I don’t know what I am doing while trying to configure docker but I just wanted to share what steps I took and where my frustrations were with respect to dockerizing existing ASP.NET Core SPA applications.</em></p><p>I thought to take a spin at docker and decided to dockerize the A100 website. I knew that you could just right click the project and click on “<em><strong>Add Docker Support</strong></em>” to dockerize an existing application. So I did that and then I clicked on F5 to debug the website inside a docker container from Visual Studio. It didn’t worked because there was no node installed.</p><p><strong>No node installed on the base dotnet runtime image</strong></p><p>The A100 ASP.NET Core application is a spa application built using the default Angular template. The backend is ASP.NET Web API and front-end is using Angular, all within a single Visual Studio project. The spa application depends on npm and node during compile time as well as runtime. The docker file that is generated uses nano server as the base image, which has the dotnet core runtime and nothing else. There is no node installed in the base nano server image.</p><p><strong>Not an easy way to install node on nano server without PowerShell</strong></p><p>This led me to the path of installing node on the nano server, but there is no easy way to install node because PowerShell is not available in those nano server images. </p><p>“<em>Mitul, curl.exe and tar.exe are available on nano server, why you didn’t try that</em>”- as you might suggest. I did try that, however, I couldn’t unzip the .zip file that I downloaded from the website using tar.exe. I tried tar.exe –xf node.zip multiple times but it didn’t work. Maybe there is a bug in the tar.exe on nano server. It kept saying “<em>Unrecognized format</em>”. Maybe tar doesn’t support .zip files. At this point, I gave up the idea of using a window server container image since I couldn’t get it to work. </p><p>“<em>But Mitul, you could have used multi stage builds in docker image to download the file inside server core and then copied into nano server image. Why didn’t you try that</em>?” – as you might suggest again. I didn’t realize this at this point and I learned that that’s how <a href="https://github.com/dotnet/dotnet-docker/blob/master/3.0/aspnetcore-runtime/nanoserver-1803/amd64/Dockerfile">dotnet is building their base image</a>. This is an option that I will try next. I hope it would work. </p><p>I followed this <a href="https://github.com/aspnet/Announcements/issues/298">github issue</a> to successfully install nodejs.</p><p><strong>Node-sass doesn’t work on Linux container </strong></p><p>I switched to Linux containers and then regenerated docker file again and this time too the base image didn’t had node installed on it. So I had to install nodejs both on the sdk image and the runtime image because you need once for compiling and another one during runtime. Finally, I was successfully able to install node. Yay! But when I tried to build docker image, it errored out complaining that node-sass is not suitable or not available for your environment. I came across the suggestion that I might have to rebuild it. I tried rebuilding node-sass package and this time I was successfully able to rebuild node-sass. However, I during runtime I kept getting error that could not generate .css from sass and it was node-sass related issue. It didn’t work. </p><blockquote><p>Error: Missing binding /app/ClientApp/node_modules/node-sass/vendor/linux-x64-67/binding.node<br>
Node Sass could not find a binding for your current environment: Linux 64-bit with Node.js 11.x</p>
<p>Found bindings for the following environments:<br> - Windows 64-bit with Node.js 10.x<br> - Windows 64-bit with Node.js 11.x</p>
<p>This usually happens because your environment has changed since running `npm install`.<br>
Run `npm rebuild node-sass` to download the binding for your current environment.</p></blockquote><p>My cross platform dream was vanishing quickly at this point. I was questioning my approach of developing in windows and hosting it in linux. Should I have developed inside a Linux environment like Windows Subsystem for Linux (WSL) instead? Since node-sass was giving issues, I decided to ditch sass and use scss. But I realized that scss is sass itself. Its newer version is called scss and it still requires node-sass. I was getting really frustrated. Finally, I switched to plain .css for the A100 website. </p><p>This step worked and I was able to do F5 and run the website from inside a Linux container. It might seem like that in one or two tries this thing worked for me. No. I spent one and half day in trying to figure this thing out with multiple attempts at building a docker image. I realized that there are few things where we can improve this experience for new users. </p><p><strong>Provide alternate ways of creating images with node installed</strong></p><p>I understand that ASP.NET Core team wants to keep the docker images as lean as possible but please provide documentation on how we can easily install node on nano server. Maybe there is an I was not able to find it. A better approach would be that when you try to click on “<em><strong>Add Docker Support</strong></em>” inside Visual Studio, and if this is a spa application, then provide a message or add comments to the docker file indicating that there is no node installed and please follow this article to install node in this docker container. </p><p><strong>Node-sass npm package to be fixed</strong></p><p>The node-sass package has given me the maximum issues. I do not understand that why would node-sass not work on Linux. Inside of package.json, there is nothing specific related to windows then when it tries to install node-sass on Linux, then it should try to install node-sass that is native to Linux. Why it is failing to work properly on Linux? I have no idea. It would be nice if this is addressed as I am not using sass or scss for now. I could be wrong but I have seen in tutorials or on youtube videos that most people are using sass or scss. If this is the case then this has to be improved.</p><p>I learnt that I need to read more documentation regarding docker, Linux and ASP.NET Core. If you have suggestions then please provide me in improving my understanding of docker. </p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com2tag:blogger.com,1999:blog-7679200490261166601.post-32237138959628782482019-01-15T02:51:00.001-05:002019-01-15T02:51:43.115-05:00PowerApps From A DevOps Perspective<p>In this post, I would like to talk about PowerApps from a DevOps perspective. When we think about DevOps we think about Source Control, Continuous Integration, Automation, Configuration Management etc. Let’s take a look into it.</p>
<p><strong>No Source Control for PowerApps</strong> </p><p>When you are developing with PowerApps, there is no way to do Source Control. There are no source files. The only artifact you can version control is the .zip file that you can export. The application that you create/edit in the browser is continuously updated and whenever you feel like it is ready for publishing then you make that version of the application available to users in your organization. Lack of version control causes problems when you cannot track down which version of the application caused a particular bug. The only way to guarantee no bugs is by doing a thorough testing of the application. </p><p><strong>Multiple Environments Beware</strong></p><p>Creating a separate Dev, Test, UAT and Production environment is quite common for DevOps practices. In PowerApps, you can create separate environments in PowerApps but it comes with its own problems. For instance, in order to create and administer environments you need Plan 2 License in PowerApps. This is not a big deal since you can manage with one license. If you are creating application that uses On-Premises Data Gateway then only applications from the Default Environment can connect to the Gateway. Yeah! if you thought that was a bummer. Then you are right. Well all hope is not lost, if you really need it then you have to create a support ticket with Microsoft. Another limitation with multiple environments is that you cannot create as many environments as you like. Because you can only create two production environments. Another bummer. If you wanted Test, UAT and Production environment then you are toast. You will have to request another user license and then you would be able to create it. In PowerApps, having multiple environments creates more issues in my opinion from a DevOps perspective. It is not as seamless as one would come to expect from a traditional DevOps practice. If you are creating Model Driven apps only then it makes sense to create multiple environments. If you are creating only Canvas Driven apps then just create different apps with – DEV, TEST, UAT and PROD suffixes. Having multiple apps for different purposes creates it own issues as detailed below.</p><p><strong>CI/CD Pipeline</strong></p><p>In PowerApps, you don’t have to build your code. Any change you make to the application is live for you to test it. In that way it is very productive. To publish the application you just click on the publish button and it is live. But what if you screw an update, then you have an option of going back to a previous version of the application. The workflow of getting the application to end-users all the way to production is not seamless. Since we create different app for DEV,TEST,UAT we had to export and import every time we had to propagate a change to these apps. For one of the applications we built it was quite a lot of work every time we exported and imported into another application-dev/test/prod. For instance, we had to ensure that all the connections were pointing to environment specific connection strings, data going in accurately, perform manual checks, create a new flow every time. </p><p><strong>Configuration Management</strong> </p><p>Well there is none. You cannot manage configuration of your PowerApps application in one place. For example, you can put app settings inside web.config file in a typical asp.net web application. In asp.net when you change environments then you can just update web.config. If you want to persist your app settings then it is better to put them inside a SQL table if you are utilizing On-Premises databases. In other words, store your app settings into an external persistent store. </p><p><strong>Collaboration</strong></p><p>It is very common to collaborate with brilliant team mates on different projects. If you are using any modern source control system then you can easily collaborate with Team members and work on features simultaneously. In PowerApps, only one developer can work on the application at given point of time. Good luck building your next ERP front end! One of the selling points of PowerApps is that it is very good for building quick small single purpose application that a single developer can churn out in days. But not good when you want to build a large application. </p><p>Since we are on the topic of collaboration let me add it here. If you developing application for an enterprise and think people are going to use it, then please don’t use your own personal account to develop the application. Because the user who creates the application becomes the owner of the application and guess what, you cannot change the owner of the application later on. If you are an ops guy then you want to have that ownership of the application. Please utilize a service account to develop PowerApps application. </p><p>That’s all I have for today and if you would like to share anything regarding PowerApps please let me know in the comment below. </p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com3tag:blogger.com,1999:blog-7679200490261166601.post-2596526483680544152018-12-17T23:39:00.001-05:002018-12-18T11:18:08.719-05:005 Takeaways for Building PowerApps Application<p>In this post, I would like to share some of the pitfalls we encountered while developing an enterprise grade PowerApps Application. </p><p>First some background about the application. The key components of the application comprised of an on-premises SQL Server, Enterprise Data Gateway, Microsoft Flow and Windows Service. The SQL Server database consisted of enterprise ERP data and hence entities in this database had lots of columns just like a typical ERP system. The application we built was PowerApps Canvas application where you have more control over the look and feel of the application. We were surprised by how much you can customize the look of the application and make it look enterprizey [see I made up a new word].</p><p><strong>Limit the number of controls</strong><br>
There are couple of reasons why you want to limit controls that are present on a page and in the whole application. </p><p>1. For performance reasons, in PowerApps Canvas driven apps, if you are adding lots of controls on a given page then performance of the application degrades. We saw performance of dropdowns controls and calendar controls decrease. Dropdown would appear after a delay and calendar popup would show after a delay.</p><p>2. In PowerApps if you add any control in the application then you can access that control from anywhere unlike Windows Forms page where you can only access controls that are present on a given page. Oh! that’s powerful you might say and that is why it is called PowerApps. As you add more controls to PowerApps, it becomes difficult to keep track of all the controls and soon you will run out of creative and unique names to provide for your controls.</p><p>3. After some point when you have lots of controls, you will forget control names and make mistakes while trying to reference the right control. Why did that popup didn’t disappear when it was supposed to.</p>
<p><strong>Limit the logic you put into the Functions bar</strong><br>
One of the SOLID principles is Separation of Concerns and when you go against it then it bites you where it hurts the most. In PowerApps, presentation layer is meant for displaying and Microsoft Flow is for handling business logic processing. The Function bar at the top is so powerful that you can accomplish a whole lot with so many functions at your disposal. So no wonder we added a ton of business logic into it and to a point that lines of code increased to 1000 lines. No kidding. In hindsight, it is always easy to point the mistake and bang on the head that it was so obvious but everyday it is not. Let’s go through the issues with putting lot of lines of code into that tiny function bar at the top. People familiar with Excel will be able to relate to this. </p><p>1. Every single time to view code you have to expand the function bar.</p><p>2. Endless scrolling of numerous lines of code. </p><p>3. As lines of code increases, error messages looked like some ancient Egyptian symbols. We were spending hours debugging the error message. Luckily we knew how to comment code :) and narrow down the issues.</p><p>4. Smallest mistake or a syntax issue can take up hours to resolve it because the error message don’t point to the the current line of code. Oh and did I tell you that there are no line numbers for the code you write in that function bar. </p><p>5. Function bar freezes when there are many lines of code. Even few characters of text can take few seconds to appear. When you try to comment out a section it takes a while to take effect. </p><p>6. Bonus! There is no source control for that code. So if you broke something then good luck. Like a good programmer you should always use source control for any type of code. If you had lot of logic associated with a button and you clicked on Associated Flow with this button then oops you just lost all that logic. Please put code into source control.</p><p><strong>Limit the number of columns and records you display</strong><br>
PowerApps is not meant to create the front end of an ERP system and it should not be used for such purposes. PowerApps shines when you build an application that fulfils that last mile gap. As there are more columns on a given page, the page becomes slower to respond and UX is not the best when a user has to scroll horizontally or vertically to view the full picture.</p><p><strong>Put application settings into external table</strong></p><p>In PowerApps, there is no App.Config or Web.Config file where one can put application settings and change them. So it is important that you store app settings into an external table. If you hard code some key into Application then when you try to export and import PowerApps to create a new application then you will have to edit that key again. Try to do that at multiple places and this process become quickly inefficient. Again this is basic 101 if you a developer. </p><p><strong>Do not fear Flow because of Licensing issues</strong></p><p><strong></strong>Early on we wanted to be cost effective and hence avoided using Flow. The moment we found ourselves stuck with having to debug 1000 lines of code again and again we decided to give Flow a chance. What we learnt after reading the docs is that for our purposes, the Flow quota allotted for per user per month were plenty. If you have Office 365 license then you have 2000 executions allowed per user per month and it is aggregated at the tenant level. As soon as we resorted to Flow and extracted key business logic into SQL Server stored procedure, we were back to application stability. </p><p>So that’s all I have to share this Monday late night. If you have your learnings regarding PowerApps please share in the comments below. </p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-6984326780760618662018-12-04T19:01:00.001-05:002018-12-04T19:01:10.399-05:00A20-Upgrading to Angular 7 and ASP.NET Core<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a13building-app-using-angular-and.html">A13</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a14building-app-using-angular-and.html">A14</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a15building-app-using-angular-and.html">A15</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a16building-app-using-angular-and.html">A16</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a17building-app-using-angular-and.html">A17</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a18building-app-using-angular-and.html">A18</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a19building-app-using-angular-and.html">A19</a>, <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>Due to personal issues (new job, new country, India trip), I wasn’t able to blog. </p><p>Since I posted last time, Angular came out with Angular 7 and a new SDK for .NET Core also came out. Instead of continuing on the older version of Angular I decided to update everything.</p><p>I updated </p><p>Node to latest version v11.3.0.</p><p>NPM is at 6.4.1</p><p>I updated Visual Studio to latest and VS Code to latest version. </p><p>For updating angular to 7, I followed steps outlined at <a title="https://update.angular.io/" href="https://update.angular.io/">https://update.angular.io/</a></p><p>Updated Angular-cli globally and locally </p><p>Updated Angular Core and RxJs stuff. </p><p>Github warns you of vulnerabilities and everytime you run npm commands it shows you issues with packages. There were lot of package vulnerabilities so one by one I removed all of them and npm audit is not complaining anymore. </p><p>After updating all the packages and running ng update for angular packages, I was getting errors when running dotnet run. </p><p><a href="https://lh3.googleusercontent.com/-6zurqHSRM9w/XAcVPbU1I0I/AAAAAAAASFc/hvmSvvmKlcM0qpdbOla4CbzSTB4fIjQIwCHMYCw/s1600-h/image%255B3%255D"><img width="644" height="280" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-2yUNt7srs28/XAcVQN3FwPI/AAAAAAAASFg/lCjPNCHXN8kZvH9nNZK53ePhn7N-M0p2wCHMYCw/image_thumb%255B1%255D?imgmax=800" border="0"></a></p><p>I had to update nuget package Microsoft.AspNetCore.SpaServices.Extensions. After updating it, I had to fix package.json scripts section’s start and build command to remove –extract-css flags. </p><p>Last issue I had to fix was related to certain rxjs operator usages in the application, eg. of, map, catch. </p><p><a href="https://lh3.googleusercontent.com/-2h0gNJT4zMg/XAcVQtDpEJI/AAAAAAAASFk/pJOOEiSTjSogRCQt98FJEPwj90CHL1ebgCHMYCw/s1600-h/image%255B7%255D"><img width="644" height="83" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-kusuRGS3hKo/XAcVRNDMSDI/AAAAAAAASFo/PSTIm4Zh2vosDuvgUD2BamxkRsHc_ubiQCHMYCw/image_thumb%255B3%255D?imgmax=800" border="0"></a></p><p>After fixing those I was able to compile and run the command dotnet run to view the application in the browser as I used to do before. </p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-67626656186916152732018-07-12T22:49:00.001-04:002018-07-12T22:49:20.711-04:00A19–Building App using Angular and ASP.NET Core 2.1–Review Order Details<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a13building-app-using-angular-and.html">A13</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a14building-app-using-angular-and.html">A14</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a15building-app-using-angular-and.html">A15</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a16building-app-using-angular-and.html">A16</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a17building-app-using-angular-and.html">A17</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a18building-app-using-angular-and.html">A18</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>On training front, I finished Angular Reactive Forms course by Deborah Kurata on Pluralsight and I am watching Angular Services course by Brice Wilson.</p><p>Today, I was able to accomplish following.</p><p>1. Create a Review Order Page. </p><p>2. Create relevant server side API methods to return data</p><p>3. On submit button show confirmation page. </p><p><a href="https://lh3.googleusercontent.com/-u8fzMpsT4NI/W0gTIY34BAI/AAAAAAAAP4g/jZZgJhMB25IN98MshdgRjgIRx6joHcmQACHMYCw/s1600-h/image%255B6%255D"><img width="425" height="484" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-IFwvqyAKVZw/W0gTJMEz07I/AAAAAAAAP4k/_6UPaSx8CoMhpu0Tv4Ds6fZ2kZ4zT7CPgCHMYCw/image_thumb%255B2%255D?imgmax=800" border="0"></a></p><p><a href="https://lh3.googleusercontent.com/-XpsrWtlNEoQ/W0gTJ2UhpcI/AAAAAAAAP4o/TuIkd8X2Zhc08jba5LtL0mIYZc91FVLpACHMYCw/s1600-h/image%255B7%255D"><img width="336" height="484" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-56dMq94bjIs/W0gTKqK5SrI/AAAAAAAAP4s/EdXqamWttPABgbXr6u5z0xOzPcgYrhANwCHMYCw/image_thumb%255B3%255D?imgmax=800" border="0"></a></p><p><a href="https://lh3.googleusercontent.com/-2skqgOzw8Tw/W0gTLDlQnqI/AAAAAAAAP4w/YItkhH0ETCogx68EON1unjMCsvZZlIqDgCHMYCw/s1600-h/image%255B10%255D"><img width="244" height="93" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-Vq-w83gYXGc/W0gTL9LE8nI/AAAAAAAAP40/rD4El-A_TKQVlzyIWCxroQK-Yl-XtG4eACHMYCw/image_thumb%255B4%255D?imgmax=800" border="0"></a></p><p>One of the things I had to change was make model names to be camelCase so when retrieving nested object they would display appropriately. </p><p>While there are many many issues with the app, I am able to do accomplish one workflow of being able to checkout products in a wizard style format. Notice that I am returning hard coded data for order review page. Once we figure out persistent store we can then return data from data store. There is also an issue with user identity. We want to associate orders with a person and we want to associate orders with unique id. All of that complexity can be handled piece by piece. </p><p>Next I want to tackle authentication piece.</p><p>That’s all I have for today. Checkout latest changes on <a href="https://github.com/mitulsuthar/A100">github</a> and <a href="https://a100store.azurewebsites.net/">website</a>.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-19344252808887000882018-07-10T00:50:00.001-04:002018-07-10T00:50:22.930-04:00A18–Building App using Angular and ASP.NET Core 2.1–Reactive Forms, Submitting data<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a13building-app-using-angular-and.html">A13</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a14building-app-using-angular-and.html">A14</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a15building-app-using-angular-and.html">A15</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a16building-app-using-angular-and.html">A16</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a17building-app-using-angular-and.html">A17</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>So I was able to accomplish following</p><p>1. Created classes to collect data from Form into Model for Shipping and Payment Pages.</p><p>2. Create CheckoutService to call Checkout controller methods</p><p>3. Created Controller methods for insert of shipping info and payment info</p><p>That’s all I have for today. Checkout latest changes on <a href="https://github.com/mitulsuthar/A100">github</a> and <a href="https://a100store.azurewebsites.net/">website</a>.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-9044492272496548422018-07-08T09:43:00.001-04:002018-07-08T09:43:48.544-04:00A17–Building App using Angular and ASP.NET Core 2.1–Reactive Forms Validation<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a13building-app-using-angular-and.html">A13</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a14building-app-using-angular-and.html">A14</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a15building-app-using-angular-and.html">A15</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a16building-app-using-angular-and.html">A16</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>I am currently watching Angular Reactive Forms course on Pluralsight by Deborah Kurata. I like Reactive Forms. </p><p>Today, I added form validation to shipping info page and payment info page. </p><p><a href="https://lh3.googleusercontent.com/-I58AU34aWtA/W0IU7gqx5UI/AAAAAAAAPws/odFLQHlNxjAlVljaWTfx0HMw3oKKcCjogCHMYCw/s1600-h/image%255B2%255D"><img width="223" height="244" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-Zml9itKnpvE/W0IVCNRDYmI/AAAAAAAAPww/pM9sIm28_NwALRphM-aCC95YaZwsllmqwCHMYCw/image_thumb?imgmax=800" border="0"></a></p><p><a href="https://lh3.googleusercontent.com/-l87wz3VKNqE/W0IVCxHZ5-I/AAAAAAAAPw0/KycCv13QAA4QkaWLNdWzPSN3tdZ9m_fswCHMYCw/s1600-h/image%255B5%255D"><img width="213" height="244" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-f2qtApWdF9o/W0IVDcKG52I/AAAAAAAAPw4/L05KoVUxY78DEjUkHI8onfSVxMCKVcPUQCHMYCw/image_thumb%255B1%255D?imgmax=800" border="0"></a></p><p>Couple of things to note regarding behavior</p><p>1. Validation occurs as soon as you tab our or touch a control. </p><p>2. If you click on Next button then validation messages will be shown </p><p>3. Simple Regex pattern matching used for phone, card number, security code</p><p>4. Added dropdown for selecting card type and expiration month and year</p><p>5. Custom group validation implemented for validation Expiration month. </p><p><a href="https://lh3.googleusercontent.com/-j0jEq7gEA9E/W0IVD5zceAI/AAAAAAAAPw8/ukdB5DhBLLggXzQnD8WbZp0w5Bp5iHREgCHMYCw/s1600-h/image%255B9%255D"><img width="644" height="285" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-ZaoXKzEZAgs/W0IVEg2r6lI/AAAAAAAAPxA/_BhIs53q4msjcfnLpPy6PPyLmLH8GRj3ACHMYCw/image_thumb%255B3%255D?imgmax=800" border="0"></a></p><p>Special thanks to <a href="https://github.com/loiane">Loiane Groner</a> for sharing code @ <a href="https://github.com/loiane/angular-reactive-forms-validate-submit/blob/master/src/app/submit-flag-form/submit-flag-form.component.ts">repo</a> to show validation on button click. This trick and using a method to display validation messages saved a ton of repetitive code for me. I like it a lot. </p><p>Thanks to <a href="https://github.com/DeborahK">Debora Kurata</a> for sharing code @ <a href="https://github.com/DeborahK/Angular2-ReactiveForms">repo</a>.</p><p>That’s all I have for today. Checkout latest changes on <a href="https://github.com/mitulsuthar/A100">github</a> and <a href="https://a100store.azurewebsites.net/">website</a>.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-31153055641527174572018-07-04T03:42:00.001-04:002018-07-04T03:42:59.707-04:00A16–Building App using Angular and ASP.NET Core 2.1–Reactive Forms<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a13building-app-using-angular-and.html">A13</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a14building-app-using-angular-and.html">A14</a>, <a href="http://mscodingblog.blogspot.com/2018/07/a15building-app-using-angular-and.html">A15</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>I finished watching Angular Routing course and I think that it is very powerful. I like multiple router-outlet, child routes and route guard concept. Route resolver is a good concept too but I am running into issues when implementing in another child route. </p><p>Today using reactive forms I have added forms to collect shipping and payment information. There is no validation logic in there right now. </p><p>Shipping Info Form</p><p><a href="https://lh3.googleusercontent.com/-PZxAmj2oQWY/Wzx6en9uUII/AAAAAAAAPms/4d7ToNOG0nMnlHsFlqdLgAtAj-FvlljhQCHMYCw/s1600-h/image%255B2%255D"><img width="244" height="169" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-QICSGBYHiXA/Wzx6fcSzB4I/AAAAAAAAPmw/fK-pQkYLnywGof6_1GtX4iKCUW2HLFnRwCHMYCw/image_thumb?imgmax=800" border="0"></a></p><p>Payment Info Form</p><p><a href="https://lh3.googleusercontent.com/-GA1D-0Y0Sjo/Wzx6f2u3zAI/AAAAAAAAPm0/_vFNFJnuWMEXJvGOWzY_78F-RuB1jQf6ACHMYCw/s1600-h/image%255B5%255D"><img width="244" height="173" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-gU5bicsXSQE/Wzx6gntNMPI/AAAAAAAAPm4/FJLqpj5u28Y3pji_8whIgDT6PC_TA6aXgCHMYCw/image_thumb%255B1%255D?imgmax=800" border="0"></a></p><p>One of my pet peeve with Angular is dependency injection. I ran into an issue with a child route resolver using a service and I couldn’t figure out why it won’t resolve the service. I wish there was a better way to let us know about why it couldn’t resolve the service. Can we have Angular Dependency Injection best practice guide? </p><p>Alright everyone, that’s all I have for today. Checkout latest changes on <a href="https://github.com/mitulsuthar/A100">github</a> and <a href="https://a100store.azurewebsites.net/">website</a>.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-4559625999144502262018-07-01T23:48:00.001-04:002018-07-01T23:48:07.745-04:00A15–Building App using Angular and ASP.NET Core 2.1–Route Resolvers, Child Routes<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a13building-app-using-angular-and.html">A13</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a14building-app-using-angular-and.html">A14</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>Past three days have been hectic so there were no posts. </p><p>I am currently watching Angular Routing pluralsight course by Debora Kurata. I learned about Route Resolvers and Child routes. I refactored product/edit, product/details, product/delete page to use route resolver. Next I wanted to finish checkout page but I wanted to have a wizard type flow. After learning about child routes, I added shipping info, payment details, order review and confirmation components. I also refactored shopping cart as part of wizard. Shopping cart is the first page in this wizard. </p><p>Now when you click on shopping cart icon to the top right of the app you will see.</p><p><a href="https://lh3.googleusercontent.com/-eK-saGQhTYM/WzmgUzD1IRI/AAAAAAAAPgo/HGwigGDTrtA9XY9l7qmVkq44zFJ-rFgrgCHMYCw/s1600-h/image%255B3%255D"><img width="644" height="389" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-X1LYwq9F37A/WzmgV0-R31I/AAAAAAAAPgs/lJ85VItYQdcV0036ATHGXe8lw01KT3wYwCHMYCw/image_thumb%255B1%255D?imgmax=800" border="0"></a></p><p>At the top, you can see different stages as nav items. As you start clicking different nav items you will see respective components. </p><p><a href="https://lh3.googleusercontent.com/-l8vewtgjxPU/WzmgWd3QsOI/AAAAAAAAPgw/NypQzD6L9ToMyFE3jxsBzb2guUcaav5FACHMYCw/s1600-h/image%255B6%255D"><img width="244" height="47" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-_I6w3h_I9po/WzmgXLUCutI/AAAAAAAAPg0/KNPAqIbFx3w4VUwguySrbjqu8Sh025aogCHMYCw/image_thumb%255B2%255D?imgmax=800" border="0"></a></p><p>Below is our shopping-routing module. </p><pre class="brush: javascript">import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component';
import { CheckoutComponent } from './checkout/checkout.component';
import { CheckoutPaymentComponent } from './checkout-payment/checkout-payment.component';
import { CheckoutReviewComponent } from './checkout-review/checkout-review.component';
import { CheckoutConfirmationComponent } from './checkout-confirmation/checkout-confirmation.component';
import { CheckoutShippinginfoComponent } from './checkout-shippinginfo/checkout-shippinginfo.component';
const routes: Routes = [
{
path: 'checkout', component: CheckoutComponent, children: [
{ path: '', redirectTo: 'shoppingcart', pathMatch: 'full'},
{ path: 'shoppingcart', component: ShoppingCartComponent} ,
{ path: 'shippinginfo', component: CheckoutShippinginfoComponent },
{ path: 'payment', component: CheckoutPaymentComponent },
{ path: 'review', component: CheckoutReviewComponent },
{ path: 'confirmation', component: CheckoutConfirmationComponent }
]
}];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ShoppingRoutingModule { }
</pre>
<p>There is one thing I learned about property pathMatch: ‘full’—you cannot add it wherever you like. Initially I added to checkout path, and then I added to different child paths and it wasn’t working. So I removed that property.</p><p>Other minor css improvements include navbar nav-item active styling. </p><p><a href="https://lh3.googleusercontent.com/-NwR4r-oqyXU/WzmgXgeASRI/AAAAAAAAPg4/uMWHpT4uBlUV7z0_IYt4fX0z_vO-jK7VgCHMYCw/s1600-h/image%255B9%255D"><img width="244" height="60" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-BHWo0a0KArw/WzmgYKHsWbI/AAAAAAAAPg8/ArNbfR8lrGg5dtn_K-AiNDjZHA5bCFNzQCHMYCw/image_thumb%255B3%255D?imgmax=800" border="0"></a></p><p>One thing bugging me is responsiveness of top wizard navigation for checkout component. I want to figure best way to handle that. I am thinking to show full text with icon and on smaller screens just show icons. As shown below, you can see how bad the styling is on smaller screens—completely unacceptable.</p><p><a href="https://lh3.googleusercontent.com/-XGI9GK3-OK0/WzmgZXPpqpI/AAAAAAAAPhA/5N4455_Vmv4voh5q9NCaQnEZYRnSfkfzgCHMYCw/s1600-h/image%255B12%255D"><img width="181" height="244" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-d3rSTRAVvdM/WzmgdlSLbPI/AAAAAAAAPhE/oBn3PQ2nmaUhh22YOf7p9weEQXXurgTaQCHMYCw/image_thumb%255B4%255D?imgmax=800" border="0"></a></p><p>Alright everyone, that’s all I have for today. Checkout latest changes on <a href="https://github.com/mitulsuthar/A100">github</a> and <a href="https://a100store.azurewebsites.net/">website</a>.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-14047872805693263102018-06-27T00:30:00.001-04:002018-06-27T00:30:04.291-04:00A14–Building App using Angular and ASP.NET Core 2.1–Responsive Product Card and Shopping Cart<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a13building-app-using-angular-and.html">A13</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>Today’s post is again going to be brief. You might think that these posts are becoming boring and I don’t put more code. My goal is to work on this app just a little bit everyday and learn something everyday. I don’t get to spend a lot of time on this app since most of the time I am working late at night. Sometimes I might do a lot and sometimes not a whole lot. So my goal is just learn and share—even if it is one sentence that I watched a video. </p><p>I tried to make product card and shopping cart responsive. I added checkout-contact component and hooked up to checkout button. However, I didn’t implemented logic for collecting contact information. I am thinking of first completing entire navigation scenario of checkout process and then implement details of each page. </p><p>Alright that’s it. Checkout the code on <a href="https://github.com/mitulsuthar/A100">github</a>.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-33917875961202384932018-06-26T01:42:00.001-04:002018-06-26T01:42:44.891-04:00A13–Building App using Angular and ASP.NET Core 2.1–Updating Shopping Cart Quantity<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a12building-app-using-angular-and.html">A12</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>Today’s post is going to be brief. I added following functionality to Shopping Cart page. </p><p>When you increase quantity of any given product then,</p><p>1. Order sub total should get updated. </p><p>2. Order Quantity should be updated. </p><p>3. Cart total should be updated. </p><p>Since there is no database backend, I have to add extra logic to account for persistence. For example, to simulate accurate quantity and amount post deletion, I had to write custom server side logic. This is unnecessary once data store is in place. </p><p><a href="https://lh3.googleusercontent.com/-n1-tyK-S4Eg/WzHSTPQKWrI/AAAAAAAAOf4/JHF6zpNCpps5FYYoFCY440K6gqeGp8etgCHMYCw/s1600-h/image%255B5%255D"><img width="244" height="112" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-1vT8340j4GY/WzHSTnCgLhI/AAAAAAAAOf8/-Drx8u422-YgKRXPaGUc1dR0tKXswIeOACHMYCw/image_thumb%255B1%255D?imgmax=800" border="0"></a></p><p>After increasing quantity order sub total is updated. </p><p><a href="https://lh3.googleusercontent.com/-Foq53oClsUk/WzHSURMFXCI/AAAAAAAAOgA/qNJy91rSK4knrUR2NwzkthQJq9t5HcBCQCHMYCw/s1600-h/image%255B2%255D"><img width="244" height="128" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-fXCxJTnSsRI/WzHSU2uj6EI/AAAAAAAAOgE/pk587gmGDlYa3zCXN9kQiDG3gUvRUi3kQCHMYCw/image_thumb?imgmax=800" border="0"></a></p><p>There is one issue I am running into and that is when using ngModelChange on input element, the change event is firing twice which seems like bug to me or I am doing something wrong (I think later to be true).</p><p>That’s all for today, see ya next time. </p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-76756299675040931632018-06-25T01:05:00.001-04:002018-06-25T01:05:38.452-04:00A12–Building App using Angular and ASP.NET Core 2.1–Shopping Cart<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a11building-app-using-angular-and.html">A11</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>In the previous post, I started created Shopping Cart page where you could view products and remove products from Shopping Cart. Today, I want to fix a couple of things about Shopping Cart. If you added multiple products then we want to do following things.</p><p>1. Show Product along with quantity</p><p>2. Change Product quantity</p><p>3. Show Order Total </p><p>4. Update Order Total when product is deleted. </p><p>5. Do not show same product multiple times if product is ordered multiple times. </p><p>Below is the UI for new checkout experience.</p><p><a href="https://lh3.googleusercontent.com/-0bRAchpIJIA/WzB4GhJDORI/AAAAAAAAOZU/WnMmQy6wPFMHREQjVhI8uWFzdy5SYgDDQCHMYCw/s1600-h/image%255B3%255D"><img width="644" height="265" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-8Igpt51OVkA/WzB4HGmGyPI/AAAAAAAAOZY/uB4oPKZ1aMkMiH1Xg3IxIruLOs67Bb2nACHMYCw/image_thumb%255B1%255D?imgmax=800" border="0"></a></p><p>If I remove product then order total is updated. </p><p><a href="https://lh3.googleusercontent.com/-pQ071s0cLto/WzB4Hm93VpI/AAAAAAAAOZc/54ihkOKt6PgXGdo0cFbo4DFrrV5mKuwSwCHMYCw/s1600-h/image%255B7%255D"><img width="644" height="187" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/--9Hd-ar6qQw/WzB4IP2w_JI/AAAAAAAAOZg/LybqVMqe9jkd7tFxkDGFDnJq1vVzkcHYQCHMYCw/image_thumb%255B3%255D?imgmax=800" border="0"></a></p><p>I created a complete different class for displaying products on Shopping Cart Page. I am calling it ShoppingCartProduct. In the ShoppingCartController, I am getting all products and then doing a group by into ShoppingCartProduct.</p>
<pre class="brush: csharp">using A100.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
namespace A100.Controllers
{
[Route("api/[controller]")]
public class ShoppingCartController : Controller
{
private static List<Product> shoppingCartProducts = new List<Product>() {
new Product { Id = 1, Title = "XYZ1",Description = "Description XYZ 1 ", Price = 101,ImageUrl = "ImageUrl_XYZ1" },
new Product { Id = 1, Title = "XYZ1",Description = "Description XYZ 1 ", Price = 101,ImageUrl = "ImageUrl_XYZ1" },
new Product { Id = 1, Title = "XYZ1",Description = "Description XYZ 1 ", Price = 101,ImageUrl = "ImageUrl_XYZ1" },
new Product { Id = 2, Title = "XYZ2",Description = "Description XYZ 2 ", Price = 200,ImageUrl = "ImageUrl_XYZ2" }
};
[HttpGet("[action]")]
public IEnumerable<ShoppingCartProduct> Products()
{
var s = from y in shoppingCartProducts
group y by y.Id into grouping
let p = grouping.First()
select new ShoppingCartProduct { Id = grouping.Key,
Title = p.Title,
Quantity = grouping.Count(),
Price = p.Price,
Description = p.Description,
ImageUrl = p.ImageUrl,
TotalPrice = p.Price * grouping.Count() };
return s.ToList();
}
[HttpPost("[action]/{id:int}")]
public ActionResult Add(int id)
{
return new JsonResult("product added to cart");
}
[HttpPost("[action]/{id:int}")]
public ActionResult Delete(int id)
{
return new JsonResult("product removed from cart");
}
}
}
</pre>
<p>I had to update relevant parts shopping-cart.component.ts as well. </p><p>That’s all I have for today. Please check source code on <a href="https://github.com/mitulsuthar/A100">Github</a> and ask questions if you have any.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-20098928059636088812018-06-22T01:51:00.001-04:002018-06-22T01:51:23.632-04:00A11–Building App using Angular and ASP.NET Core 2.1–Services and Component Refactoring<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a10building-app-using-angular-and.html">A10</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>Yesterday, I added items to Shopping Cart and API to add products to Shopping Cart. Next step is to be able to checkout products from Shopping Cart and in order to do so I needed a page to display products in Shopping Cart. So Below are things, I was able to accomplish today. </p><p>1. Added Shopping Cart Component, Display Products in the cart, Remove products from Shopping Cart. </p><p><a href="https://lh3.googleusercontent.com/-Mbvl5YguDEc/WyyOV6hjInI/AAAAAAAAOXw/L0rkDfPmV78cO_VouPVtsXvDKgswzZ_TQCHMYCw/s1600-h/image%255B2%255D"><img width="244" height="193" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-1vQOdhdevig/WyyOWUKGbFI/AAAAAAAAOX0/nbleFcTfLQswATPbezfb-guCyprovjeBACHMYCw/image_thumb?imgmax=800" border="0"></a></p><p>2. I added Shopping Module and Routing Module for that. Since after adding Shopping Module, I was running into issues related to ShoppingCartWidget component. NavMenu component was complaining that you have to declare in the main module. I moved ShoppingCartWidget component into Shared Folder and reference from there. I had to refactor little bit regarding that. </p><p>3. Added code to Shopping Service related to removing product from shopping cart. </p><p>There are couple of things, I wanted to share that I liked. I switch between VS Code and Visual Studio for this project and I am in love with VS Code. Today I installed few extensions for VS and it makes your job much easier. Below are the ones I use for Angular Development. </p><p>1. Angular 6 Snippets – Mikael Morlund</p><p>2. Angular Files – Alexander Ivanichev</p><p>3. Angular Language Service – Angular</p><p>4. Angular V6 Snippets – John Papa</p><p>5. Debugger for Chrome – Microsoft </p><p>6. TSLint – Egamma </p><p>7. SASS – Robin Bentley </p><p>Angular Language Service extension and snippets is by far my favorite one. But they all helped me a lot. VS Code is so light weight. I like it a lot. </p><p>On learning path I am intending on finishing below courses </p><p>1. Learning Angular Routing by Debora Kurata on Pluralsight</p><p>2. Implementing and Securing an API with ASP.NET Core by Shawn Wildermuth on Pluralsight</p><p>See ya next time. And leave comments if you like anything or have any suggestion. </p><p><a href="https://github.com/mitulsuthar/A100">Code on GitHub.</a></p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-61659869312653709982018-06-21T00:21:00.001-04:002018-06-21T12:11:10.892-04:00A10–Building App using Angular and ASP.NET Core 2.1–Services, Events and Cross Component Communication<div dir="ltr" style="text-align: left;" trbidi="on">
This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a9building-app-using-angular-and-aspnet.html">A9</a> <a href="https://github.com/mitulsuthar/A100">Github Repo</a><br />
Today, I was able to accomplish few things, <br />
1. Added Shopping service that adds product and updates shopping cart <br />
2. Established cross component communication via Shopping Service (Home Component –> Add To Cart –> Updates Shopping Cart<br />
3. Created API for adding and getting products from ShoppingCart Controller. I am returning hard coded products and hence you will always see two shown at the top right.<br />
Right now I didn’t wanted to create any database backend. I want to just make sure client server interaction is in sync. Once I like how everything is structured then I will invest time into adding database layer. Below is code for Shopping Service.<br />
<pre class="brush: javascript">import { Injectable, Inject } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Product } from '../product/product';
import { catchError, map, tap,last } from 'rxjs/operators';
import 'rxjs/add/observable/throw';
import { of } from 'rxjs/observable/of';
import { Observable } from 'rxjs/Observable';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
@Injectable()
export class ShoppingService {
private _baseUrl: string;
private cartQuantitySource = new BehaviorSubject(0);
currentCartQuantity = this.cartQuantitySource.asObservable();
private _addProductToShoppingCart: string = "api/ShoppingCart/Add/";
private _getProductsFromShoppingCart: string = "api/ShoppingCart/Products";
constructor(private _http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
this._baseUrl = baseUrl;
this.updateCartQuantity();
}
updateCartQuantity() {
this._http.get<Product[]>(this._baseUrl + this._getProductsFromShoppingCart).subscribe(products => {
this.log(`getting products from shopping cart`);
this.cartQuantitySource.next(products.length);
});
}
addProductToCart(product: Product) {
this._http.post(this._baseUrl + this._addProductToShoppingCart + product.id, product, httpOptions).subscribe(x => {
this.log(`added Product To Shopping Cart`);
this.updateCartQuantity();
});
}
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.log(error); //log to console instead
this.log(`${operation} failed: ${error.message}`);
return Observable.throw(error || 'Server error');
};
}
private log(message: string) {
console.log("Shopping Service " + message);
}
}
</pre>
One of the things, I am not liking is how api urls are hard coded as strings. This is something I want to improve upon. I will put them inside an injectable service which has all the urls. <br />
Next thing I want to learn is authentication and authorization with ASP.NET Core.</div>
Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0tag:blogger.com,1999:blog-7679200490261166601.post-47773393406495916052018-06-20T01:04:00.001-04:002018-06-20T01:06:33.488-04:00A9–Building App using Angular and ASP.NET Core 2.1–Font-awesome, Pagination and Layout<p>This post is a part of a series of posts that I am writing as I am building an app using Angular and ASP.NET Core 2.1. Links to previous posts –> <a href="http://mscodingblog.blogspot.com/2018/06/a1building-app-using-angular-and-aspnet.html">A1</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a2building-app-using-angular-and-aspnet.html">A2</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a3building-app-using-angular-and-aspnet.html">A3</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a4building-app-using-angular-and-aspnet.html">A4</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a5building-app-using-angular-and-aspnet.html">A5</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a6building-app-using-angular-and-aspnet.html">A6</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a7building-app-using-angular-and-aspnet.html">A7</a>, <a href="http://mscodingblog.blogspot.com/2018/06/a8building-app-using-angular-and-aspnet.html">A8</a>, <a href="https://github.com/mitulsuthar/A100">Github Repo</a></p><p>Today, I got carried away with home page layout and wasn’t able to fix communication between components. I spent trying to add pagination using some blogpost but then I discovered ngx-pagination. Below are things I was able to do.</p><p>1. Added pagination using ngx-pagination npm package it was pretty straight forward</p><p>2. Added font-awesome npm package so I could add shopping cart icon. </p><p>3. Added pagination to home page and products page</p><p>4. Added shopping-cart-widget component for displaying cart information</p><p><a href="https://lh3.googleusercontent.com/-moQcECjQmlA/WyngbaQYYDI/AAAAAAAAOWM/dfrNXnxz4pwZrr3S_uUnBHsmNSiiKpwHACHMYCw/s1600-h/image%255B3%255D"><img width="644" height="335" title="image" style="display: inline; background-image: none;" alt="image" src="https://lh3.googleusercontent.com/-VHmVO1ACW2E/WyngcXTiRII/AAAAAAAAOWQ/d5aFenhR4rsljmYel5_FMceS813y2WtOwCHMYCw/image_thumb%255B1%255D?imgmax=800" border="0"></a></p><p>Next, I want to fix communication between components, ie. when you click on add to cart button it should add items to cart and call server side api method. </p><p>See ya next time.</p>Mitul Sutharhttp://www.blogger.com/profile/10382570646381807553noreply@blogger.com0