<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Arjav Dave's Blog]]></title><description><![CDATA[Arjav Dave's Blog]]></description><link>https://blog.royalecheese.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 05 Sep 2026 07:57:53 GMT</lastBuildDate><atom:link href="https://blog.royalecheese.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How To Debug Node JS Inside Docker?]]></title><description><![CDATA[What is a Debugger?
For any developer, the debugger is the best friend. One can easily find bugs in software with a debugger.
One can add a breakpoint to pause execution. Secondly, one can also add logic to a breakpoint to halt the execution. As an e...]]></description><link>https://blog.royalecheese.com/how-to-debug-node-js-inside-docker</link><guid isPermaLink="true">https://blog.royalecheese.com/how-to-debug-node-js-inside-docker</guid><category><![CDATA[Docker]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Thu, 05 May 2022 05:34:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/jOqJbvo1P9g/upload/v1651728720357/YalEWtMhY.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-a-debugger">What is a Debugger?</h2>
<p>For any developer, the debugger is the best friend. One can easily find bugs in software with a debugger.</p>
<p>One can add a breakpoint to pause execution. Secondly, one can also add logic to a breakpoint to halt the execution. As an example, consider a <code>for</code> loop having 1,000 iterations. The execution should stop when the iteration count reaches above 100. To do so, put a breakpoint on the <code>for</code> loop. Next, add the logic to halt the execution when the iteration goes above 100.</p>
<p>Besides halting a program, debuggers show memory allocations. For example, halting the execution will show memory consumed at any given point.</p>
<h2 id="heading-what-is-a-remote-debugger">What Is a Remote Debugger?</h2>
<p>Debugging is usually done on a localhost. Doing it remotely is called remote debugging :). That is, if you debug software running on a remote host, its called remote debugging. It is helpful for multiple reasons.</p>
<p>For one, one can debug software locally. Consider a scenario where software is on the cloud. It might be deployed either for dev, UAT, or production. Now an issue happens on the cloud but not on the localhost. In this case, it would be very helpful to connect to the cloud and attach the debugger to the process. One can execute the software line by line to evaluate the issue and fix it.</p>
<p>Secondly, remote debugging is also useful when the software is running inside a container. Let’s say a project is running inside Docker. One won’t be directly able to run the project and connect to it via the debugger. Instead, the docker container should expose its container port. Secondly, the remote debugger needs configuration to connect the project inside the docker container.</p>
<p>Docker helps create portable containers that are fast and easy to deploy on various machines. These containers can be run locally on your Windows, Mac &amp; Linux. Also, major cloud systems like AWS or Azure do support them out of the box. If you want to learn more Docker basics and need a cheat sheet for Docker CLI, <a target="_blank" href="https://betterprogramming.pub/a-beginners-cheat-sheet-for-docker-f5024fd6c17f">here</a> is an introductory article about it.</p>
<p>In this article, we will set up a NodeJS project to run inside a docker container. We will also set up a remote debugging for the project.</p>
<p>If you love this article so far, please <a target="_blank" href="https://blog.royalecheese.com/">follow me</a> and do check out other such awesome articles on my profile.</p>
<h2 id="heading-setting-up-the-project">Setting Up the Project</h2>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before we move further, the system should have docker desktop and VS Code installed. Other than that, no other requirements are there.</p>
<p>For the hasty ones, I have made the source code available as a repository. You can check it out <a target="_blank" href="https://github.com/shenanigan/docker-node-debug">here</a>.</p>
<h3 id="heading-creating-project-files">Creating Project Files</h3>
<p>We are going to create a very simple express Node JS project. It will simply return a static JSON string on opening a specific URL. For this, we will create a file named <code>server.js</code>, which is the entry point to our project.</p>
<p>Create a <code>server.js</code> file with the following contents:</p>
<pre><code>const server <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>)();
server.listen(<span class="hljs-number">3000</span>, async () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { });
server.get(<span class="hljs-string">"/node-app"</span>, async (<span class="hljs-keyword">_</span>, response) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    response.json({ <span class="hljs-string">"node"</span>: <span class="hljs-string">"app"</span> });
});
</code></pre><p>The <code>server.js</code> file states that display <code>{“node”: “app”}</code> on opening <code>http://localhost:3000/node-app</code> URL in the browser.</p>
<p>Secondly, we will need a <code>package.json</code> file to configure the project and add dependencies. For that, create a <code>package.json</code> file with the following content:</p>
<pre><code>{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"node-app"</span>,
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"express"</span>: <span class="hljs-string">"^4.17.1"</span>
    }
}
</code></pre><p>Run the <code>npm install</code> command to install the dependencies locally. This will create a <code>node_modules</code> in the project directory.</p>
<p>Even though we will be running the project inside a container, the dependencies need to be installed. It is needed since we will be mapping our current project directory to a container project directory. It is explained below how to do so.</p>
<h3 id="heading-running-as-docker-container">Running as Docker Container</h3>
<p>A <code>Dockerfile</code> is needed to run the project as a docker container. Create a <code>Dockerfile</code> with the following contents:</p>
<pre><code><span class="hljs-comment"># Download the slim version of node</span>
FROM node:<span class="hljs-number">17</span>-slim
<span class="hljs-comment"># Needed for monitoring any file changes</span>
RUN <span class="hljs-built_in">npm</span> install -g nodemon
<span class="hljs-comment"># Set the work directory to app folder. </span>
<span class="hljs-comment"># We will be copying our code here</span>
WORKDIR /node
<span class="hljs-comment">#Copy all files from current directory to the container</span>
COPY . .
<span class="hljs-comment"># Needed for production. Check comments below</span>
RUN <span class="hljs-built_in">npm</span> install
</code></pre><p>Here, the project is set up to run as a simple node server without allowing any breakpoints. The container will be running the project out of a node directory inside the container. nodemon is installed globally in the container. It’s needed for watching any file change in the directory. It is explained in detail below.</p>
<p>The <code>RUN npm install</code> command is needed only when deploying to production. We will map the <code>/node</code> directory of our container to the current project directory on localhost using Docker Compose (next section). But when the app is deployed on the container, it needs to install the dependencies on its own.</p>
<h3 id="heading-docker-ignore">Docker Ignore</h3>
<p>The Docker ignore feature is very much similar to git ignore. <code>.gitignore</code> doesn’t track the files or folders mentioned in it. Similarly, we don’t want to copy unnecessary files in the container, which takes up space.</p>
<p>In our case, we don’t want to copy the node_modules folder to the container. To do so, create a <code>.dockerignore</code> file in the project directory with the following contents:</p>
<pre><code>node_modules<span class="hljs-operator">/</span>
</code></pre><h3 id="heading-docker-compose">Docker Compose</h3>
<p>Docker Compose is a really helpful way to build and run docker containers with a single command. It is also helpful for running multiple containers at the same time. It is one of the reasons we use docker compose instead of plain docker. To know more about docker compose and how to run multiple containers, please visit the article Run Multiple Containers With Docker Compose.</p>
<p>Now, let’s create a <code>docker-compose.yml</code> file to add some more configurations. Add the below contents to <code>docker-compose.yml</code> file once created:</p>
<pre><code><span class="hljs-attr">version:</span> <span class="hljs-string">'3.4'</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">node-app:</span>
    <span class="hljs-comment"># 1. build the current directory</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span>
    <span class="hljs-comment"># 2. Run the project using nodemon, for monitoring file changes</span>
    <span class="hljs-comment"># Run the debugger on 9229 port</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">nodemon</span> <span class="hljs-string">--inspect=0.0.0.0:9229</span> <span class="hljs-string">/node/server.js</span> <span class="hljs-number">3000</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-comment"># 3. Bind the current directory on local machine with /node inside the container.</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.:/node</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-comment"># 4. map the 3000 and 9229 ports of container and host</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"3000:3000"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"9229:9229"</span>
</code></pre><p>The <code>docker-compose.yml</code> file is explained point-wise below.</p>
<p>Point to our current directory for building the project.
Run the project using nodemon, since if there are any changes in the local directory, we want to restart the project in the docker with the changes. Nodemon is a utility that will monitor for any changes in your source and automatically restart your server.
Bind our current directory to the <code>/node</code> directory using volumes.</p>
<p>In addition to exposing and binding the 3000 port for the server, expose the 9229 for attaching the debugger.</p>
<p>Use the above <code>docker-compose.yml</code> file only for debugging.</p>
<p>The above <code>docker-compose.yml</code> exposes the debug port. In addition, it also monitors for any file changes inside the container (which are not going to happen). Lastly, it maps the volumes of the container to the project directory.</p>
<p>For production, create a new file <code>docker-compose-prod.yml</code> with the following contents:</p>
<pre><code>version: <span class="hljs-string">'3.4'</span>
services:
  node<span class="hljs-operator">-</span>app:
    build: .
    command: node <span class="hljs-operator">/</span>node<span class="hljs-operator">/</span>server.js <span class="hljs-number">3000</span>
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-string">"3000:3000"</span>
</code></pre><p>It simply runs the project and exposes the 3000 port. We are using multiple docker compose files to manage separate environments. Check the Running a Project section below to understand how to run a project based on different docker compose files.</p>
<p>Before we can run the project, we still need to configure the debugger to connect to the container.</p>
<h3 id="heading-configure-a-remote-debugger">Configure a Remote Debugger</h3>
<p>First, check if you have <code>launch.json</code> file created in your project. <code>launch.json</code> defines different types of configurations we can run for debugging. If it is not created, visit the <code>RUN AND DEBUG</code> tab on the left in your VS Code, as seen in the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651728153089/qyvgaJ_ng.png" alt="Screenshot 2022-05-02 at 4.57.08 PM.png" /></p>
<p>Click on the text that says create a <code>launch.json</code> file. Before you can proceed, it will ask the type of application to proceed. Select <code>Node.js</code>. It will create a new <code>launch.json</code> file in your project with a default Node.js configuration added.</p>
<p>Since we are not going to run the node application locally, go ahead and delete that configuration. Instead, replace the launch.json file with the following content:</p>
<pre><code>{
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.2.0"</span>,
    <span class="hljs-attr">"configurations"</span>: [
        {
            <span class="hljs-comment">// 1. Type of application to attach to</span>
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"node"</span>,

            <span class="hljs-comment">// 2. Type of request. In this case 'attach'</span>
            <span class="hljs-attr">"request"</span>: <span class="hljs-string">"attach"</span>,
            <span class="hljs-comment">// 3. Restart the debugger whenever it gets disconnected</span>
            <span class="hljs-attr">"restart"</span>: <span class="hljs-literal">true</span>,
            <span class="hljs-comment">// 4. Port to connect to </span>
            <span class="hljs-attr">"port"</span>: <span class="hljs-number">9229</span>,
            <span class="hljs-comment">// 5. Name of the configuration</span>
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Docker: Attach to Node"</span>,
            <span class="hljs-comment">// 6. Connect to /node directory of docker</span>
            <span class="hljs-attr">"remoteRoot"</span>: <span class="hljs-string">"/node"</span>
        }
    ]
}
</code></pre><p>The configuration added is pretty self-explanatory. Basically, we are asking the debugger to connect to a remote host with port number 9229. We are also requesting the debugger to restart whenever it gets disconnected to the host. By default, the debugger tries to connect on <code>http://localhost:9229/</code>. But project is hosted inside the <code>/node</code> directory in docker. To map <code>/node</code>, the remoteRoot attribute is used.</p>
<h2 id="heading-running-the-project">Running the Project</h2>
<p>That’s about it! Now, if you run docker compose up, your project will start running. For the first run, it will download some layers of the node slim SDK and then install nodemon inside the docker container. But, subsequent runs would be much faster. Running docker compose up will show the following output in your terminal:</p>
<pre><code><span class="hljs-attribute">docker</span> compose up
</code></pre><p>In order to attach the debugger, run the Docker: Attach to Node task from the <code>RUN AND DEBUG</code> tab. The debugger will now attach to the <code>/node</code> directory of your docker container. Next, put a breakpoint on line 4 of your <code>server.js</code> file, i.e., <code>response.json({ “super”: “app1” });</code>. Finally, open your browser and hit <code>http://localhost:3000</code>. The breakpoint will be hit, and the execution will halt.</p>
<p>For production, we need to use the <code>docker-compose-prod.yml</code> file. To do so, we need to mention the file name in the docker command. Execute the following command to run the project as if in a production environment:</p>
<pre><code>docker compose <span class="hljs-operator">-</span>f docker<span class="hljs-operator">-</span>compose<span class="hljs-operator">-</span>prod.yml up
</code></pre><p>With the above command, a debugger cannot be attached to the container since we are not exposing any debugging point.</p>
<h2 id="heading-source-code">Source Code</h2>
<p>Here is the <a target="_blank" href="https://github.com/shenanigan/docker-node-debug">link</a> to the final source code of the project we have created.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Debugging is one of the best things for development. It’s the cherry on top when we are able to debug remotely. Remote debugging enables us to connect to code running not only on the cloud but also to a docker container running locally.</p>
<p>I hope you have enjoyed this article. Feel free to check out some of my other articles:</p>
<ul>
<li><a target="_blank" href="https://betterprogramming.pub/a-beginners-cheat-sheet-for-docker-f5024fd6c17f">Docker: An introduction and cheat sheet</a></li>
<li><a target="_blank" href="https://betterprogramming.pub/run-multiple-containers-with-docker-compose-9297957f7a3c">Running multiple containers with Docker Compose</a> </li>
<li><a target="_blank" href="https://arjavdave.com/2021/03/16/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops/">Setup CI/CD for Android with Azure Pipelines</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Lazy Loading in Angular — A Beginner’s Guide]]></title><description><![CDATA[What is Lazy Loading?


Lazy loading is a process of loading components, modules or other assets of a website as required. Since, Angular creates a SPA (Single Page Application), all of its components are loaded, at once. Secondly, a lot of unnecessa...]]></description><link>https://blog.royalecheese.com/lazy-loading-in-angular-a-beginners-guide</link><guid isPermaLink="true">https://blog.royalecheese.com/lazy-loading-in-angular-a-beginners-guide</guid><category><![CDATA[Angular]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[performance]]></category><category><![CDATA[SEO]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Wed, 28 Apr 2021 07:20:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619594294756/tr5NdDJYb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[
<p></p><h2>What is Lazy Loading?</h2>
<p></p>

<p></p><p>Lazy loading is a process of loading components, modules or other assets of a website as required. Since, Angular creates a <a href="https://en.wikipedia.org/wiki/Single-page_application#:~:text=From%20Wikipedia%2C%20the%20free%20encyclopedia,browser%20loading%20entire%20new%20pages." target="_blank">SPA (Single Page Application)</a>, all of its components are loaded, at once. Secondly, a lot of unnecessary libraries or modules might be loaded as well.</p>
<p></p>

<p></p><p>For a small application it would be okay, but as the application grows the load time will increase, if everything is loaded at once. Lazy loading allows Angular to load components and modules as and when needed.</p>
<p></p>

<p></p><p>First of all, to understand how lazy loading works in Angular, we need to understand the basic building blocks of the framework: NgModules.</p>
<p></p>

<p></p><p>In order to understand how Lazy Loading works we first need to understand the building block of Angular: NgModules.</p>
<p></p>

<p></p><h2>What are NgModules?</h2>
<p></p>

<p></p><p>Angular libraries like RouterModule, BrowserModule, FormsModule are NgModules. Angular Material, which is a 3rd party, is also a type of NgModule. NgModule consists of files &amp; code related to a specific domain or having a similar set of functionalities.</p>
<p></p>

<p></p><p>A typical NgModule file declares components, directives, pipes, and services. It can also import other modules that are needed in the current module.</p>
<p></p>

<p></p><p>One of the important advantage of NgModules is they can be lazy loaded.  Let's have a look at how we can configure lazy loading.</p>
<p></p>

<p></p><h2>How to Create NgModules</h2>
<p></p>

<p></p><p>In this tutorial, we will create two modules <em>Module</em> <em>A</em> and <em>Module B</em> which will be lazy loaded. On the main screen we will have two buttons for loading each module lazily.</p>
<p></p>

<p></p><h4>Create a Project</h4>
<p></p>

<p></p><p>Create a new Angular project <em>lazy-load-demo</em> by executing the below command.</p>
<p></p>

<pre><code>ng <span class="hljs-built_in">new</span> lazy-<span class="hljs-keyword">load</span>-demo <span class="hljs-comment">--routing --strict --style css</span>
code lazy-<span class="hljs-keyword">load</span>-demo
</code></pre>

<p></p><p>Here, we are creating a new project with routing. Secondly, the <a href="https://angular.io/guide/strict-mode" target="_blank">strict mode</a> is enabled. Lastly, we are mentioning the stylesheet format to css. The second command opens your project in VS Code. </p>
<p></p>

<p></p><h4>Root Module</h4>
<p></p>

<p></p><p>By default, a root module or app module is created under <em>/src/app</em>. Below is the NgModule file created:</p>
<p></p>

<pre><code><span class="hljs-keyword">import</span> { NgModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { BrowserModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/platform-browser'</span>;

<span class="hljs-keyword">import</span> { AppRoutingModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app-routing.module'</span>;
<span class="hljs-keyword">import</span> { AppComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.component'</span>;

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppModule</span> { }</span>
</code></pre>

<p></p><p>First, we are importing all the required modules and components.</p>
<p></p>

<p></p><p>After that, <em><strong>@NgModule</strong></em> decorator states that AppModule class is a type of NgModule. The decorator accepts <em>declarations, imports, providers, and bootstrap. </em>Here are the descriptions for each of them:</p>
<p></p>

<p></p><ul><li><strong><em>declarations</em></strong>: The components in this module.</li><li><strong><em>imports</em></strong>: The modules that are required by the current module.</li><li><strong><em>providers</em></strong>: The service providers if any.</li><li><strong><em>bootstrap</em></strong>: The <em>root</em> component that Angular creates and inserts into the <code>index.html</code> host web page.</li></ul>
<p></p>

<p></p><h4>Main screen</h4>
<p></p>

<p></p><p>The main screen will have 2 buttons, namely, <strong><em>Load Module A </em></strong>&amp; <strong><em>Load Module B.</em></strong> As the name suggests, clicking these buttons will lazily load each module. </p>
<p></p>

<p></p><p>For that, replace your <em>app.component.html</em> file with the contents below:</p>
<p></p>

<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding: 20px; color: white; background-color: green;"</span> <span class="hljs-attr">routerLink</span>=<span class="hljs-string">"a"</span>&gt;</span>Load Module A<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding: 20px; color: white; background-color: blue;"</span> <span class="hljs-attr">routerLink</span>=<span class="hljs-string">"b"</span>&gt;</span>Load Module B<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">router-outlet</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-outlet</span>&gt;</span>
</code></pre>

<p></p><p>Let's define the modules for routes <em>a </em>&amp; <em>b</em>.</p>
<p></p>

<p></p><h4>Lazy Loaded Modules</h4>
<p></p>

<p></p><p>In order to create lazy loaded modules execute the below commands.</p>
<p></p>

<pre><code>ng generate <span class="hljs-keyword">module</span> modulea --route a --<span class="hljs-keyword">module</span> app.<span class="hljs-keyword">module</span>
ng generate <span class="hljs-keyword">module</span> moduleb --route b --<span class="hljs-keyword">module</span> app.<span class="hljs-keyword">module</span>
</code></pre>

<p></p><p>The commands will generate two folders <em><strong>modulea</strong></em> &amp; <strong><em>moduleb</em></strong>. Subsequently, each folder will contain their own <em>module.ts</em>,<em> routing.ts </em>and <em>component</em> files.</p>
<p></p>

<p></p><p>If you check your <em>app-routing.module.ts</em> you will see the below code for routes.</p>
<p></p>

<pre><code><span class="hljs-keyword">const</span> routes: Routes = [
  { <span class="hljs-attr">path</span>: <span class="hljs-string">'a'</span>, <span class="hljs-attr">loadChildren</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./modulea/modulea.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.ModuleaModule) },
  { <span class="hljs-attr">path</span>: <span class="hljs-string">'b'</span>, <span class="hljs-attr">loadChildren</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./moduleb/moduleb.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.ModulebModule) }
];
</code></pre>

<p></p><p>It implies that when route <em>a</em> or <em>b </em>is visited load their respective modules lazily. </p>
<p></p>

<p></p><p>On running the project with <strong><em>ng serve</em></strong>, you will see the below screen:</p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.18.55-PM.png" alt />Home Page 
<p></p>

<p></p><p>On clicking <em>Load Module A</em> button, you will be routed to page <em>a</em>. This is how your screen should look like.</p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.18.14-PM.png" alt />Lazily loaded Module A
<p></p>

<p></p><p>You should see a similar screen that says <strong><em>moduleb works! </em></strong>when clicked on <em>Load Module B.</em></p>
<p></p>

<p></p><h2>How to Verify that the Lazy Loading Worked</h2>
<p></p>

<p></p><p>In order to verify the files loaded, open the developer tools by pressing F12. After that, visit the <em>Network</em> tab as you can see in the screenshot below. When you refresh the page it will show few files requested.</p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/Network-Tab-1024x601.jpg" alt />Network Tab
<p></p>

<div class="wp-block-spacer"></div>



<div class="wp-block-columns is-style-default">
<div class="wp-block-column">
<p>Go ahead and clear your list of requests by hitting the clear button as shown in the image on the right</p>
</div>



<div class="wp-block-column">
<img src="https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.42.21-PM.png" alt />
</div>
</div>



<p></p><div class="wp-block-spacer"></div>
<p></p>

<p></p><p>When you click on the <em>Load Module A</em>, you will see a request for <em>modulea-modulea-module.js</em> as in the screenshot below. This verifies that the <em>Module A </em>was lazily loaded.</p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.46.50-PM.png" alt />Module A Loaded
<p></p>

<p></p><p>Similarly, when you click <em>Load Module B</em>, the <em>moduleb-moduleb-module.js</em> file is loaded. Hence, verifying that Module B was loaded lazily.</p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.47.10-PM.png" alt />Module B Loaded
<p></p>

<p></p><h2>Use Cases</h2>
<p></p>

<p></p><p>As we have seen, it’s very easy to create lazy loading modules. There are lots of use cases where they are useful, such as</p>
<p></p>

<p></p><ul><li>Creating a separate module for pre-login vs post-login screens.</li><li>For an e-commerce website, vendor facing vs customer facing screens can belong to separate modules. You can also create a separate module for payment.</li><li>A separate CommonModule which contains shared components, directives, or pipelines is usually created. Directives like <em>Copy Code</em> button, components like <em>up vote/down vote </em>are usually included in a common module.</li></ul>
<p></p>

<p></p><h2>Conclusion</h2>
<p></p>

<p></p><p>For smaller websites, it might not matter much that all the modules are loaded at once. But, as the site grows it's very effective to have separate modules which are loaded as needed.</p>
<p></p>

<p></p><p>Because of lazy loading, load time for the websites can be reduced drastically. This is specially helpful when you are trying to rank higher in SEO's. Even if not, less loading times means better user experience.</p>
<p></p>

<p></p><p>Are you interested in more articles? Check these out:</p>
<p></p>

<p></p><ul><li><a href="https://arjavdave.com/2021/04/14/learn-test-driven-development-with-integration-tests-in-net-5-0/">Learn TDD with Integration Tests in .NET</a></li><li><a href="https://arjavdave.com/2021/03/31/net-5-setup-authentication-and-authorisation/">How to authenticate &amp; authorise API’s correctly in .NET</a></li><li><a href="https://arjavdave.com/2021/03/22/azure-functions-wkhtmltopdf/">Azure Functions &amp; wkhtmltopdf: Convert HTML to PDF</a></li></ul>
<p></p>
]]></content:encoded></item><item><title><![CDATA[5 Ways to Increase Your Efficiency as a Developer]]></title><description><![CDATA[Like any other field, the efficiency of a developer varies based on several factors: work culture, management, personal life, skills, and so on. Some we can control, and some we cannot.


In this article, we will discuss how to make oneself better by...]]></description><link>https://blog.royalecheese.com/5-ways-to-increase-your-efficiency-as-a-developer</link><guid isPermaLink="true">https://blog.royalecheese.com/5-ways-to-increase-your-efficiency-as-a-developer</guid><category><![CDATA[Productivity]]></category><category><![CDATA[Career]]></category><category><![CDATA[software development]]></category><category><![CDATA[Developer]]></category><category><![CDATA[programmer]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Thu, 22 Apr 2021 13:40:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619098724835/ncv41PVlS.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[
<p></p><p>Like any other field, the efficiency of a developer varies based on several factors: work culture, management, personal life, skills, and so on. Some we can control, and some we cannot.</p>
<p></p>

<p></p><p>In this article, we will discuss how to make oneself better by working on things we control. There are some clichéd tips like <em>Stay Focused, Avoid Distractions </em>and<em> Be in the FLOW</em>.</p>
<p></p>

<p></p><p>But we are going to get a bit more technical on increasing the efficiency.</p>
<p></p>

<p></p><h2>Journey of a Software Developer</h2>
<p></p>

<p></p><p>The usual stages of a developer are shown below:</p>
<p></p>

<p></p><blockquote><p>Coder -&gt; Programmer -&gt; Architect</p></blockquote>
<p></p>

<p></p><p><strong>Coder</strong></p>
<p></p>

<p></p><p>Many people use the words “coder” and “programmer” interchangeably. But, there is a difference between the two. A coder is someone who knows how to write the code or a script. Optimisation and architecture are not their priority. Rather, they focus on writing code that just works.</p>
<p></p>

<p></p><p><strong>Programmer</strong></p>
<p></p>

<p></p><p>A programmer is a superset of a coder. They know how to write code, but in addition, they also write it with great optimisation and robustness. Secondly, time and memory complexities are kept in mind when writing code.</p>
<p></p>

<p></p><p><strong>Architect</strong></p>
<p></p>

<p></p><p>As you might have guessed, the architect or the software architect, to be precise, is a superset of a programmer. The architect is responsible for not only a piece of code but also how these pieces fit together.</p>
<p></p>

<p></p><h2>Coder vs. Programmer vs. Architect: An Example</h2>
<p></p>

<p></p><p>A great example would be to ask these three people to “Create tic-tac-toe.”</p>
<p></p>

<p></p><p>When asking the coders, they will start coding, assuming it's a 3x3 tic-tac-toe with two players. Secondly, they might write nested <em>for </em>loops as a brute force approach to figure out who won the match. There might also be no design patterns in place like MVC, MVVM, or others.</p>
<p></p>

<p></p><p>However, the programmer will first understand the requirements completely and then start on the coding part. They will create optimised algorithms for maintaining the state of the players and a scalable way to change the grid dimensions.</p>
<p></p>

<p></p><p>The architect will select which technology is best for designing the game. A few other questions come into play: Is it going to be a web-based or mobile app? Will an API be required or just played locally? In addition, they will also decide on which design patterns to implement. Once the architecture is defined, only then will the actual implementation start.</p>
<p></p>

<p></p><blockquote><p>The goal of each developer should be to become a great architect along with having great technical skills.</p></blockquote>
<p></p>

<p></p><p>Here are a few ways to improve your efficiency and skills:</p>
<p></p>

<p></p><h2>1. Pen and Paper</h2>
<p></p>

<p></p><p>Many people find it offensive to give a pen and paper coding interview. In my opinion, it is one of the best tools to get clarity. Jot down the things that you want to achieve and how you will achieve them.</p>
<p></p>

<p></p><p>In the tic-tac-toe example above, you can write down that there will be models like Player and Board. Secondly, a game engine will be required to calculate the result after each move. Also, write down what each class will contain, e.g., Player will have a name and symbol property.</p>
<p></p>

<p></p><p>You get the idea. Figure out your design patterns. What you are trying to achieve in code, achieve it with pen and paper first.</p>
<p></p>

<p></p><h2>2. Become Your Own Manager</h2>
<p></p>

<p></p><p>At the start of the day, create a TODO list of your tasks, e.g., you will complete the game engine today, or you will complete the board UI today, etc.</p>
<p></p>

<p></p><p>Try to break it down into smaller tasks of 3–4 hours each. Having clear goals for the day keeps you motivated. Secondly, it gives a sense of satisfaction when the tasks are completed; it boosts morale.</p>
<p></p>

<p></p><p>Essentially, it’s a variant of the <a href="https://www.atlassian.com/agile/kanban#:~:text=Kanban%20is%20a%20popular%20framework,of%20work%20at%20any%20time.">Kanban</a> management style. But, I have found it to be quite effective.</p>
<p></p>

<p></p><h2>3. Documentation</h2>
<p></p>

<p></p><img src="https://miro.medium.com/max/1400/0*ic31iXHxVYtLUjWo.png" alt /><em><span>Reference: https://www.reddit.com/r/ProgrammerHumor/comments/ijoxq6/why_read_documentation/</span></em>
<p></p>

<p></p><p>One of the reasons for taking time to get things right is a lack of knowledge.</p>
<p></p>

<p></p><p>Astonishingly, there are many developers who just assume how the system or library works. They spend an enormous amount of time figuring out by themselves how it works without reading the documentation. This leads to a loss in efficiency.</p>
<p></p>

<p></p><p>A simple example I personally encountered was writing blogs using the Markdown syntax. I know most of the basic syntax, but I was unaware of some of the advanced ones. I wasted around 5–10 minutes trying to figure it out. If I had gone to the documentation, instead, it would have saved me time.</p>
<p></p>

<p></p><p>Here is a <a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet">Markdown</a> cheat sheet in case you are wondering.</p>
<p></p>

<p></p><h2>4. Don’t Reinvent the Wheel</h2>
<p></p>

<p></p><p>If something is already available in the market, with great reliability, use it. Usually speaking the pros will outweigh the cons.</p>
<p></p>

<p></p><p>As an example, you should never write your own cryptographic function or library. There are pretty decent libraries available in almost all languages, and they will save you lots of time. More importantly, it will have the correct implementation, and it will be well optimised</p>
<p></p>

<p></p><p>But don’t overdo it. You shouldn’t import <a href="https://underscorejs.org/">underscore</a> or <a href="https://lodash.com/">lodash</a> just to loop through an array. It only increases your package size and hurts the user experience.</p>
<p></p>

<p></p><h2>5. Testing</h2>
<p></p>

<p></p><p>Test-Driven Development (TDD) is another methodology that is going strong. Writing tests is as important as writing code. Though manual testing is required, automated tests provide confidence that your system will not break logically.</p>
<p></p>

<p></p><p>Initially, it might feel that you are spending more time writing tests. But as the project grows, it would be worth it. Fewer bugs and more robustness.</p>
<p></p>

<p></p><p>Here is an article on <a href="https://arjavdave.com/2021/04/14/learn-test-driven-development-with-integration-tests-in-net-5-0/">how to do TDD</a> in .NET Core.</p>
<p></p>

<p></p><h2>Conclusion</h2>
<p></p>

<p></p><p>I, personally, felt a lot of different emotions when following the above tips, but they have improved my efficiency a lot, especially creating a to-do list for the day.</p>
<p></p>

<p></p><p>Read more interesting articles:</p>
<p></p>

<p></p><ul><li><a href="https://arjavdave.com/2021/04/19/a-frustrated-developers-review-of-xcode-android-studio/">XCode or Android Studio: Which is Better?</a></li><li><a href="https://arjavdave.com/2021/03/11/continuous-integration-cicd-for-ios-on-azure-devops-part-1/">Setup CI-CD for iOS</a></li><li><a href="https://arjavdave.com/2021/03/16/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops/">Setup CI-CD for Android</a></li><li><a href="https://arjavdave.com/2021/04/14/learn-test-driven-development-with-integration-tests-in-net-5-0/">Learning Integration Tests in .NET with .TDD</a></li></ul>
<p></p>
]]></content:encoded></item><item><title><![CDATA[XCode vs Android Studio: Which is better?]]></title><description><![CDATA[What makes me eligible to review!


I have been in the industry for more than 11+ years now. I started my career with BlackBerry (BB) Development. I feel old already!


Eclipse was our best friend back then for mobile development. I did my internship...]]></description><link>https://blog.royalecheese.com/xcode-vs-android-studio-which-is-better</link><guid isPermaLink="true">https://blog.royalecheese.com/xcode-vs-android-studio-which-is-better</guid><category><![CDATA[iOS]]></category><category><![CDATA[Xcode]]></category><category><![CDATA[Android]]></category><category><![CDATA[Android Studio]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Tue, 20 Apr 2021 05:35:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618855597947/FfVwYg11V.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[
<p></p><h2>What makes me eligible to review!</h2>
<p></p>

<p></p><p>I have been in the industry for more than 11+ years now. I started my career with BlackBerry (BB) Development. I feel old already!</p>
<p></p>

<p></p><p><strong><em>Eclipse</em></strong> was our best friend back then for mobile development. I did my internship working on the famous <a href="https://zagat.com/" target="_blank">Zagat</a> app for BlackBerry. It was overall a great learning experience.</p>
<p></p>

<p></p><p>For my full-time job I switched to a start up named <a href="https://www.linkedin.com/company/spinlet/" target="_blank">Spinlet</a> which I hope is still going strong. I worked as BlackBerry developer in the beginning but switched to iOS development eventually.</p>
<p></p>

<p></p><p>My iOS experience was nothing but exciting in those initial years. Eventually I started my own firm <a href="https://royalecheese.com" target="_blank">Royale Cheese</a> with a friend that provides mobile design &amp; development. </p>
<p></p>

<p></p><p>After around 4 years of iOS development I found my way into Android development. I have got a fair share of experience with Android development as well, around 3 years to be precise.</p>
<p></p>

<p></p><p>We have since then been working on full stack. That's when I realised how horrible the mobile development tools are.</p>
<p></p>

<p></p><p>Enough with the chit chat. Here's an honest review of the mobile development tools and technologies. </p>
<p></p>

<p></p><h2>XCode</h2>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/xcode.png" alt />
<p></p>

<p></p><p>It used to be a good tool in the past. But it has become terrible lately. Here is a list of all the issues even after 15 years:</p>
<p></p>

<p></p><ul><li><strong>Auto-completion</strong>: Firstly, who in the right mind would set Esc as the suggestions key. Secondly, the autocompletion doesn't work many a times or gives weird suggestions that are out of context. </li><li><strong>Build Times</strong>: It takes a lot of time to create an archive or to run on a device for the first time. It's best to <a href="https://arjavdave.com/2021/03/11/continuous-integration-cicd-for-ios-on-azure-devops-part-1/" target="_blank">setup a CI/CD</a> to archive and upload builds. </li><li><strong>Signing &amp; Deployment:</strong> With the latest version's it's getting easier. But, it's still confusing with the signing certificates and the provision profiles for someone who is a beginner.</li><li><strong>Memory Hog</strong>: Somehow XCode keeps on hogging memory. For every new device on which the build needs to run it occupies 3 GB. Archives take a huge chunk and so as the simulators. Overall it occupies around 50GB if I don't clean up regularly.</li><li><strong>Updates</strong>: Each update is around 10-12GB even the minor upgrades. What's worse is it requires more than 40GB of free space to get installed. Last but not the least, XCode takes around 12GB of the space.</li><li><strong>Design</strong>: Initially to design UI there was struts &amp; springs, then came the Autolayout and now the SwiftUI. It is worrisome that the methodology keeps on changing.</li><li><strong>Cocoapods</strong>: is getting worse day by day because it's repo is getting so big. Secondly, it increases the build times by a lot.</li><li><strong>Camera</strong>: Possibly due to hardware limitations camera was not supported in simulators. But, now it's been a while. If the location can be simulated why not the Camera?</li></ul>
<p></p>

<p></p><h2>Android Studio</h2>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/android-studio.png" alt />
<p></p>

<p></p><p>I haven't got the chance to use it in the early days. But from what I have been seeing it wouldn't have been pretty. Here are some of the frustrating issues:</p>
<p></p>

<p></p><ul><li><strong>Fragmentation</strong>: I feel this might be on top of every ones list. Supporting the staggering amount of devices to support might just overwhelm anyone.</li><li><strong>Gradle</strong>: Oh My God! Gradle takes forever to run builds. There are optimisation's which can help alleviate the problem, but it still remains the problem.</li><li><strong>RAM Hogger</strong>: With emulators and IDE running together, the combo requires around 10-12GB of RAM. That's way more than what their official documentation says: <strong><em>4GB</em></strong>.</li><li><strong>Signing Keys</strong>: You lose your signing keys and you can't upload to the same app again. You will have to create a new app and get the reviews and downloads again. There is some improvement in this area recently.</li><li><strong>IDE: </strong>I always felt Java based IDE's to be clumsy. That includes IntelliJ (on which Android Studio is based), Eclipse or NetBeans. It's responsiveness is not like other tools like XCode or VS Code.</li></ul>
<p></p>

<p></p><h2>XCode vs Android Studio Review</h2>
<p></p>

<p></p><p>Even though it might feel Android has less issues it does have some serious issues. <strong>Fragmentation</strong> and <strong>Gradle</strong> might alone be enough to make Android look bad. </p>
<p></p>

<p></p><p>Personally I prefer to work in XCode compared to Android since I own a Mac and probably I am more used to it. </p>
<p></p>

<p></p><h2>Conclusion</h2>
<p></p>

<p></p><p>Overall both platforms have a huge user base. But, I feel that these tools still have a long way to go. </p>
<p></p>

<p></p><p>As an example I am absolutely in love how Microsoft has revamped dotnet to <a href="https://dotnet.microsoft.com/download" target="_blank">dotnet core</a> and their IDE to <a href="https://code.visualstudio.com/" target="_blank">VS Code</a>. </p>
<p></p>

<p></p><p>Jot down your frustrations in the comments below. </p>
<p></p>

<p></p><p>Read some of my articles here:</p>
<p></p>

<p></p><ul><li><a href="https://arjavdave.com/2021/03/11/continuous-integration-cicd-for-ios-on-azure-devops-part-1/" target="_blank">Setup CI-CD for iOS</a></li><li><a href="https://arjavdave.com/2021/03/16/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops/" target="_blank">Setup CI-CD for Android</a></li><li><a href="https://arjavdave.com/2021/04/14/learn-test-driven-development-with-integration-tests-in-net-5-0/" target="_blank">Learning Integration Tests in .NET with .TDD</a></li><li><a href="https://arjavdave.com/2021/04/05/going-password-less-with-dotnet/" target="_blank">Provide a password-less to login</a></li></ul>
<p></p>
]]></content:encoded></item><item><title><![CDATA[Learn Test-Driven Development with Integration Tests in .NET 5.0]]></title><description><![CDATA[TDD (Test Driven Development) is a much debated word in the tech industry. Debates like Whether you should do TDD or not? or How advantageous is it?  are quite popular. Simply said, TDD is test before you develop. 


Now, there are a lot of school of...]]></description><link>https://blog.royalecheese.com/learn-test-driven-development-with-integration-tests-in-net-50</link><guid isPermaLink="true">https://blog.royalecheese.com/learn-test-driven-development-with-integration-tests-in-net-50</guid><category><![CDATA[TDD (Test-driven development)]]></category><category><![CDATA[Testing]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[dotnet]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Thu, 15 Apr 2021 08:08:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618474042605/rzsAJx4jx.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[
<p></p><p>TDD (Test Driven Development) is a much debated word in the tech industry. Debates like <em>Whether you should do TDD or not?</em> or <em>How advantageous is it?</em>  are quite popular. Simply said, TDD is test before you develop. </p>
<p></p>

<p></p><p>Now, there are a lot of school of thoughts regarding what type of test's are included and what are not in TDD. As an example, should it include Unit Test, Integration Test, System Test or even UAT? </p>
<p></p>

<p></p><p>In this article, we will go through a real-world example on how to write integration tests in .NET 5.0 with TDD methodology.</p>
<p></p>

<p></p><h2>Project Requirements</h2>
<p></p>

<p></p><p>TDD requires a very clear understanding of scope of work. Without clarity, all the test cases might not be covered.</p>
<p></p>

<p></p><p>Let's define the scope of work. We will be developing a <em>patient admission system</em> for a Hospital.</p>
<p></p>

<p></p><h4>Business Requirements</h4>
<p></p>

<p></p><ul><li>A hospital has X ICU rooms, Y Premium rooms &amp; Z General rooms.</li><li>ICU &amp; Premium rooms can have a single patient at a time, while General rooms can have 2 patients. Each room has a room number.</li><li>On admitting, the patient has to provide name, age, gender &amp; phone number. </li><li>It is possible to search a patient via name or phone number.</li><li>Same patient cannot be admitted to multiple beds while he is still checked in.</li><li>A patient cannot be admitted if all the rooms are occupied.</li></ul>
<p></p>

<p></p><h4>Model Validation Rules</h4>
<p></p>

<p></p><p>Based on the above requirements, there are 2 models namely Patient &amp; Room. </p>
<p></p>

<p></p><ul><li>A patient's age is between 0 &amp; 150. The length of name should be between 2 and 40. Gender can be male, female &amp; other. Phone Number's length should be between 7 and 12 and it should all be digits. </li><li>Room type can be either "ICU", "Premium" or "General".</li></ul>
<p></p>

<p></p><h4>Test Cases</h4>
<p></p>

<p></p><p>Now, that we have defined rules &amp; requirements, lets start creating test cases. Since it's a basic CRUD application we mostly have integration tests. </p>
<p></p>

<p></p><h6>Patient</h6>
<p></p>

<p></p><ul><li>Do all the model validation tests.</li><li>Admit the same patient twice</li><li>Check out the same patient twice. </li><li>Admit the same patient to multiple rooms at the same time.</li><li>Search a patient with phone number and name.</li></ul>
<p></p>

<p></p><h2>TDD Setup</h2>
<p></p>

<p></p><p>In the above section we gathered requirements. Secondly, we defined the models. Finally, we created the list of test cases which we will implement. </p>
<p></p>

<p></p><p>Open your terminal and run the below script to create and setup a new project.</p>
<p></p>
<pre><code>mkdir TDD
cd TDD
dotnet <span class="hljs-built_in">new</span> sln
dotnet <span class="hljs-built_in">new</span> webapi <span class="hljs-comment">--name TDD</span>
dotnet <span class="hljs-built_in">new</span> xunit <span class="hljs-comment">--name TDD.Tests</span>
cd TDD
dotnet <span class="hljs-keyword">add</span> package Microsoft.EntityFrameworkCore <span class="hljs-comment">--version 5.0.5</span>
cd ../TDD.Tests
dotnet <span class="hljs-keyword">add</span> reference ../TDD/TDD.csproj
dotnet <span class="hljs-keyword">add</span> package Microsoft.EntityFrameworkCore <span class="hljs-comment">--version 5.0.5</span>
dotnet <span class="hljs-keyword">add</span> package Microsoft.AspNetCore.Hosting <span class="hljs-comment">--version 2.2.7</span>
dotnet <span class="hljs-keyword">add</span> package Microsoft.AspNetCore.Mvc.Testing <span class="hljs-comment">--version 5.0.5</span>
dotnet <span class="hljs-keyword">add</span> package Microsoft.EntityFrameworkCore.InMemory <span class="hljs-comment">--version 5.0.5</span>
cd ..
dotnet sln <span class="hljs-keyword">add</span> TDD/TDD.csproj
dotnet sln <span class="hljs-keyword">add</span> TDD.Tests/TDD.Tests.csproj
code .
</code></pre>
<p></p><p>The above script creates a solution file named <em>TDD.sln</em>. Secondly, we create 2 projects for TDD &amp; TDD.Tests. Then we add the dependencies for each project. Lastly, we add the projects to the solution and open the project in VS Code. </p>
<p></p>

<p></p><p>Before we start testing, some more setup is required. Basically, integration tests test the a specific module without mocking. So we will be mimicking our application via TestServer.</p>
<p></p>

<p></p><h4>Custom WAF</h4>
<p></p>

<p></p><p>In order to mimic the TestServer there is a class called <a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing.webapplicationfactory-1?view=aspnetcore-5.0" target="_blank">WebApplicationFactory</a> (WAF) which bootstraps the application in memory.</p>
<p></p>

<p></p><p>In your <strong><em>TDD.Tests</em></strong> project create a file named <em>PatientTestsDbWAF.cs</em> with the following code.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> System.Linq;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Hosting;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc.Testing;
<span class="hljs-keyword">using</span> Microsoft.Extensions.DependencyInjection;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore;


<span class="hljs-keyword">namespace</span> TDD.Tests
{
    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatientTestsDbWAF</span>&amp;<span class="hljs-title">lt</span>;</span>TStartup&amp;gt; : WebApplicationFactory&amp;lt;TStartup&amp;gt; where TStartup : <span class="hljs-class"><span class="hljs-keyword">class</span>
    {</span>

        <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> IWebHostBuilder <span class="hljs-title">CreateWebHostBuilder</span><span class="hljs-params">()</span>
        </span>{
            <span class="hljs-keyword">return</span> WebHost.CreateDefaultBuilder()
                .UseStartup&amp;lt;TStartup&amp;gt;();
        }
        <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureWebHost</span><span class="hljs-params">(IWebHostBuilder builder)</span>
        </span>{
            builder.ConfigureServices(async services =&amp;gt;
           {
               <span class="hljs-comment">// Remove the app's DbContext registration.</span>
               var descriptor = services.SingleOrDefault(
                      d =&amp;gt; d.ServiceType ==
                          typeof(DbContextOptions&amp;lt;DataContext&amp;gt;));

               <span class="hljs-keyword">if</span> (descriptor != null)
               {
                   services.Remove(descriptor);
               }

               <span class="hljs-comment">// Add DbContext using an in-memory database for testing.</span>
               services.AddDbContext&amp;lt;DataContext&amp;gt;(options =&amp;gt;
                  {
                      <span class="hljs-comment">// Use in memory db to not interfere with the original db.</span>
                      options.UseInMemoryDatabase(<span class="hljs-string">"PatientTestsTDD.db"</span>);
                  });
           });
        }
    }
}
</code></pre>
<p></p><p>We are removing the applications DbContext and adding an <strong>in memory</strong> DbContext. It is a necessary step since we don't want to interfere with the original database. </p>
<p></p>

<p></p><p>Secondly, we are initialising the database with some dummy data. </p>
<p></p>

<p></p><p>Since, DataContext is a custom class, it will give compiler error. So, we need to create it.</p>
<p></p>

<p></p><h4>Data Context</h4>
<p></p>

<p></p><p>Therefore, in your <strong>TDD project</strong>, create a file named <em>DataContext.cs</em> with the following code.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">TDD</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DataContext</span> : <span class="hljs-title">DbContext</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DataContext</span>(<span class="hljs-params">DbContextOptions options</span>) : <span class="hljs-title">base</span>(<span class="hljs-params">options</span>)</span> { }

        <span class="hljs-comment">// For storing the list of patients and their state</span>
        <span class="hljs-keyword">public</span> DbSet&amp;lt;Patient&amp;gt; Patient { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-comment">// For the storying the rooms along with their types and capacity</span>
        <span class="hljs-keyword">public</span> DbSet&amp;lt;Room&amp;gt; Room { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-comment">// For logging which patients are currently admitted to which room</span>
        <span class="hljs-keyword">public</span> DbSet&amp;lt;RoomPatient&amp;gt; RoomPatient { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

    }
}
</code></pre>
<p></p><p>Here Patient, Room &amp; RoomPatient are Entity classes with the required properties, which we will create next.</p>
<p></p>

<p></p><h4>Patient</h4>
<p></p>

<p></p><p>Again, in your <strong>TDD project</strong>, create a file named <em>Patient.cs</em> and paste in the code below.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> <span class="hljs-keyword">System</span>;
<span class="hljs-keyword">using</span> <span class="hljs-keyword">System</span>.ComponentModel.DataAnnotations;
<span class="hljs-keyword">using</span> <span class="hljs-keyword">System</span>.ComponentModel.DataAnnotations.<span class="hljs-keyword">Schema</span>;

namespace TDD
{
    <span class="hljs-built_in">public</span> <span class="hljs-keyword">class</span> Patient
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.<span class="hljs-keyword">Identity</span>)]
        <span class="hljs-built_in">public</span> <span class="hljs-type">int</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-built_in">public</span> String <span class="hljs-type">Name</span> { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-built_in">public</span> String PhoneNumber { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-built_in">public</span> <span class="hljs-type">int</span> Age { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-built_in">public</span> String Gender { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre>
<p></p><h4>Room</h4>
<p></p>

<p></p><p>Create another file named <em>Room.cs</em> with the following code.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> <span class="hljs-keyword">System</span>;
<span class="hljs-keyword">using</span> <span class="hljs-keyword">System</span>.ComponentModel.DataAnnotations;
<span class="hljs-keyword">using</span> <span class="hljs-keyword">System</span>.ComponentModel.DataAnnotations.<span class="hljs-keyword">Schema</span>;

namespace TDD
{
    <span class="hljs-built_in">public</span> <span class="hljs-keyword">class</span> Room
    {
          [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.<span class="hljs-keyword">Identity</span>)]
        <span class="hljs-built_in">public</span> <span class="hljs-type">int</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-built_in">public</span> String RoomType { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-built_in">public</span> <span class="hljs-type">int</span> CurrentCapacity { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-built_in">public</span> <span class="hljs-type">int</span> MaxCapacity { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre>
<p></p><h4>RoomPatient</h4>
<p></p>

<p></p><p>Create the last model file <em>RoomPatient.cs</em> with the following code.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> System.ComponentModel.DataAnnotations;
<span class="hljs-keyword">using</span> System.ComponentModel.DataAnnotations.Schema;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">TDD</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">RoomPatient</span>
    {
        [<span class="hljs-meta">Key</span>]
        [<span class="hljs-meta">DatabaseGenerated(DatabaseGeneratedOption.Identity)</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">Required</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> RoomId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">ForeignKey(<span class="hljs-meta-string">"RoomId"</span>)</span>]
        <span class="hljs-keyword">public</span> Room Room { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">Required</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> PatientId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">ForeignKey(<span class="hljs-meta-string">"PatientId"</span>)</span>]
        <span class="hljs-keyword">public</span> Patient Patient { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre>
<p></p><p>Now you shouldn't be getting any compiler error.</p>
<p></p>

<p></p><p>Lastly, remove the WeatherForecast.cs and WeatherForecastController.cs files. </p>
<p></p>

<p></p><p>Go to your terminal in VS Code and run the below command.</p>
<p></p>
<pre><code><span class="hljs-built_in">cd</span> TDD.Tests
dotnet <span class="hljs-built_in">test</span>
</code></pre>
<p></p><p>You will see a nice green result which says 1 test passed.</p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/Test_Success_1-1024x211.png" alt />Test Success
<p></p>

<p></p><h4>Patient Controller</h4>
<p></p>

<p></p><p>Unfortunately dotnet doesn't provide a way to directly test the model's in itself. So, we will have to create a controller to test it. </p>
<p></p>

<p></p><p>Go ahead and create <em>PatientController.cs</em> in the Controllers folder in <strong>TDD project</strong> with the below code.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">TDD.Controllers</span>
{
    [<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
    [<span class="hljs-meta">ApiController</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">PatientController</span> : <span class="hljs-title">Controller</span>
    {
        [<span class="hljs-meta">HttpPost</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">AddPatient</span>(<span class="hljs-params">[FromBody] Patient Patient</span>)</span>
        {
            <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Insert the patient into db</span>
            <span class="hljs-keyword">return</span> Created(<span class="hljs-string">"/patient/1"</span>, Patient);
        }
    }
}
</code></pre>
<p></p><p>We created an api to add a patient. In order to test our model we will call this api.</p>
<p></p>

<p></p><p>That is all the things required to start testing.</p>
<p></p>

<p></p><h2>Model Validation Tests</h2>
<p></p>

<p></p><p>Since, we have setup the basic code for testing, let's write a test that fails. We will start our testing with the model validation tests.</p>
<p></p>

<p></p><h4>Failing (Red) State</h4>
<p></p>

<p></p><p>Let's create a new file named <em>PatientTests.cs</em> in your <strong>TDD.Tests project</strong> and delete the file named <em>UnitTest1.cs</em>. Copy the below code in your file.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> System.Net;
<span class="hljs-keyword">using</span> System.Net.Http;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Xunit;
<span class="hljs-keyword">using</span> System.Text;
<span class="hljs-keyword">using</span> System.Text.Json;
<span class="hljs-keyword">using</span> Microsoft.Extensions.DependencyInjection;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc.Testing;
<span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> TDD.Tests
{
    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatientTests</span> :</span> IClassFixture&amp;lt;PatientTestsDbWAF&amp;lt;Startup&amp;gt;&amp;gt;
    {
        <span class="hljs-comment">// HttpClient to call our api's</span>
        <span class="hljs-keyword">private</span> readonly HttpClient httpClient;
        <span class="hljs-keyword">public</span> WebApplicationFactory&amp;lt;Startup&amp;gt; _factory;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PatientTests</span><span class="hljs-params">(PatientTestsDbWAF&amp;lt;Startup&amp;gt; factory)</span>
        </span>{
            _factory = factory;

            <span class="hljs-comment">// Initiate the HttpClient</span>
            httpClient = _factory.CreateClient();
        }

        [Theory]
        [InlineData(<span class="hljs-string">"Test Name 2"</span>, <span class="hljs-string">"1234567891"</span>, <span class="hljs-number">20</span>, <span class="hljs-string">"Male"</span>, HttpStatusCode.Created)]
        [InlineData(<span class="hljs-string">"T"</span>, <span class="hljs-string">"1234567891"</span>, <span class="hljs-number">20</span>, <span class="hljs-string">"Male"</span>, HttpStatusCode.BadRequest)]
        [InlineData(<span class="hljs-string">"A very very very very very very loooooooooong name"</span>, <span class="hljs-string">"1234567891"</span>, <span class="hljs-number">20</span>, <span class="hljs-string">"Male"</span>, HttpStatusCode.BadRequest)]
        [InlineData(null, <span class="hljs-string">"1234567890"</span>, <span class="hljs-number">20</span>, <span class="hljs-string">"Invalid Gender"</span>, HttpStatusCode.BadRequest)]
        [InlineData(<span class="hljs-string">"Test Name"</span>, <span class="hljs-string">"InvalidNumber"</span>, <span class="hljs-number">20</span>, <span class="hljs-string">"Male"</span>, HttpStatusCode.BadRequest)]
        [InlineData(<span class="hljs-string">"Test Name"</span>, <span class="hljs-string">"1234567890"</span>, <span class="hljs-number">-10</span>, <span class="hljs-string">"Male"</span>, HttpStatusCode.BadRequest)]
        [InlineData(<span class="hljs-string">"Test Name"</span>, <span class="hljs-string">"1234567890"</span>, <span class="hljs-number">20</span>, <span class="hljs-string">"Invalid Gender"</span>, HttpStatusCode.BadRequest)]
        [InlineData(<span class="hljs-string">"Test Name"</span>, <span class="hljs-string">"12345678901234444"</span>, <span class="hljs-number">20</span>, <span class="hljs-string">"Invalid Gender"</span>, HttpStatusCode.BadRequest)]
        <span class="hljs-function"><span class="hljs-keyword">public</span> async Task <span class="hljs-title">PatientTestsAsync</span><span class="hljs-params">(String Name, String PhoneNumber, <span class="hljs-keyword">int</span> Age, String Gender, HttpStatusCode ResponseCode)</span>
        </span>{
            var scopeFactory = _factory.Services;
            <span class="hljs-keyword">using</span> (var scope = scopeFactory.CreateScope())
            {
                var context = scope.ServiceProvider.GetService&amp;lt;DataContext&amp;gt;();

                <span class="hljs-comment">// Initialize the database, so that </span>
                <span class="hljs-comment">// changes made by other tests are reset. </span>
                await DBUtilities.InitializeDbForTestsAsync(context);

                <span class="hljs-comment">// Arrange</span>
                var request = <span class="hljs-keyword">new</span> HttpRequestMessage(HttpMethod.Post, <span class="hljs-string">"api/patient"</span>);

                request.Content = <span class="hljs-keyword">new</span> StringContent(JsonSerializer.Serialize(<span class="hljs-keyword">new</span> Patient
                {
                    Name = Name,
                    PhoneNumber = PhoneNumber,
                    Age = Age,
                    Gender = Gender
                }), Encoding.UTF8, <span class="hljs-string">"application/json"</span>);

                <span class="hljs-comment">// Act</span>
                var response = await httpClient.SendAsync(request);

                <span class="hljs-comment">// Assert</span>
                var StatusCode = response.StatusCode;
                Assert.Equal(ResponseCode, StatusCode);
            }
        }
    }
}
</code></pre>
<p></p><p>[Theory] attribute allows us to mention different parameters for our tests. Consequently, we don't have to write different tests for all the combinations.</p>
<p></p>

<p></p><p>Also, DBUtilities is a utility class to reinitialise the database to it's initial state. This might seem trivial when we have 1 or 2 tests but, gets critical as we add more tests. </p>
<p></p>

<p></p><h4>DBUtilities</h4>
<p></p>

<p></p><p>The DBUtilities class will initialise your database with 1 patient and 3 different type of rooms. </p>
<p></p>

<p></p><p>Create a file named <em>DBUtilities.cs</em> in your <strong><em>TDD.Tests</em></strong> project with the below code.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> System.Threading.Tasks;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">TDD.Tests</span>
{
    <span class="hljs-comment">// Helps to initialise the database either from the WAF for the first time</span>
    <span class="hljs-comment">// Or before running each test.</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DBUtilities</span>
    {

        <span class="hljs-comment">// Clears the database and then,</span>
        <span class="hljs-comment">//Adds 1 Patient and 3 different types of rooms to the database</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeDbForTestsAsync</span>(<span class="hljs-params">DataContext context</span>)</span>
        {
            context.RoomPatient.RemoveRange(context.RoomPatient);
            context.Patient.RemoveRange(context.Patient);
            context.Room.RemoveRange(context.Room);

            <span class="hljs-comment">// Arrange</span>
            <span class="hljs-keyword">var</span> Patient = <span class="hljs-keyword">new</span> Patient
            {
                Name = <span class="hljs-string">"Test Patient"</span>,
                PhoneNumber = <span class="hljs-string">"1234567890"</span>,
                Age = <span class="hljs-number">20</span>,
                Gender = <span class="hljs-string">"Male"</span>
            };
            context.Patient.Add(Patient);

            <span class="hljs-keyword">var</span> ICURoom = <span class="hljs-keyword">new</span> Room
            {
                RoomType = <span class="hljs-string">"ICU"</span>,
                MaxCapacity = <span class="hljs-number">1</span>,
                CurrentCapacity = <span class="hljs-number">1</span>
            };
            context.Room.Add(ICURoom);

            <span class="hljs-keyword">var</span> GeneralRoom = <span class="hljs-keyword">new</span> Room
            {
                RoomType = <span class="hljs-string">"General"</span>,
                MaxCapacity = <span class="hljs-number">2</span>,
                CurrentCapacity = <span class="hljs-number">2</span>
            };
            context.Room.Add(GeneralRoom);

            <span class="hljs-keyword">var</span> PremiumRoom = <span class="hljs-keyword">new</span> Room
            {
                RoomType = <span class="hljs-string">"Premium"</span>,
                MaxCapacity = <span class="hljs-number">1</span>,
                CurrentCapacity = <span class="hljs-number">1</span>
            };
            context.Room.Add(PremiumRoom);

            <span class="hljs-keyword">await</span> context.SaveChangesAsync();
        }
    }
}
</code></pre>
<p></p><p>Go ahead and run the <em>dotnet test</em> command again and you will see 1 passed and 4 failed tests. This is because the 4 tests were expecting BadRequest but getting a Created result. </p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/failed_tests-1024x195.png" alt />Failing (Red) State
<p></p>

<p></p><p>Let's fix it!</p>
<p></p>

<p></p><h4>Success (Green) State</h4>
<p></p>

<p></p><p>In order to fix these we need to add attributes to our <em>Patient.cs</em> class.</p>
<p></p>

<p></p><p>Update the <em>Patient.cs</em> file as below.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.ComponentModel.DataAnnotations;
<span class="hljs-keyword">using</span> System.ComponentModel.DataAnnotations.Schema;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">TDD</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Patient</span> : <span class="hljs-title">IValidatableObject</span>
    {
        [<span class="hljs-meta">Key</span>]
        [<span class="hljs-meta">DatabaseGenerated(DatabaseGeneratedOption.Identity)</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">Required</span>]
        [<span class="hljs-meta">StringLength(40, MinimumLength = 2, ErrorMessage = <span class="hljs-meta-string">"The name should be between 2 &amp;amp; 40 characters."</span>)</span>]
        <span class="hljs-keyword">public</span> String Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">Required</span>]
        [<span class="hljs-meta">DataType(DataType.PhoneNumber)</span>]
        [<span class="hljs-meta">RegularExpression(@<span class="hljs-meta-string">"^(\d{7,12})$"</span>, ErrorMessage = <span class="hljs-meta-string">"Not a valid phone number"</span>)</span>]
        <span class="hljs-keyword">public</span> String PhoneNumber { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">Required</span>]
        [<span class="hljs-meta">Range(1, 150)</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Age { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        [<span class="hljs-meta">Required</span>]
        <span class="hljs-keyword">public</span> String Gender { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-keyword">public</span> Boolean IsAdmitted { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-keyword">public</span> IEnumerable&amp;lt;ValidationResult&amp;gt; Validate(ValidationContext validationContext)
        {
            <span class="hljs-comment">// Only Male, Female or Other gender are allowed</span>
            <span class="hljs-keyword">if</span> (Gender.Equals(<span class="hljs-string">"Male"</span>, System.StringComparison.CurrentCultureIgnoreCase) == <span class="hljs-literal">false</span> &amp;amp;&amp;amp;
                Gender.Equals(<span class="hljs-string">"Female"</span>, System.StringComparison.CurrentCultureIgnoreCase) == <span class="hljs-literal">false</span> &amp;amp;&amp;amp;
                Gender.Equals(<span class="hljs-string">"Other"</span>, System.StringComparison.CurrentCultureIgnoreCase) == <span class="hljs-literal">false</span>)
            {
                <span class="hljs-function"><span class="hljs-keyword">yield</span> return new <span class="hljs-title">ValidationResult</span>(<span class="hljs-params"><span class="hljs-string">"The gender can either be Male, Female or Other"</span></span>)</span>;
            }

            <span class="hljs-keyword">yield</span> <span class="hljs-keyword">return</span> ValidationResult.Success;
        }
    }
}
</code></pre>
<p></p><p>Here, we have added the required attributes. We have also implemented the <em>IValidatableObject</em> interface so that we can verify the <em>Gender</em>.</p>
<p></p>

<p></p><p>Time to run the <em>dotnet test</em> command. You will see a nice green line saying 5 tests passed.</p>
<p></p>

<p></p><img src="https://arjavdave.com/wp-content/uploads/2021/04/Tests_passed_2-1024x244.png" alt />
<p></p>

<p></p><p>You can add more edge case scenarios in the <em>InlineData</em> to test the Patient model validation tests thoroughly.</p>
<p></p>

<p></p><h2>Duplicate Patient Test</h2>
<p></p>

<p></p><p>We shall now create a test which fails when we try to add a duplicate patient.</p>
<p></p>

<p></p><h4>Failing (Red) Test</h4>
<p></p>

<p></p><p>Create another test in your class <em>PatientTests. </em>Add the below code.</p>
<p></p>
<pre><code>[<span class="hljs-meta">Fact</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PatientDuplicationTestsAsync</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">var</span> scopeFactory = _factory.Services;
    <span class="hljs-keyword">using</span> (<span class="hljs-keyword">var</span> scope = scopeFactory.CreateScope())
    {
        <span class="hljs-keyword">var</span> context = scope.ServiceProvider.GetService&amp;lt;DataContext&amp;gt;();
        <span class="hljs-keyword">await</span> DBUtilities.InitializeDbForTestsAsync(context);

        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> Patient = <span class="hljs-keyword">await</span> context.Patient.FirstOrDefaultAsync();

        <span class="hljs-keyword">var</span> Request = <span class="hljs-keyword">new</span> HttpRequestMessage(HttpMethod.Post, <span class="hljs-string">"api/patient"</span>);
        Request.Content = <span class="hljs-keyword">new</span> StringContent(JsonSerializer.Serialize(Patient), Encoding.UTF8, <span class="hljs-string">"application/json"</span>);

        <span class="hljs-comment">// Act</span>
        <span class="hljs-keyword">var</span> Response = <span class="hljs-keyword">await</span> httpClient.SendAsync(Request);

        <span class="hljs-comment">// Assert</span>
        <span class="hljs-keyword">var</span> StatusCode = Response.StatusCode;
        Assert.Equal(HttpStatusCode.BadRequest, StatusCode);
    }
}
</code></pre>
<p></p><p>We have used a [Fact] attribute instead of [Theory] attribute here since we don't want to test the same method with different parameters. Instead, we want to make the same request twice.</p>
<p></p>

<p></p><p>Run <em><strong>dotnet test </strong></em>to run our newly created test. The test will fail with message <em>Assert.Equal() Failure</em>. Time to fix it.</p>
<p></p>

<p></p><h4>Success (Green) Test</h4>
<p></p>

<p></p><p>To fix the failing test we need to add the implementation for the AddPatient method in <em>PatientController.cs</em>. Update the file's code as below.</p>
<p></p>
<pre><code><span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">TDD.Controllers</span>
{
    [<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
    [<span class="hljs-meta">ApiController</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">PatientController</span> : <span class="hljs-title">Controller</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> DataContext _context;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PatientController</span>(<span class="hljs-params">DataContext context</span>)</span>
        {
            _context = context;
        }
        [<span class="hljs-meta">HttpPost</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&amp;lt;IActionResult&amp;gt; AddPatientAsync([FromBody] Patient Patient)
        {
            <span class="hljs-keyword">var</span> FetchedPatient = <span class="hljs-keyword">await</span> _context.Patient.FirstOrDefaultAsync(x =&amp;gt; x.PhoneNumber == Patient.PhoneNumber);
            <span class="hljs-comment">// If the patient doesn't exist create a new one</span>
            <span class="hljs-keyword">if</span> (FetchedPatient == <span class="hljs-literal">null</span>)
            {
                _context.Patient.Add(Patient);
                <span class="hljs-keyword">await</span> _context.SaveChangesAsync();
                <span class="hljs-keyword">return</span> Created(<span class="hljs-string">$"/patient/<span class="hljs-subst">{Patient.Id}</span>"</span>, Patient);
            }
            <span class="hljs-comment">// Else throw a bad request</span>
            <span class="hljs-keyword">else</span>
            {
                <span class="hljs-keyword">return</span> BadRequest();
            }
        }
    }
}
</code></pre>
<p></p><p>Run the <strong><em>dotnet test </em></strong>again and you will see that the test has passed.</p>
<p></p>

<p></p><p>You can run all the tests by calling <strong><em>dotnet test.</em></strong></p>
<p></p>

<p></p><h2>Important Notes</h2>
<p></p>

<p></p><p>As you add more models/domains like Doctors, Staff, Instruments etc. You will have to create more tests. Make sure to have a different WAF, utility wrappers and different Test files for each of them.</p>
<p></p>

<p></p><p>Secondly, the tests in the same file do not run in parallel. But, the tests from different files do run in parallel. Therefore, each WAF should have a different database name so that data is not misconfigured. </p>
<p></p>

<p></p><p>Lastly, the connections to the original database still needs to be setup in the main project. </p>
<p></p>

<p></p><h2>Thought Process</h2>
<p></p>

<p></p><p>The thought process for creating tests for all scenarios are similar. </p>
<p></p>

<p></p><p>That is, you should first identify the requirements. Then, set up a skeleton of methods and classes without implementation. Write tests to verify the implementation. Finally, refactor as needed and rerun the tests.</p>
<p></p>

<p></p><p>This tutorial didn't include authentication and authorisation for api's. You can <a href="https://arjavdave.com/2021/03/31/net-5-setup-authentication-and-authorisation/" target="_blank">read here</a> on how to set it up.</p>
<p></p>

<p></p><p>Since, it is not possible to cover all the test cases, I have created a <a href="https://github.com/shenanigan/tdd-demo" target="_blank">repository on Github</a>. It covers the implementation for all the test cases and the implementation as well. </p>
<p></p>

<p></p><p>You can find the <a href="https://github.com/shenanigan/tdd-demo" target="_blank">project here</a>.</p>
<p></p>

<p></p><h2>Conclusion</h2>
<p></p>

<p></p><p>In order for TDD to be effective you really need to have a clear idea of what the requirements are. If the requirements keep on changing it would get very tough to maintain the tests as well as the project. </p>
<p></p>

<p></p><p>TDD mainly covers unit, integration &amp; functional tests. You will still have to do UAT, Configuration &amp; Production testing before you go live. </p>
<p></p>

<p></p><p>Having said that, TDD is really helpful in making your project bug free. Secondly, it boosts your confidence for the implementation. You will be able to change bits &amp; pieces of your code as long as the tests pass. Lastly, it provides a better architecture for your project. </p>
<p></p>

<p></p><p>Hope you like the article. Let me know your thoughts or feedback. </p>
<p></p>
<p><a target="_blank" href="https://arjavdave.com">Check more tutorials on .NET here.</a></p>
]]></content:encoded></item><item><title><![CDATA[.NET 5: How to authenticate & authorise API's correctly]]></title><description><![CDATA[In over 11 years of my experience I have seen so many API's that have major security flaw. They either lack a proper setup of Authentication or Authorisation or both. The developers might feel okay since these endpoints are usually not public. But it...]]></description><link>https://blog.royalecheese.com/net-5-how-to-authenticate-and-authorise-apis-correctly</link><guid isPermaLink="true">https://blog.royalecheese.com/net-5-how-to-authenticate-and-authorise-apis-correctly</guid><category><![CDATA[Security]]></category><category><![CDATA[server]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[#cybersecurity]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Wed, 31 Mar 2021 08:17:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1617178473306/-iopuMiPg.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In over 11 years of my experience I have seen so many API's that have major security flaw. They either lack a proper setup of Authentication or Authorisation or both. The developers might feel okay since these endpoints are usually not public. But it is a huge security loop hole which anyone can easily target. </p>
<p>To better understand security for API's let's create a demo project for FBI. There will be an Admin who can enrol FBI Agents and change their clearance levels. Secondly FBI Agents with <em>Clearance Level 1</em> will be able to access public files and agents with <em>Clearance Level 2</em> will be able to access pubic &amp; classified files.</p>
<p>First some theory! Not Interested? <a class="post-section-overview" href="#project-setup">Take me to the code</a>.</p>
<h2 id="authentication">Authentication</h2>
<p>Our Agent has successfully cleared all his exams; time to enrol him. In order to do that he will provide his documents and in return will get his badge. </p>
<p>In the above scenario <em>providing documents</em> is like login where once verified he will be provided with a token (badge). This process is called <em>Authentication</em>. It determines whether agents are who they claim to be. </p>
<p>We are going to use Json Web Tokens (JWT) Bearer tokens for authentication. <em>Bearer tokens</em> are a type of tokens generated by servers which contain details of the claims/roles of a user trying to login. Bearer tokens are mostly structured tokens like <em>JWT</em>. <a target="_blank" href="https://jwt.io/introduction">Read here</a> to know more about JWT.</p>
<h2 id="authorisation">Authorisation</h2>
<p>Now since the FBI Agent has got his badge he can enter the FBI building. He is also able to access public files, but when trying to access classified files he gets <em><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401">401</a></em> error. </p>
<p>This is because FBI Agent is not <em>authorised</em> to access classified files. <em>Authorisation</em> determines what agents can and cannot access.</p>
<p>As mentioned above the JWT Bearer token contains claims/roles. Based on it, our server decides whether to give access to a private resource or not. </p>
<h2 id="access-flow">Access Flow</h2>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y1pcf6w2cjhtj9hgzm8v.jpg" alt="Access Flow" /></p>
<p>As you can see in the above diagram on successful login the server returns a Bearer token. The client uses the bearer token in subsequent calls to access a private resource. </p>
<p>These are the two main concepts that we are going to implement in our article. </p>
<p>Enough with the theory, show me some code!</p>
<h2 id="project-setup-lessa-idproject-setupgreaterlessagreater">Project Setup <a id="project-setup"></a></h2>
<p>Create a new project by executing the command <strong>dotnet new webapi --name FBI</strong> from your cli. It will create a project with a sample WeatherForecast api. </p>
<p>Why work on WeatherForecast when we can work on FBI. Go ahead and delete <em>WeatherForecast.cs</em> file. </p>
<p>Add dependencies by executing the commands</p>
<pre><code><span class="hljs-selector-tag">dotnet</span> <span class="hljs-selector-tag">add</span> <span class="hljs-selector-tag">package</span> <span class="hljs-selector-tag">Microsoft</span><span class="hljs-selector-class">.IdentityModel</span><span class="hljs-selector-class">.Tokens</span> <span class="hljs-selector-tag">--version</span> 6<span class="hljs-selector-class">.9</span><span class="hljs-selector-class">.0</span>
<span class="hljs-selector-tag">dotnet</span> <span class="hljs-selector-tag">add</span> <span class="hljs-selector-tag">package</span> <span class="hljs-selector-tag">Microsoft</span><span class="hljs-selector-class">.AspNetCore</span><span class="hljs-selector-class">.Authentication</span><span class="hljs-selector-class">.JwtBearer</span> <span class="hljs-selector-tag">--version</span> 5<span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.4</span>
</code></pre><p>In <em>ConfigureServices</em> function in your <em>Startup.cs</em> file add the below code. </p>
<pre><code>var TokenValidationParameters = <span class="hljs-built_in">new</span> TokenValidationParameters
{
    ValidIssuer = "https://fbi-demo.com",
    ValidAudience = "https://fbi-demo.com",
    IssuerSigningKey = <span class="hljs-built_in">new</span> SymmetricSecurityKey(<span class="hljs-keyword">Encoding</span>.UTF8.GetBytes("SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ")),
    ClockSkew = TimeSpan.Zero // remove delay <span class="hljs-keyword">of</span> token <span class="hljs-keyword">when</span> expire
};
</code></pre><p>We are defining the parameters for validating a token. Make sure that the length of the string for generating SymmetricSecurityKey is 32.</p>
<p>Next, setup the services to add authentication for API's.</p>
<pre><code>services
    .AddAuthentication(<span class="hljs-function"><span class="hljs-params">options</span> =&gt;</span>
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(<span class="hljs-function"><span class="hljs-params">cfg</span> =&gt;</span>
    {
        cfg.TokenValidationParameters = TokenValidationParameters;
    });
</code></pre><p>The <em>AddAuthentication</em> method registers services required by authentication services. It also configures JWT Bearer Authentication as the default scheme.</p>
<p>The <em>AddJwtBearer</em> enables JWT-bearer authentication and setting the TokenValidationParameters defined above.</p>
<p>Now let's add some Authorisation claims for our <em>Agent</em> &amp; <em>Admin</em>.</p>
<pre><code>services.AddAuthorization(<span class="hljs-function"><span class="hljs-params">cfg</span> =&gt;</span>
    {
        cfg.AddPolicy(<span class="hljs-string">"Admin"</span>, <span class="hljs-function"><span class="hljs-params">policy</span> =&gt;</span> policy.RequireClaim(<span class="hljs-string">"type"</span>, <span class="hljs-string">"Admin"</span>));
        cfg.AddPolicy(<span class="hljs-string">"Agent"</span>, <span class="hljs-function"><span class="hljs-params">policy</span> =&gt;</span> policy.RequireClaim(<span class="hljs-string">"type"</span>, <span class="hljs-string">"Agent"</span>));
        cfg.AddPolicy(<span class="hljs-string">"ClearanceLevel1"</span>, <span class="hljs-function"><span class="hljs-params">policy</span> =&gt;</span> policy.RequireClaim(<span class="hljs-string">"ClearanceLevel"</span>, <span class="hljs-string">"1"</span>, <span class="hljs-string">"2"</span>));
        cfg.AddPolicy(<span class="hljs-string">"ClearanceLevel2"</span>, <span class="hljs-function"><span class="hljs-params">policy</span> =&gt;</span> policy.RequireClaim(<span class="hljs-string">"ClearanceLevel"</span>, <span class="hljs-string">"2"</span>));
    });
</code></pre><p>The <em>AddAuthorization</em> method registers services required for authorisation. We are also adding claims for <em>Admin</em>, <em>Agent</em>, <em>ClearanceLevel1</em> and <em>ClearanceLevel2</em> by calling <em>AddPolicy</em>. A claim is a name value pair that represents what the subject is. Since clearance level 2 can also access clearance level 1 we have put <em>"1", "2"</em> in ClearanceLevel1. You can read more about claims <em><a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-5.0">here</a></em>.</p>
<p>Lastly in the <em>Configure</em> method add the below line just above <em>app.UseAuthorization();</em></p>
<pre><code><span class="hljs-selector-tag">app</span><span class="hljs-selector-class">.UseAuthentication</span>();
</code></pre><h2 id="admin-controller">Admin Controller</h2>
<p>Rename your file <em>WeatherForecastController.cs</em> to <em>AdminController.cs</em>. Do change the class name and constructor names as well. Finally, remove everything except the constructor. </p>
<pre><code><span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">FBI.Controllers</span>
{
    [<span class="hljs-meta">ApiController</span>]
    [<span class="hljs-meta">Route(<span class="hljs-meta-string">"[controller]"</span>)</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AdminController</span> : <span class="hljs-title">ControllerBase</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AdminController</span>(<span class="hljs-params"></span>)</span> { }
    }
}
</code></pre><h3 id="login-api">Login API</h3>
<p>Let's create a login API for Admin so that she can get a token to perform other tasks. </p>
<pre><code>[<span class="hljs-meta">HttpPost</span>]
[<span class="hljs-meta">Route(<span class="hljs-meta-string">"[action]"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">Login</span>(<span class="hljs-params">[FromBody] User User</span>)</span>
{
    <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Authenticate Admin with Database</span>
    <span class="hljs-comment">// If not authenticate return 401 Unauthorized</span>
    <span class="hljs-comment">// Else continue with below flow</span>

    <span class="hljs-keyword">var</span> Claims = <span class="hljs-keyword">new</span> List&lt;Claim&gt;
            {
                <span class="hljs-keyword">new</span> Claim(<span class="hljs-string">"type"</span>, <span class="hljs-string">"Admin"</span>),
            };

    <span class="hljs-keyword">var</span> Key = <span class="hljs-keyword">new</span> SymmetricSecurityKey(Encoding.UTF8.GetBytes(<span class="hljs-string">"SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ"</span>));

    <span class="hljs-keyword">var</span> Token = <span class="hljs-keyword">new</span> JwtSecurityToken(
        <span class="hljs-string">"https://fbi-demo.com"</span>,
        <span class="hljs-string">"https://fbi-demo.com"</span>,
        Claims,
        expires: DateTime.Now.AddDays(<span class="hljs-number">30.0</span>),
        signingCredentials: <span class="hljs-keyword">new</span> SigningCredentials(Key, SecurityAlgorithms.HmacSha256)
    );

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> OkObjectResult(<span class="hljs-keyword">new</span> JwtSecurityTokenHandler().WriteToken(Token));
}
</code></pre><p>In the above code <em>User</em> is a model with properties <em>Username</em> &amp; <em>Password</em>. We are also creating an object of <em>JwtSecurityToken</em> using configurations that we have used in <em>Startup.cs</em> file. The token is then converted to string and returned in an OkObjectResult.</p>
<p>You can now open Swagger and execute the API to see a bearer token. A bearer token will be returned as you can see below. </p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aedozuzics30gunpp3b5.png" alt="Bearer Token Response" /></p>
<p>Keep the token handy since we are going to use it in the next section. You can also visit https://jwt.io to analyse your token.</p>
<h3 id="generate-badge-api">Generate Badge API</h3>
<p>Generating badge for an Agent is a sensitive task and should only be Authorised by an <em>Admin</em>. We are going to add an <em>Authorize</em> attribute for the <em>GenerateBadge</em> api.</p>
<pre><code>[<span class="hljs-meta">HttpPost</span>]
[<span class="hljs-meta">Route(<span class="hljs-meta-string">"[action]"</span>)</span>]
[<span class="hljs-meta">Authorize(Policy = <span class="hljs-meta-string">"Admin"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">GenerateBadge</span>(<span class="hljs-params">[FromBody] Agent Agent</span>)</span>
{
<span class="hljs-keyword">var</span> Claims = <span class="hljs-keyword">new</span> List&lt;Claim&gt;
    {
        <span class="hljs-keyword">new</span> Claim(<span class="hljs-string">"type"</span>, <span class="hljs-string">"Agent"</span>),
        <span class="hljs-keyword">new</span> Claim(<span class="hljs-string">"ClearanceLevel"</span>, Agent.ClearanceLevel.ToString()),
    };

    <span class="hljs-keyword">var</span> Key = <span class="hljs-keyword">new</span> SymmetricSecurityKey(Encoding.UTF8.GetBytes(<span class="hljs-string">"SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ"</span>));

    <span class="hljs-keyword">var</span> Token = <span class="hljs-keyword">new</span> JwtSecurityToken(
        <span class="hljs-string">"https://fbi-demo.com"</span>,
        <span class="hljs-string">"https://fbi-demo.com"</span>,
        Claims,
        expires: DateTime.Now.AddDays(<span class="hljs-number">30.0</span>),
        signingCredentials: <span class="hljs-keyword">new</span> SigningCredentials(Key, SecurityAlgorithms.HmacSha256)
    );

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> OkObjectResult(<span class="hljs-keyword">new</span> JwtSecurityTokenHandler().WriteToken(Token));
}
</code></pre><p>Here Agent is a model with properties <em>Name</em> as string and <em>ClearanceLevel</em> as int.</p>
<p>Now when you go back to swagger and try to execute <em>GenerateBadge</em> api it will give you 401 Unauthorised response. Since we have not passed the bearer token we are getting this error. </p>
<p>To be able to add the Authorize header in Swagger change the <em>services.AddSwaggerGen</em> as below:</p>
<pre><code>services.AddSwaggerGen(c =&gt;
{
    c.SwaggerDoc("v1", <span class="hljs-built_in">new</span> OpenApiInfo { Title = "FBI", Version = "v1" });
    c.AddSecurityDefinition("Bearer", <span class="hljs-built_in">new</span> OpenApiSecurityScheme
    {
        <span class="hljs-keyword">In</span> = ParameterLocation.<span class="hljs-keyword">Header</span>,
        Description = "Please enter JWT with Bearer into field",
        <span class="hljs-type">Name</span> = "Authorization",
        <span class="hljs-keyword">Type</span> = SecuritySchemeType.ApiKey
    });
    c.AddSecurityRequirement(<span class="hljs-built_in">new</span> OpenApiSecurityRequirement {
    { <span class="hljs-built_in">new</span> OpenApiSecurityScheme
            {
                Reference = <span class="hljs-built_in">new</span> OpenApiReference { <span class="hljs-keyword">Type</span> = ReferenceType.SecurityScheme, Id = "Bearer"}
            },
        <span class="hljs-built_in">new</span> string[] {}
    }
    });
});
</code></pre><p>When you refresh Swagger in your browser you will notice an <em>Authorize</em> button on the right side above the list of apis.</p>
<p>Click on the newly added <em>Authorize</em> button in Swagger which will open up a dialog. We need to mention what type of token it is. So first enter <em>Bearer</em> in the field then a space and then the token generated from the <em>/Admin/Login</em> api from previous section. </p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ks0jtoy39ogcpa61s71.png" alt="Authorization Header" /></p>
<p>Click on the header to lock in the token. Now you are all set. When you execute the <em>GenerateBadge</em> api again you will get a token (analogous to badge). Keep this token handy, since we require in next section. Also make sure to <strong>pass ClearanceLevel as 1</strong> for now.</p>
<h2 id="agent-controller">Agent Controller</h2>
<p>Create a new file <em>AgentController.cs</em> with below content.</p>
<pre><code><span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">FBI.Controllers</span>
{
    [<span class="hljs-meta">ApiController</span>]
    [<span class="hljs-meta">Route(<span class="hljs-meta-string">"[controller]"</span>)</span>]
    [<span class="hljs-meta">Authorize(Policy = <span class="hljs-meta-string">"Agent"</span>)</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AgentController</span> : <span class="hljs-title">ControllerBase</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AgentController</span>(<span class="hljs-params"></span>)</span> { }
    }
}
</code></pre><p>As you can see above we are authorising the whole controller for Agent's access only. So even Admin won't be able to access the API's we are going to create.</p>
<h3 id="access-records-apis">Access Records API's</h3>
<p>Let's add the api's to access both public and classified files. </p>
<pre><code>[<span class="hljs-meta">HttpGet</span>]
[<span class="hljs-meta">Route(<span class="hljs-meta-string">"[action]"</span>)</span>]
[<span class="hljs-meta">Authorize(Policy = <span class="hljs-meta-string">"ClearanceLevel1"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> ActionResult&lt;String&gt; <span class="hljs-title">AccessPublicFiles</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> OkObjectResult(<span class="hljs-string">"Public Files Accessed"</span>);
}

[<span class="hljs-meta">HttpGet</span>]
[<span class="hljs-meta">Route(<span class="hljs-meta-string">"[action]"</span>)</span>]
[<span class="hljs-meta">Authorize(Policy = <span class="hljs-meta-string">"ClearanceLevel2"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> ActionResult&lt;String&gt; <span class="hljs-title">AccessClassifiedFiles</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> OkObjectResult(<span class="hljs-string">"Classified Files Accessed"</span>);
}
</code></pre><p>We have added <em>Authorize</em> attribute's for both API's such that public files can be accessed by <em>ClearanceLevel1</em> and classified files can be accessed by <em>ClearanceLevel2</em>.</p>
<p>If you try to access these API's with the Admin token you will get 403 Forbidden error. So go ahead and click on the <em>Authorize</em> button again and click on <em>logout</em>. Then, get the token from the above step and paste in the field with <em>Bearer</em> as a prefix i.e. <em>Bearer </em>. </p>
<p>Now when you access <em>/Agent/AccessPublicFiles</em> api you will see response 200 with message <em>Public Files Accessed</em>. But when you try the classified api you get 403 Forbidden error.</p>
<h2 id="changing-clearance-level">Changing Clearance Level</h2>
<p>Fast forward 3 years and our <em>Agent's</em> performance has been mind bogglingly good. Management has now decided to promote him to ClearanceLevel2. </p>
<p>The <em>Agent</em> goes to the <em>Admin</em> and asks her to provide a token/badge with Clearance Level 2.</p>
<p>The <em>Admin</em> calls the <em>/Admin/Login</em> api to generate his own token first. She then enters it in the <em>Authorize</em> dialog. </p>
<p><em>/Admin/GenerageBadge</em> api is then called by Admin with value 2 in the ClearanceLevel. This generates a new token/badge which she then hands over to <em>Agent</em>.</p>
<p>The <em>Agent</em> enters this token/badge in the <em>Authorize</em> dialog and when he now calls <em>/Agent/AccessClassifiedFiles</em> he is pleased to see the result <em>Classified Files Accessed</em>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>You can find the whole project <a target="_blank" href="https://github.com/shenanigan/fbi-demo">here</a> on github.</p>
<p>API security is extremely important and shouldn't be taken lightly even if it's for internal use only. Setup Authentication and Authorisation and you are halfway there. </p>
<p>There are other other security measures you can security against DDoS attacks, accepting API's from a particular IP or domain only etc.</p>
<p>How did you like the article? What are the other security measures do you usually take? Any feedbacks or comments?</p>
<p>You can checkout out more tutorials on <a target="_blank" href="https://arjavdave.com">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[How to Speed Up Your Website with Azure CDN]]></title><description><![CDATA[What is CDN? 
A Content Delivery Network (CDN) helps you deliver your content more quickly. You can serve any type of content that remains unchanged over a period of time, like images, videos, CSS, JavaScript, HTML files, PDFs, and more.
A CDN is a g...]]></description><link>https://blog.royalecheese.com/how-to-speed-up-your-website-with-azure-cdn</link><guid isPermaLink="true">https://blog.royalecheese.com/how-to-speed-up-your-website-with-azure-cdn</guid><category><![CDATA[Azure]]></category><category><![CDATA[CDN]]></category><category><![CDATA[Security]]></category><category><![CDATA[SEO]]></category><category><![CDATA[optimization]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Mon, 29 Mar 2021 05:03:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616994138052/P__JFhHc8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="what-is-cdn-lessa-namewhat-is-cdngreaterlessagreater">What is CDN? <a></a></h2>
<p>A Content Delivery Network (CDN) helps you deliver your content more quickly. You can serve any type of content that remains unchanged over a period of time, like images, videos, CSS, JavaScript, HTML files, PDFs, and more.</p>
<p>A CDN is a group of servers that are spread across the world to deliver the content from the <em>Edge servers</em>. Edge servers are servers located closest to the place from where the request is being made.</p>
<p>Depending on the request, edge servers may either return the content from its cache or they can fetch it from the <em>Origin Server</em>. The servers that serve the actual content are called Origin servers.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i7t9e95tryv3mi44ghc.png" alt="CDN Overview" /></p>
<p>In the above image, the Edge Servers are located around the world and the Origin Server is located in California, USA. When a request is made, the Edge Server located at Mumbai, India may contact the Origin Server if it's not able to serve the content.</p>
<h2 id="how-cdn-works-lessa-namehow-cdn-worksgreaterlessagreater">How CDN works? <a></a></h2>
<p>CDNs have four main parts: A <em>Consumer</em>, <em>DNS</em>, <em>Edge Server</em> &amp; <em>Origin Server</em>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gni65528kxlmciivhnyw.png" alt="CDN Detail" /></p>
<p>When the Consumer makes a request, it is at first accepted by its Internet Service Provider (ISP). The ISP will then hit the content provider's <em>Authoritative DNS</em>. </p>
<blockquote>
<p>An Authoritative DNS converts the DNS request to an IP request.</p>
</blockquote>
<p>When the Authoritative DNS is made, it returns the IP address of the closest Edge Server. The Edge Server will then check in its own cache to see if the requested content is available.</p>
<p>If it is, then it returns the content. If the content is not available, it requests the content from the Origin Server and on retrieval caches it.</p>
<h2 id="benefits-of-cdn-lessa-namebenefits-cdngreater">Benefits of CDN <a></a></h2>
<h4 id="low-bandwidth-consumption">Low Bandwidth Consumption</h4>
<p>Many web hosts have a limited bandwidth allocation per month. If you go beyond that you will be charged extra.</p>
<p>With a CDN most of your bandwidth will be saved since the content will be served by the edge servers.</p>
<h4 id="low-latency">Low Latency</h4>
<p>The Edge Servers cache the content. So anytime cached content is requested the latency is reduced drastically. This is because the request doesn't go all the way to the Origin Server.</p>
<h4 id="security-against-ddos">Security against DDoS</h4>
<p>Almost all the popular CDN's have the capability to protect your webserver against Distributed denial of service (DDos) attacks.</p>
<h4 id="improves-seo">Improves SEO</h4>
<p>Loading time is one of the factors that can affect your site's SEO rankings. If you are serving most of your content via CDN, the loading times are drastically reduced and can help improve your SEO.</p>
<h2 id="deep-dive-into-azure-cdn-lessa-namedeep-dive-azure-cdngreaterlessagreater">Deep Dive into Azure CDN <a></a></h2>
<p>Let's say you have created an Azure Storage Account and hosted a very simple site that displays Hello World as h1. Now that you know the benefits of CDNs, you want to serve your simple site over CDN.</p>
<p> You will have an endpoint something like <em>https://demostorageaccountarjav.z29.web.core.windows.net/</em>(where instead of demostorageaccountarjav it would be your storage account's name). Here are more details on how to <a target="_blank" href="https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website">setup a static website</a>. </p>
<p>Login to your Azure Portal and click on <em>Create a resource</em> from you dashboard. Search for <em>CDN</em> which will open the resource in the marketplace as below.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ravgtt3j0xmeu5osd9d2.png" alt="CDN Create Resource" /></p>
<p>This will open up a form to create a CDN profile. A CDN profile is a set of CDN endpoints. There is not much to fill in here except the name, resource group, and the pricing tier.</p>
<p>Next, select the checkbox to create a CDN endpoint. An endpoint is where the Consumer will be requesting content. So if you have multiple sites you can create multiple endpoints as well.</p>
<p>I have attached a screenshot for your reference on what values to put in. Since CDN is a global service the region selection will be disabled. </p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z35xu3l7ox243ea6ecfj.png" alt="CDN Details" /></p>
<p>You can now click on <em>Create</em> to generate the profile and endpoint. It will take a couple of minutes to create. After it is created and when you go to the home screen, you will have these 4 resources:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o6bxldg59sqod68o4kq1.png" alt="Azure Resources" /></p>
<p>As discussed earlier the CDN Profile is a group of <em>Endpoints</em>. To view the details click the <em>Endpoint</em> resource. You will see an overview with a link to the <em>Endpoint hostname</em>.  </p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vt4t5uxyv6u0i5llicwp.png" alt="Endpoint Overview" /></p>
<p>When you open the endpoint hostname it might show "404 not found" initially. You might have to wait another 10-15 minutes before your actual site is visible. </p>
<p>As discussed in the <a class="post-section-overview" href="#benefits-cdn">benefits</a> section you can configure the Endpoint for security, caching, routing &amp; a lot of other things. You can explore more concepts <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cdn/cdn-how-caching-works">here</a>.</p>
<h2 id="how-to-access-via-sas-token-lessa-nameaccess-sas-tokengreaterlessagreater">How to Access via SAS Token <a></a></h2>
<p>You may be wondering what if my resource is in a private container and can only be accessed via a <em>Shared Access Signature</em> (SAS) Token. Well you are in luck! The query strings are passed on as they are and since SAS is as a query string you are good. </p>
<p>Go ahead and create a new storage account (with static website disabled). Add a new Endpoint in the CDN profile that points to the newly created storage account.</p>
<p>For demo purposes I have created a container named <em>site</em> with private access level and uploaded a Blob named <em>Photo.jpeg</em> in a Storage Account with URL https://demostorageaccountarjav.blob.core.windows.net.</p>
<p>You can of course get a SAS token from the Azure portal directly for testing, but that's not how you would usually do in real-world. For that find below a simple snippet to create SAS token in Node.js.</p>
<pre><code><span class="hljs-keyword">const</span> azureSasToken = <span class="hljs-built_in">require</span>(<span class="hljs-string">'azure-sas-token'</span>);

<span class="hljs-comment">// default token validity is 7 days</span>
<span class="hljs-keyword">let</span> sasToken = azureSasToken.createSharedAccessToken(<span class="hljs-string">'https://&lt;service namespace&gt;.servicebus.windows.net/&lt;topic name or queue&gt;'</span>,
                                <span class="hljs-string">'&lt;signature key name&gt;'</span>,
                                <span class="hljs-string">'&lt;signature hash&gt;'</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`sasToken: <span class="hljs-subst">${sasToken}</span>`</span>);

<span class="hljs-comment">// Specify your own validity in secs, two hours in this example</span>
sasToken = azureSasToken.createSharedAccessToken(<span class="hljs-string">'https://&lt;service namespace&gt;.servicebus.windows.net/&lt;topic name or queue&gt;'</span>,
                                <span class="hljs-string">'&lt;signature key name&gt;'</span>,
                                <span class="hljs-string">'&lt;signature hash&gt;'</span>, 
                                <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">2</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`sasToken: <span class="hljs-subst">${sasToken}</span>`</span>);
</code></pre><p>We have used a simple npm package named <a target="_blank" href="https://www.npmjs.com/package/azure-sas-token">azure-sas-token</a>. Once the SAS is generated your URL will look something like:</p>
<pre><code><span class="hljs-attribute">https</span>://demostorageaccountarjav.blob.core.windows.net/site/Photo.jpeg?sp=r&amp;st=<span class="hljs-number">2021</span>-<span class="hljs-number">03</span>-<span class="hljs-number">25</span>T<span class="hljs-number">07</span>:<span class="hljs-number">28</span>:<span class="hljs-number">45</span>Z&amp;se=<span class="hljs-number">2022</span>-<span class="hljs-number">02</span>-<span class="hljs-number">02</span>T<span class="hljs-number">15</span>:<span class="hljs-number">28</span>:<span class="hljs-number">45</span>Z&amp;spr=https&amp;sv=<span class="hljs-number">2020</span>-<span class="hljs-number">02</span>-<span class="hljs-number">10</span>&amp;sr=b&amp;sig=PD<span class="hljs-number">4</span>HlRI<span class="hljs-number">8</span>bDEirMevpYQgpx<span class="hljs-number">6</span>drwh%<span class="hljs-number">2</span>BE<span class="hljs-number">5</span>EpILfXkQOMlvw%<span class="hljs-number">3</span>D
</code></pre><p>The above URL is pointing directly to the storage account. So go ahead and change the origin so that it uses the origin endpoint. </p>
<pre><code><span class="hljs-attribute">https</span>://demowebsitearjav.azureedge.net/site/Photo.jpeg?sp=r&amp;st=<span class="hljs-number">2021</span>-<span class="hljs-number">03</span>-<span class="hljs-number">25</span>T<span class="hljs-number">07</span>:<span class="hljs-number">28</span>:<span class="hljs-number">45</span>Z&amp;se=<span class="hljs-number">2022</span>-<span class="hljs-number">02</span>-<span class="hljs-number">02</span>T<span class="hljs-number">15</span>:<span class="hljs-number">28</span>:<span class="hljs-number">45</span>Z&amp;spr=https&amp;sv=<span class="hljs-number">2020</span>-<span class="hljs-number">02</span>-<span class="hljs-number">10</span>&amp;sr=b&amp;sig=PD<span class="hljs-number">4</span>HlRI<span class="hljs-number">8</span>bDEirMevpYQgpx<span class="hljs-number">6</span>drwh%<span class="hljs-number">2</span>BE<span class="hljs-number">5</span>EpILfXkQOMlvw%<span class="hljs-number">3</span>D
</code></pre><p>When you visit this site you will now be able to view the protected resource via CDN.</p>
<h2 id="conclusion-lessa-nameconclusiongreaterlessagreater">Conclusion <a></a></h2>
<p>In my opinion everyone should be using a Content Delivery Network. There are lots of other providers like Cloudflare, S3 etc. but Microsoft is one of the major players which is emerging with a wide variety of services. </p>
<p>If you are an Azure fan like I am you should definitely give Azure CDN a try. </p>
<p>For any feedback or questions you can <a target="_blank" href="https://arjavdave.com/contact/">get in touch</a> with me.</p>
<p>Check <a target="_blank" href="https://arjavdave.com">here</a> for more tutorials like this.</p>
]]></content:encoded></item><item><title><![CDATA[Azure Functions & wkhtmltopdf: Convert HTML to PDF]]></title><description><![CDATA[We are going to use Azure Functions & wkhtmltopdf tool to generate a PDF file from an HTML file. You might want to create a PDF file for a great many reasons e.g. generate invoices for sales, medical reports for your patients, insurance forms for you...]]></description><link>https://blog.royalecheese.com/azure-functions-and-wkhtmltopdf-convert-html-to-pdf</link><guid isPermaLink="true">https://blog.royalecheese.com/azure-functions-and-wkhtmltopdf-convert-html-to-pdf</guid><category><![CDATA[Azure]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Mon, 22 Mar 2021 09:14:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616404369243/g4YEU8Ya-.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We are going to use Azure Functions &amp; <a href="https://wkhtmltopdf.org/" target="_blank">wkhtmltopdf</a> tool to generate a PDF file from an HTML file. You might want to create a PDF file for a great many reasons e.g. generate invoices for sales, medical reports for your patients, insurance forms for your clients etc. There are a few ways to do this.</p>
<p>Firstly, you can use Adobe‘s fill and sign tool to fill out forms, but this mostly requires a human interaction and hence it’s not scalable and not convenient.</p>
<p>Second option is you directly create a pdf file. Based on the platform you are working on you will have tools to directly create a pdf file. If it’s a very simple pdf you can take this approach.</p>
<p>This brings us to our final and most convenient option. wkhtmltopdf is a really great tool to convert your HTML to PDF. Since it is free, open source and can be compiled for almost all platforms it is our best choice.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul><li>Microsoft VS Code</li><li>An account on <a href="https://portal.azure.com" target="_blank">Azure Portal</a></li><li>Linux Basic (B1) App Service Plan. If you already have a Windows Basic (B1) App Service Plan you can use that.</li><li>Azure Storage Account.</li></ul>

<h2 id="azure-functions">Azure Functions</h2>
<p>Since converting a HTML to PDF is a time consuming task we shouldn’t run it on our main web server. Otherwise it may start blocking other important requests. Azure Functions are the best way to delegate such tasks.</p>
<p>In order to create a function you will first need to install Azure Functions on your machine. Based on your OS install the <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=macos%2Ccsharp%2Cbash#install-the-azure-functions-core-tools" target="_blank">Azure Functions Core Tools</a>. Once installed open your command line tool to fire the below command. html2pdf is your project's name. You can replace it with any name.</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">init</span> <span class="hljs-title">html2pdf</span></span>
</code></pre><p>On executing the command it will ask for a worker runtime. Here select <em>1. dotnet</em> since being a Microsoft’s product it provides great support for dotnet. This will generate a folder named <em>html2pdf</em> in your current directory. Since Visual Studio Code allows to directly publish to Azure Functions we will use it to code and deploy.</p>
<p>After you open your project in VS Code create a file named <em>Html2Pdf.cs</em>. Azure Functions provide a wide variety of <a href="https://www.serverless360.com/blog/azure-functions-triggers-and-bindings" target="_blank">triggers</a> to execute the function. For now we will start with HTTP trigger i.e. the function can be called directly via http protocol. In our newly created file paste the below content.</p>
<pre><code><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> Microsoft.Azure.WebJobs;
<span class="hljs-keyword">using</span> Microsoft.Azure.WebJobs.Extensions.Http;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Html2Pdf</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Html2Pdf</span>
    {
        <span class="hljs-comment">// The name of the function</span>
        [<span class="hljs-meta">FunctionName(<span class="hljs-meta-string">"Html2Pdf"</span>)</span>]

        <span class="hljs-comment">// The first arugment tells that the functions can be triggerd by a POST HTTP request. </span>
        <span class="hljs-comment">// The second argument is mainly used for logging information, warnings or errors</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Run</span>(<span class="hljs-params">[HttpTrigger(AuthorizationLevel.Function, <span class="hljs-string">"POST"</span></span>)] Html2PdfRequest Request, ILogger Log)</span>
        {
        }
    }
}
</code></pre><p>We have created a skeleton in which we will now fill in the details. As you might have noticed the type of request variable is <em>Html2PdfRequest</em>. So let’s create a model <em>Html2PdfRequest.cs</em> class as below.</p>
<pre><code><span class="hljs-keyword">namespace</span> <span class="hljs-title">Html2Pdf</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Html2PdfRequest</span>
    {
        <span class="hljs-comment">// The HTML content that needs to be converted.</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> HtmlContent { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-comment">// The name of the PDF file to be generated</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> PDFFileName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre><h2 id="dinktopdf">DinkToPdf</h2>
<p>In order to invoke wkhtmltopdf from our managed code a technology called P/Invoke is used. In short P/Invoke allows us to access structs, callbacks and functions in unmanaged libraries. There is a nice P/Invoke wrapper named DinkToPdf to allow us to abstract away the technicalities.
You can add DinkToPdf to your project via nuget. Simply run the command from your root folder.</p>
<pre><code><span class="hljs-attribute">dotnet</span> add package DinkToPdf --version <span class="hljs-number">1</span>.<span class="hljs-number">0</span>.<span class="hljs-number">8</span>
</code></pre><p>Time to add some code at the top of our class <em>Html2Pdf</em>.</p>
<pre><code><span class="hljs-comment">// Read more about converter on: https://github.com/rdvojmoc/DinkToPdf</span>
<span class="hljs-comment">// For our purposes we are going to use SynchronizedConverter</span>
IPdfConverter pdfConverter = <span class="hljs-keyword">new</span> SynchronizedConverter(<span class="hljs-keyword">new</span> PdfTools());
<span class="hljs-comment">// A function to convert html content to pdf based on the configuration pased as arguments</span>
<span class="hljs-comment">// Arguments:</span>
<span class="hljs-comment">// HtmlContent: the html content to be converted</span>
<span class="hljs-comment">// Width: the width of the pdf to be created. e.g. "8.5in", "21.59cm" etc.</span>
<span class="hljs-comment">// Height: the height of the pdf to be created. e.g. "11in", "27.94cm" etc.</span>
<span class="hljs-comment">// Margins: the margis around the content</span>
<span class="hljs-comment">// DPI: The dpi is very important when you want to print the pdf.</span>
<span class="hljs-comment">// Returns a byte array of the pdf which can be stored as a file</span>
<span class="hljs-keyword">private</span> byte[] BuildPdf(<span class="hljs-keyword">string</span> HtmlContent, <span class="hljs-keyword">string</span> Width, <span class="hljs-keyword">string</span> Height, MarginSettings Margins, <span class="hljs-keyword">int</span>? DPI = <span class="hljs-number">180</span>)
{
  <span class="hljs-comment">// Call the Convert method of SynchronizedConverter "pdfConverter"</span>
  <span class="hljs-keyword">return</span> pdfConverter.Convert(<span class="hljs-keyword">new</span> HtmlToPdfDocument()
            {
                <span class="hljs-comment">// Set the html content</span>
                Objects =
                {
                    <span class="hljs-keyword">new</span> ObjectSettings
                    {
                        HtmlContent = HtmlContent
                    }
                },
                <span class="hljs-comment">// Set the configurations</span>
                GlobalSettings = <span class="hljs-keyword">new</span> GlobalSettings
                {
                    <span class="hljs-comment">// PaperKind.A4 can also be used instead PechkinPaperSize</span>
                    PaperSize = <span class="hljs-keyword">new</span> PechkinPaperSize(Width, Height),
                    DPI = DPI,
                    Margins = Margins
                }
            });
}
</code></pre><p>I have added inline comments so as to be self explanatory. If you have any questions you can ask me in the comments section below. Let’s call the above created function from our <em>Run</em> method.</p>
<pre><code>// PDFByteArray <span class="hljs-keyword">is</span> a byte <span class="hljs-keyword">array</span> <span class="hljs-keyword">of</span> pdf <span class="hljs-keyword">generated</span> <span class="hljs-keyword">from</span> the HtmlContent 
var PDFByteArray = BuildPdf(Request.HtmlContent, "8.5in", "11in", <span class="hljs-built_in">new</span> MarginSettings(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>,<span class="hljs-number">0</span>));
</code></pre><p>Once the byte array is generated let’s store that as a blob in Azure Storage. Before you upload the blob, do create a container. Once you do that add the below code after <em>PDFByteArray</em>.</p>
<pre><code><span class="hljs-comment">// The connection string of the Storage Account to which our PDF file will be uploaded</span>

<span class="hljs-comment">// Make sure to replace with your connection string.</span>
<span class="hljs-keyword">var</span> StorageConnectionString = <span class="hljs-string">"DefaultEndpointsProtocol=https;AccountName=&lt;YOUR ACCOUNT NAME&gt;;AccountKey=&lt;YOUR ACCOUNT KEY&gt;;EndpointSuffix=core.windows.net"</span>;

<span class="hljs-comment">// Generate an instance of CloudStorageAccount by parsing the connection string</span>
<span class="hljs-keyword">var</span> StorageAccount = CloudStorageAccount.Parse(StorageConnectionString);

<span class="hljs-comment">// Create an instance of CloudBlobClient to connect to our storage account</span>
CloudBlobClient BlobClient = StorageAccount.CreateCloudBlobClient();

<span class="hljs-comment">// Get the instance of CloudBlobContainer which points to a container name "pdf"</span>
<span class="hljs-comment">// Replace your own container name</span>
CloudBlobContainer BlobContainer = BlobClient.GetContainerReference(<span class="hljs-string">"pdf"</span>);

<span class="hljs-comment">// Get the instance of the CloudBlockBlob to which the PDFByteArray will be uploaded</span>
CloudBlockBlob Blob = BlobContainer.GetBlockBlobReference(Request.PDFFileName);

<span class="hljs-comment">// Upload the pdf blob</span>
await Blob.UploadFromByteArrayAsync(PDFByteArray, <span class="hljs-number">0</span>, PDFByteArray.Length);
</code></pre><p>You will see some errors and warning after you add this code. For that firstly, add the missing import statements. Secondly, change the return type from void to async Task for the Run function. Here is what the final <em>Html2Pdf.cs</em> file will look like.</p>
<pre><code><span class="hljs-keyword">using</span> Microsoft.Azure.WebJobs;
<span class="hljs-keyword">using</span> Microsoft.Azure.WebJobs.Extensions.Http;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging;
<span class="hljs-keyword">using</span> DinkToPdf;
<span class="hljs-keyword">using</span> IPdfConverter = DinkToPdf.Contracts.IConverter;
<span class="hljs-keyword">using</span> Microsoft.WindowsAzure.Storage;
<span class="hljs-keyword">using</span> Microsoft.WindowsAzure.Storage.Blob;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Html2Pdf</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Html2Pdf</span>
    {
        <span class="hljs-comment">// Read more about converter on: https://github.com/rdvojmoc/DinkToPdf</span>
        <span class="hljs-comment">// For our purposes we are going to use SynchronizedConverter</span>
        IPdfConverter pdfConverter = <span class="hljs-keyword">new</span> SynchronizedConverter(<span class="hljs-keyword">new</span> PdfTools());

        <span class="hljs-comment">// A function to convert html content to pdf based on the configuration pased as arguments</span>
        <span class="hljs-comment">// Arguments:</span>
        <span class="hljs-comment">// HtmlContent: the html content to be converted</span>
        <span class="hljs-comment">// Width: the width of the pdf to be created. e.g. "8.5in", "21.59cm" etc.</span>
        <span class="hljs-comment">// Height: the height of the pdf to be created. e.g. "11in", "27.94cm" etc.</span>
        <span class="hljs-comment">// Margins: the margis around the content</span>
        <span class="hljs-comment">// DPI: The dpi is very important when you want to print the pdf.</span>
        <span class="hljs-comment">// Returns a byte array of the pdf which can be stored as a file</span>
        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">byte</span>[] <span class="hljs-title">BuildPdf</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> HtmlContent, <span class="hljs-keyword">string</span> Width, <span class="hljs-keyword">string</span> Height, MarginSettings Margins, <span class="hljs-keyword">int</span>? DPI = <span class="hljs-number">180</span></span>)</span>
        {
            <span class="hljs-comment">// Call the Convert method of SynchronizedConverter "pdfConverter"</span>
            <span class="hljs-keyword">return</span> pdfConverter.Convert(<span class="hljs-keyword">new</span> HtmlToPdfDocument()
            {
                <span class="hljs-comment">// Set the html content</span>
                Objects =
                {
                    <span class="hljs-keyword">new</span> ObjectSettings
                    {
                        HtmlContent = HtmlContent
                    }
                },
                <span class="hljs-comment">// Set the configurations</span>
                GlobalSettings = <span class="hljs-keyword">new</span> GlobalSettings
                {
                    <span class="hljs-comment">// PaperKind.A4 can also be used instead of width &amp; height</span>
                    PaperSize = <span class="hljs-keyword">new</span> PechkinPaperSize(Width, Height),
                    DPI = DPI,
                    Margins = Margins
                }
            });
        }
        <span class="hljs-comment">// The name of the function</span>
        [<span class="hljs-meta">FunctionName(<span class="hljs-meta-string">"Html2Pdf"</span>)</span>]
        <span class="hljs-comment">// The first arugment tells that the functions can be triggerd by a POST HTTP request. </span>
        <span class="hljs-comment">// The second argument is mainly used for logging information, warnings or errors</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Run</span>(<span class="hljs-params">[HttpTrigger(AuthorizationLevel.Function, <span class="hljs-string">"POST"</span></span>)] Html2PdfRequest Request, ILogger Log)</span>
        {
            <span class="hljs-comment">// PDFByteArray is a byte array of pdf generated from the HtmlContent </span>
            <span class="hljs-keyword">var</span> PDFByteArray = BuildPdf(Request.HtmlContent, <span class="hljs-string">"8.5in"</span>, <span class="hljs-string">"11in"</span>, <span class="hljs-keyword">new</span> MarginSettings(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>));
           <span class="hljs-comment">// The connection string of the Storage Account to which our PDF file will be uploaded</span>

            <span class="hljs-comment">// The connection string of the Storage Account to which our PDF file will be uploaded</span>
            <span class="hljs-keyword">var</span> StorageConnectionString = <span class="hljs-string">"DefaultEndpointsProtocol=https;AccountName=&lt;YOUR ACCOUNT NAME&gt;;AccountKey=&lt;YOUR ACCOUNT KEY&gt;;EndpointSuffix=core.windows.net"</span>;

            <span class="hljs-comment">// Generate an instance of CloudStorageAccount by parsing the connection string</span>
            <span class="hljs-keyword">var</span> StorageAccount = CloudStorageAccount.Parse(StorageConnectionString);

            <span class="hljs-comment">// Create an instance of CloudBlobClient to connect to our storage account</span>
            CloudBlobClient BlobClient = StorageAccount.CreateCloudBlobClient();

            <span class="hljs-comment">// Get the instance of CloudBlobContainer which points to a container name "pdf"</span>
            <span class="hljs-comment">// Replace your own container name</span>
            CloudBlobContainer BlobContainer = BlobClient.GetContainerReference(<span class="hljs-string">"pdf"</span>);

            <span class="hljs-comment">// Get the instance of the CloudBlockBlob to which the PDFByteArray will be uploaded</span>
            CloudBlockBlob Blob = BlobContainer.GetBlockBlobReference(Request.PDFFileName);

            <span class="hljs-comment">// Upload the pdf blob</span>
            <span class="hljs-keyword">await</span> Blob.UploadFromByteArrayAsync(PDFByteArray, <span class="hljs-number">0</span>, PDFByteArray.Length);
        }
    }
}
</code></pre><p>This concludes the coding part.</p>
<h2 id="wkhtmltopdf">wkhtmltopdf</h2>
<p>We will still need to add wkhtmltopdf library in our project. There are a few caveats when selecting a particular Azure App Plan. Based on the Plan, we will have to get the wkhtmltopdf library. For our purposes we have selected Linux Basic (B1) App Service Plan since Windows Basic (B1) App Service Plan is 5 times costlier.</p>
<p>At the time of writing this blog Azure App Service Plan was using Debian 10 with amd64 architecture. Good for us, DinkToPdf provides precompiled libraries for Linux, Windows &amp; MacOS. Download the .so library for Linux and put it in your project’s root folder. I am working on MacOS so I downloaded libwkhtmltox.dylib as well. If you are using Windows or if you have hosted the Azure Functions on Windows App Service Plan you must download the libwkhtmltox.dll. Here is how our project structure will look like.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-21-at-4.41.20-PM.png" alt="project structure" /></p>
<p>When we create a build we need to include the .so library. In order to do that open your csproj file and add the below content to the ItemGroup.</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">None</span> <span class="hljs-attr">Update</span>=<span class="hljs-string">"./libwkhtmltox.so"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>PreserveNewest<span class="hljs-tag">&lt;/<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">CopyToPublishDirectory</span>&gt;</span>Always<span class="hljs-tag">&lt;/<span class="hljs-name">CopyToPublishDirectory</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">None</span>&gt;</span>
</code></pre><p>Here is the whole csproj file:</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">Project</span> <span class="hljs-attr">Sdk</span>=<span class="hljs-string">"Microsoft.NET.Sdk"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">TargetFramework</span>&gt;</span>netcoreapp3.1<span class="hljs-tag">&lt;/<span class="hljs-name">TargetFramework</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">AzureFunctionsVersion</span>&gt;</span>v3<span class="hljs-tag">&lt;/<span class="hljs-name">AzureFunctionsVersion</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"DinkToPdf"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"1.0.8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.NET.Sdk.Functions"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"3.0.11"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">None</span> <span class="hljs-attr">Update</span>=<span class="hljs-string">"host.json"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>PreserveNewest<span class="hljs-tag">&lt;/<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">None</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">None</span> <span class="hljs-attr">Update</span>=<span class="hljs-string">"local.settings.json"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>PreserveNewest<span class="hljs-tag">&lt;/<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CopyToPublishDirectory</span>&gt;</span>Never<span class="hljs-tag">&lt;/<span class="hljs-name">CopyToPublishDirectory</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">None</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">None</span> <span class="hljs-attr">Update</span>=<span class="hljs-string">"./libwkhtmltox.so"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>PreserveNewest<span class="hljs-tag">&lt;/<span class="hljs-name">CopyToOutputDirectory</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CopyToPublishDirectory</span>&gt;</span>Always<span class="hljs-tag">&lt;/<span class="hljs-name">CopyToPublishDirectory</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">None</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">Project</span>&gt;</span>
</code></pre><h2 id="creating-azure-functions-app">Creating Azure Functions App</h2>
<p>Before we deploy to Azure Functions we will have to create the Azure Functions in Azure Portal. You can go to Azure Portal and start creating the Azure Functions resource. You can follow the below screenshots for clarity.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Untitled-1.jpg" alt /></p>
<p>In the below screenshot make sure to select or create at least Basic Plan here. Secondly, in the Operating System select Linux.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.30.48-AM-979x1024.png" alt /></p>
<p>It’s good to have Application Insights since you will be able to see logs and monitor functions. Besides, it hardly costs anything. As shown in the screenshot below select Yes if you want to enable it.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.31.11-AM-962x1024.png" alt /></p>
<p>Select Next: Tags and again click Next and click Create to create your resource. It might take a few minutes to create the Azure Functions resource.</p>
<h2 id="deploying-to-azure-functions">Deploying to Azure Functions</h2>
<p>Once created we will deploy our code directly to Azure Functions via VS Code. For that you will have to go to the extensions and install the Azure Functions extension. With its help we will be able to login and manage Azure Functions.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.03.00-AM-1024x591.png" alt /></p>
<p>Once installed you will see Azure icon on the side bar. When clicked, it will open a panel with an option to Sign In to Azure.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.19.08-AM.png" alt /></p>
<p>Select Sign in to Azure which will open a browser where you can login with your account. Once logged in you can go back to VS Code and see the list of Azure Functions in your side panel.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.43.07-AM.png" alt /></p>
<p>For me there are 4 function apps. Since you might have created just one it will show one. Time to deploy the app.</p>
<p>Press F1 to open a menu with a list of actions. Select Azure Functions: Deploy to Function App… which will open a list of Azure Functions to which you can deploy. Select our newly created Azure Funtions App. This will ask for a confirmation pop-up, so go ahead and deploy it. It will take a few minutes to deploy your App.</p>
<h2 id="configuring-wkhtmltopdf">Configuring wkhtmltopdf</h2>
<p>Once you have deployed to Azure Functions there is still one last thing to do. We will need to add libwkhtmltox.so to a proper location on our Azure Functions App. Login to Azure portal and navigate to our Azure Functions App. On the side panel search for SSH and click the Go button.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.14.03-PM.png" alt /></p>
<p>This will open a SSH console in new tab. Our site is located at /home/site/wwwroot. So navigate to it's bin folder by typing in the below command.</p>
<pre><code><span class="hljs-built_in">cd</span> /home/site/wwwroot/bin
</code></pre><p>When you execute <em>ls</em> command to view the contents of the file you won’t see the <em>libwkhtmltox.so</em> file. It is actually located at <em>/home/site/wwwroot</em>.</p>
<p>That is not the correct position. We need to copy it in the bin folder. For that execute the below command.</p>
<pre><code><span class="hljs-attribute">cp</span> ../libwkhtmltox.so libwkhtmltox.so
</code></pre><p>If you know a better way on how to include the file in the bin folder please suggest in the comment below.</p>
<p>That’s it!!! You have got a fully functional Azure Functions App. Time to call it from our demo dotnet project.</p>
<h2 id="invoking-the-azure-function">Invoking the Azure Function</h2>
<p>All said and done we still need to test and call our function. Before we do that we need to get hold of <em>Code</em> which is required to call the Function. The <em>Code</em> is a secret that needs to be included to call the Function securely. To get the <em>Code</em> navigate to Azure Portal and open your Function App. In the side panel search for <em>Functions</em>.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.28.21-PM-1024x571.png" alt /></p>
<p>You will see <em>Html2Pdf</em> in the list. Click on that function which will open the details view. In the side panel there will be an option for Function Keys. Select that option to view a hidden default Code already added for you.</p>
<p><img src="https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.29.55-PM.png" alt /></p>
<p>Copy the code and keep that handy since it will be needed in the code. In order to test the function I have created a sample console app for you. Replace the base url and the <em>Code</em>.</p>
<pre><code><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Net;
<span class="hljs-keyword">using</span> System.Net.Http;
<span class="hljs-keyword">using</span> System.Net.Http.Headers;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Newtonsoft.Json;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Demo.ConsoleApp</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span>
        {
            <span class="hljs-keyword">string</span> AzureFunctionsUrl = <span class="hljs-string">"https://&lt;Your Base Url&gt;/api/Html2Pdf?code=&lt;Replace with your Code&gt;"</span>;
<span class="hljs-keyword">using</span> (HttpClient client = <span class="hljs-keyword">new</span> HttpClient())
            {
                <span class="hljs-keyword">var</span> Request = <span class="hljs-keyword">new</span> Html2PdfRequest
                {
                    HtmlContent = <span class="hljs-string">"&lt;h1&gt;Hello World&lt;/h1&gt;"</span>,
                    PDFFileName = <span class="hljs-string">"hello-world.pdf"</span>
                };
                <span class="hljs-keyword">string</span> json = JsonConvert.SerializeObject(Request);
                <span class="hljs-keyword">var</span> buffer = System.Text.Encoding.UTF8.GetBytes(json);
                <span class="hljs-keyword">var</span> byteContent = <span class="hljs-keyword">new</span> ByteArrayContent(buffer);
byteContent.Headers.ContentType = <span class="hljs-keyword">new</span> MediaTypeHeaderValue(<span class="hljs-string">"application/json"</span>);
<span class="hljs-keyword">using</span> (HttpResponseMessage res = <span class="hljs-keyword">await</span> client.PostAsync(AzureFunctionsUrl, byteContent))
                {
                    <span class="hljs-keyword">if</span> (res.StatusCode != HttpStatusCode.NoContent)
                    {
                        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Exception(<span class="hljs-string">"There was an error uploading the pdf"</span>);
                    }
                }
            }
        }
    }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Html2PdfRequest</span>
    {
        <span class="hljs-comment">// The HTML content that needs to be converted.</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> HtmlContent { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-comment">// The name of the PDF file to be generated</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> PDFFileName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre><p>Again the code should be pretty self explanatory. If you have any feedback or questions please ask in the comment section below. Once you run the above console app, it will create a <em>hello-world.pdf</em> file in your pdf container in Azure Storage.</p>
<h2 id="conclusion">Conclusion</h2>
<p>That concludes our tutorial on how to convert HTML to PDF using Azure Functions. For any feedback, questions or blog topics you can leave a comment below. Subscribe to the newsletter for upcoming and exciting new tutorials.</p>
<p>You can also follow me on <a href="https://arjav-dave.medium.com/" target="_blank">medium</a>, <a href="https://dev.to/arjavdave" target="_blank">dev.to</a> &amp; <a href="https://blog.royalecheese.com/" target="_blank">hashnode</a>.</p>
<p>Visit <a href="https://arjavdave.com">my blogs</a> for more such tutorials.</p>
]]></content:encoded></item><item><title><![CDATA[How to setup CI CD pipelines for Android with Azure DevOps]]></title><description><![CDATA[DevOps & CI/CD are buzz words for a while now and they have really proven their value in today's fast moving world and Agile development process. One understands the true value only when they have actually been a process of it and see for themselves ...]]></description><link>https://blog.royalecheese.com/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops</link><guid isPermaLink="true">https://blog.royalecheese.com/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops</guid><category><![CDATA[ci-cd]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Tue, 16 Mar 2021 10:46:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615832286561/cP7bCRX_n.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>DevOps &amp; CI/CD are buzz words for a while now and they have really proven their value in today's fast moving world and Agile development process. One understands the true value only when they have actually been a process of it and see for themselves the immense amount of time and headache it saves.</p>
<p>At  <a target="_blank" href="https://royalecheese.com">Royale Cheese</a>  initially we had setup CI/CD for Android via Microsoft’s Visual Studio App Center (an upgrade of Hockey App), but last year they declared the  <a target="_blank" href="https://devblogs.microsoft.com/appcenter/app-center-mbaas-retirement/">retirement of MBaas</a>  which got us worried about the overall future of VS App Center. That was one of the reasons we wanted to switch away from it. Secondly, the free tier provided around 400 minutes of build time per month per account which would had been sufficient for other technologies, but Android takes around 15 minutes to create a single build and deploy. We all know what gradle is capable of 😉. So having multiple apps (both iOS and Android) in the same account didn’t fare well.</p>
<p>We were already using  <a target="_blank" href="https://dev.azure.com/">Azure DevOps</a>  for CI/CD for other technologies and it seemed promising. It’s future plan had no issues of shutting down and it provided around 1800 minutes of free build time. We decided to give it a try and so far don’t have any complaints. We have started loving and adoring Azure DevOps and hope it stays this way.</p>
<p>Alright! Enough with the issues, let’s see how we can get the build &amp; deployment going. In this tutorial we are going to make the newer <strong> <a target="_blank" href="https://developer.android.com/platform/technology/app-bundle">Android App Bundle</a>  (.aab)</strong> and not .apk to upload to the Google Play Console.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>Paid  <a target="_blank" href="http://play.google.com/apps/publish/">Google developer account</a>, an app added to the Play Console.</li>
<li>Azure DevOps account with an Android repository created</li>
<li>A jks file to sign the build</li>
</ul>
<h2 id="continuous-integration">Continuous Integration</h2>
<p>Continuous Integration is the process of running a build &amp; a test suite every time a change has been pushed on to the repository. Since the Android build do take a substantial amount of time and usually cloud CI/CD tools gets costly with more build time it is better to schedule a the CI pipeline once a week. I will be using a demo project named <em>Android</em> created in my devops account to create ci cd pipelines. The project has a blank activity and support Lollipop 5.0 and above.</p>
<p>Open your project and navigate to <em>Pipelines</em> section on the left panel as in the screenshot below.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615788316354/3QaktCAEJ.png" alt="Screenshot 2021-03-15 at 11.34.37 AM.png" /></p>
<p>Click on <em>Create Pipeline</em> which will open the below page.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615788381622/UDac_edWU.png" alt="Screenshot 2021-03-15 at 11.35.54 AM.png" /></p>
<p>Here we are going to select <em>Use the classic editor</em> which is easier than the other options. The other options will eventually lead to writing yaml files for configuring your pipelines. </p>
<p>Usually you might have dev, qa and/or uat branches to upload to the internal or beta channels on Google Play Console and you would only want to promote the app to production from your Google Play Console. So select your branch from which you want to run the build and click Continue. For me everything is on the master branch. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615788759143/2Unbua8li.png" alt="Screenshot 2021-03-15 at 11.41.54 AM.png" /></p>
<p>Ideally we would be tempted to select the <em>Android</em> template and get on with our life; <strong>Don't Do It</strong>. Since the <em>Android</em> template doesn't provide a way to generate an .aab file we are not going to use it. Instead select an empty job.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615789134177/S9KtwtOqs.png" alt="Screenshot 2021-03-15 at 11.48.26 AM.png" /></p>
<p>We will now have an empty <em>Agent Job 1</em> to which we will be adding tasks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615789505012/7r2iaM1VD.png" alt="Screenshot 2021-03-15 at 11.54.31 AM.png" /></p>
<p>We are going to have to do the following tasks to create our CI pipeline:</p>
<ul>
<li>Setting up a jks file</li>
<li>Unsigned build via gradle</li>
<li>Generate .aab file</li>
<li>Create an Artifact</li>
</ul>
<p>Let's start step-by-step.</p>
<h4 id="setting-up-a-jks-file">Setting up a jks file</h4>
<p>If you have uploaded an apk or aab in the past to the play store you must have used a jks file to sign the build. You should the use the same jks file in this step. If you haven't generated a file yet  <a target="_blank" href="https://developer.android.com/studio/publish/app-signing#generate-key">here</a> is a good documentation. </p>
<p>Get a hold of your jks file since we are going to upload it now. Click on <em>+</em> sign besides <em>Agent Job 1</em> which will open a list of tasks to add. Search for <em>Download secure file</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615790140059/ScFo0XHS-.png" alt="Screenshot 2021-03-15 at 12.04.44 PM.png" /></p>
<p>Click on the <em>Add</em> button to add the task. It will give an error saying  <em>Some settings need attention</em>. It is asking for a secure file to be uploaded. Click on the gear/settings icon besides the <em>Secure file</em> field and upload your jks. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615790397405/mI4aYAm-F.png" alt="Screenshot 2021-03-15 at 12.09.25 PM.png" /></p>
<p>We will now provide a name to this file so that it can be referenced from the sign task. Click on the <em>Output Variables</em> drop down and enter <em>KeyStoreFile</em> in the <em>Reference name</em>. This will update the <em>Variables list</em> to reflect the variable name.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615790837324/GGfXHZkR7.png" alt="Screenshot 2021-03-15 at 12.12.32 PM.png" /></p>
<h4 id="unsigned-gradle-file">Unsigned gradle file</h4>
<p>Click on <em>+</em> sign besides <em>Agent Job 1</em> which will open a list of tasks to add. Search for <em>gradle</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615791164173/gyuPP26ex.png" alt="Screenshot 2021-03-15 at 12.21.33 PM.png" /></p>
<p>Click on the <em>Add</em> button to add the task. We are going to turn off <em>Publish to Azure Pipeline</em> under <em>JUnit Test Results</em> section. You can keep it on as per the requirement. Secondly we are going to update the task name. Since we are going to make a release build update your task name to <em>buildRelease</em>. If you want to build using a particular flavour you can update your task name accordingly. e.g. if you are using dev as a flavour name update your task name as <em>buildDevRelease</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615791612569/BV3Cp9zWG.png" alt="Screenshot 2021-03-15 at 12.29.40 PM.png" /></p>
<h4 id="generate-aab-file">Generate .aab file</h4>
<p>Click on <em>+</em> sign besides <em>Agent Job 1</em> which will open a list of tasks to add. Search for <em>Command line</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615791823682/WHiS-S_xY.png" alt="Screenshot 2021-03-15 at 12.32.52 PM.png" /></p>
<p>In the <em>Script</em> section clear the field and set the below text.</p>
<pre><code>jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-<span class="hljs-number">256</span> -keystore $(KeyStoreFile.secureFilePath) -storepass $(StorePassword) -keypass $(KeyPassword) $(system.defaultworkingdirectory)/app/build/outputs/bundle/release/*.aab $(KeyStoreAlias)
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615792126778/-t-i347Rb.png" alt="Screenshot 2021-03-15 at 12.38.01 PM.png" /></p>
<p>There are 4 variables in the script out of which we have already set <em>KeyStoreFile.secureFilePath</em>. Time to set other 3 variables <em>StorePassword</em>, <em>KeyPassword</em> and <em>KeyStoreAlias</em>. Navigate to the <em>Variables</em> tab on the tap and add the variables and their values as shown in the screenshot below. Once you are sure that the values are correct you can click on the lock icon to secure it. Once you lock and save no one will be able to view it including you, so make sure you still have a backup of the details in case you want to use it somewhere else. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615792349336/KNI8PSJfW.png" alt="Screenshot 2021-03-15 at 12.41.51 PM.png" /></p>
<h4 id="create-an-artifact">Create an Artifact</h4>
<p>The above step creates an .aab file but still needs to be converted in to an artifact so that it is available to the CD pipeline for release. </p>
<p>By now you are a pro at adding tasks to <em>Agent Job 1</em>. Add the following two tasks to <em>Agent Job 1</em>: <em>Copy Files</em> &amp; <em>Publish build artifacts</em>. To avoid confusion of which tasks to select I have added the screenshots below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615792747164/Y94GDf8Q8.png" alt="Screenshot 2021-03-15 at 12.45.50 PM.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615792759586/d0thbCjlT.png" alt="Screenshot 2021-03-15 at 12.47.11 PM.png" /></p>
<p>Now select the <em>Copy Files</em> task and set <em>$(system.defaultworkingdirectory)</em> in the <em>Source Folder</em> field, <em>**/</em>.aab<em> in the </em>Contents<em> and </em>$(build.artifactstagingdirectory)<em> in the </em>Target Folder*.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615793022708/yQr053oUG.png" alt="Screenshot 2021-03-15 at 12.51.17 PM.png" /></p>
<p>Click on <em>Save &amp; queue</em> and click <em>Save</em> to save our CI pipeline. </p>
<p>That is it for the <em>Continuous Integration</em>. We will now head on to the <em>Continuous Deployment</em> to find out how to release the generated artifact to the <em>Internal Testing</em> channel on Play Console.</p>
<h2 id="continuous-deployment">Continuous Deployment</h2>
<p>While Continuous Integration deals with creating a build on the machine itself, <em>Continuous Deployment</em> is all about how will you publish that build to the Play Store, App Store, Self-Hosted or Cloud server. In order to deploy to the Play Store you will need a Google Play Console account and an app created in Play Console with the same bundle id. </p>
<p>Next, we will go back to our Azure DevOps project and select <em>Releases</em> under the pipeline section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615815732616/CpqYc6atx.png" alt="Screenshot 2021-03-15 at 7.10.31 PM.png" /></p>
<p>Since there is no pre-defined template for uploading a build to the Play Store, we are going to select an empty job. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615815828831/AFKJ5JX2h.png" alt="Screenshot 2021-03-15 at 7.11.37 PM.png" /></p>
<p>We will now enter a stage name for our deployment. You can name it whatever you want. I have kept it as <em>Play Store Deployment</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615815890805/7t7hSdvpm.png" alt="Screenshot 2021-03-15 at 7.11.57 PM.png" /></p>
<p>Let's connect the CI artifact with our current CD pipeline. Select <em>Add an artifiact</em> from the artifacts. This will open a dialog on the right to which we will select our artifact. Click the drop down for <em>Source (build pipeline)</em> and you will see <em>Android-CI</em> as one and only option (unless you have added more CI pipelines or altered the CI name).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615816184765/UzaJrhrnl.png" alt="Screenshot 2021-03-15 at 7.17.01 PM.png" /></p>
<p>Select the <em>Android-CI</em> and click <em>Add</em>. After adding the artifact our release pipeline will look something like below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615816296608/-UWZ1cL8D.png" alt="Screenshot 2021-03-15 at 7.21.09 PM.png" /></p>
<p>Since the CI &amp; CD pipelines are separate we should setup a way to automatically trigger the CD pipeline when the CI is successful. To achieve that select the lightning icon for the artifact we just added. This will open a dialog on the right which will have a setting to enable/disable the Continuous Deployment trigger. When the build trigger is enabled our release pipeline will automatically start when a new build is completed by our build (CI) pipeline. We are not going to enable the Pull Request trigger.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615816429023/QTk3KBBtF.png" alt="Screenshot 2021-03-15 at 7.23.25 PM.png" /></p>
<p>So far we have setup the pipeline to start automatically when a new build is available in our build pipeline. We have also setup a Play Store Deployment stage to which we will add tasks for the actual deployment in the next section.</p>
<h4 id="deployment-task">Deployment task</h4>
<p>Select 1 job 0 task hyperlink which will switch to the Tasks tab. Select the + plus besides the <em>Agent Job</em> to open a list of new tasks. When you search for <em>Google Play</em>. you will see <em>Google Play</em> by Microsoft. Install the item if not already installed. Once installed and when you search for Google App again you will see a few new tasks as below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615816686274/RNda-0A-h.png" alt="Screenshot 2021-03-15 at 7.26.05 PM.png" /></p>
<p>Select the task that says <strong>Google Play - Release Bundle</strong> and click the <em>Add</em> button (Don't select <em>Google Play - Release</em>). Some settings will need attention which we will take care of one-by-one. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615816994957/NwtgFmv-6.png" alt="Screenshot 2021-03-15 at 7.29.43 PM.png" /></p>
<h4 id="adding-a-service-connection">Adding a service connection</h4>
<p>Firstly we need to create a new service connection. Click on <em>New</em> button besides the <em>Service Connection</em> field. It will upon up a dialog which primarily asks for a <em>Service Account E-mail</em> and a <em>Private Key</em>. We are going to create a service account on Google Play Console to upload the build. This account will also have a private key attached to it for authentication purposes. </p>
<p>To create the account visit  <a target="_blank" href="https://play.google.com/apps/publish/">Google Play Console</a> and select <em>API Access</em> under <em>Developer Account</em> under <em>Settings</em>.  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615827994987/EE1HrSYG0.png" alt="Screenshot 2021-03-15 at 10.35.10 PM.png" /></p>
<p>Click <em>Create new service account</em> button which will pop open a dialog as below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615828385115/gPM9JVtGQ.png" alt="Screenshot 2021-03-15 at 10.41.55 PM.png" /></p>
<p>Click on the link in step 1 which will redirect you to a new tab of <em>Google Cloud Console</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615828571166/fadBRgZOL.png" alt="Screenshot 2021-03-15 at 10.44.29 PM.png" /></p>
<p>Click <em>+ CREATE SERVICE ACCOUNT</em> on the top to start creating a new account. In the newly opened page I have entered <em>Google Play Console</em> in the service account name but it can be whatever you want. Enter the <em>service account description</em> if needed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615829579772/JBPv6gysF.png" alt="Screenshot 2021-03-15 at 11.01.57 PM.png" /></p>
<p>Click <em>CREATE</em> to continue. It will then ask for what permissions do you want to allocate for the user. Click the Role drop down and search for <em>Owner</em>. Select the <em>Owner</em> with <em>Full access to all resources</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615829759679/x5JHSvIha.png" alt="Screenshot 2021-03-15 at 11.03.57 PM.png" /></p>
<p>Click the <em>Continue</em> button and select <em>Done</em> in step 3 since we don't want to add any users or groups who can perform actions as this service account. You will be redirected to the list of keys page. Now select the newly created account and go to the <em>Keys</em> tab. Here we are going to add a new <em>Private Key</em> for our service connection.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615830007987/MUGGpY8LB.png" alt="Screenshot 2021-03-15 at 11.08.09 PM.png" /></p>
<p>Select <em>Add Key</em> and from the drop down select <em>Create new key</em>. It will show a pop up of which type of key you want to create.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615830146027/rkJ-gVdcJ.png" alt="Screenshot 2021-03-15 at 11.10.22 PM.png" /></p>
<p>Select the <em>JSON</em> option and click <em>CREATE</em> button. This will ask you to download the file. Store it some place secure we are not going to use the whole file as it is, rather if you open the file it will have a key called <em>private_key</em>. </p>
<p>Before we use this value head back to our google play console's <em>API Access</em> page and click on <em>Done</em> button. It will refresh the list of service accounts with your service account added to the list.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615830416668/oSGDp3kF8.png" alt="Screenshot 2021-03-15 at 11.14.53 PM.png" /></p>
<p>For the newly created service account there will be <em>Grant access</em> button for that row. Click on the button which will open a page to invite the user. Don't get confused with the term <em>invite</em> since Google has probably reused a page to display the same information for the service account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615830603632/wv-i-CLbx.png" alt="Screenshot 2021-03-15 at 11.19.12 PM.png" /></p>
<p>Click on the <em>Invite User</em> on the bottom right and in the pop up dialog that comes up click on <em>Send Invite</em>. This action will redirect you to <em>Users and Permissions</em> page with the service account added as a new user. </p>
<p>We will now setup the <em>Service Email</em> and the <em>Private key</em> in Azure DevOps. First, copy the email of the newly created service account into the <em>Service Account E-mail</em> in the Azure DevOps tab. Next open the private key json file and copy the value of <em>private_key</em> to <em>Private Key</em> field. Here is what it looks like.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615830976190/qyhbEaLXn.png" alt="Screenshot 2021-03-15 at 11.25.44 PM.png" /></p>
<p>Click on <em>Save</em> which will add the service connection to the <em>Service connection</em> field. Next enter your bundle id in the <em>Application id (com.google.MyApp)</em> field. Finally in the <em>Bundle path</em> you can browse to select the .aab file by clicking the 3 dots on the right. Usually the aab will at location </p>
<pre><code>&lt;your CI <span class="hljs-type">name</span>&gt;/<span class="hljs-keyword">drop</span>/app/build/outputs/bundle/<span class="hljs-keyword">release</span><span class="hljs-comment">/*.aab</span>
</code></pre><p>If you haven't run a CI pipeline yet you can set the path as </p>
<pre><code>$(<span class="hljs-keyword">System</span>.DefaultWorkingDirectory)/_Android-CI/<span class="hljs-keyword">drop</span>/app/build/outputs/bundle/<span class="hljs-keyword">release</span><span class="hljs-comment">/*.aab</span>
</code></pre><p>If you are using a flavour prefix <em>release</em> with your flavour name in the path. e.g. the path should be </p>
<pre><code>$(<span class="hljs-keyword">System</span>.DefaultWorkingDirectory)/_Android-CI/<span class="hljs-keyword">drop</span>/app/build/outputs/bundle/devRelease<span class="hljs-comment">/*.aab</span>
</code></pre><p>You can select which track you want to launch to from the drop down.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615831411319/-V2no_q54.png" alt="Screenshot 2021-03-15 at 11.32.52 PM.png" /></p>
<p>That is all the settings you need. You can now <em>Save</em> the pipeline and run the CI pipeline to build an aab and upload it to the Play Console. </p>
<h2 id="conclusion">Conclusion</h2>
<p>This seems to be a long tutorial but once you are successful in setting up the CI/CD pipelines it would be a huge time saver for your developers. Secondly, it will run on the cloud so there is no need for a physical machine. You can trigger the build from your phone by logging in to Azure DevOps. Hope you have enjoyed this article and hope it helps others in your team. </p>
<p>To learn how to setup CI CD pipeline for iOS you can visit <a target="_blank" href="https://arjavdave.com/2021/03/11/continuous-integration-cicd-for-ios-on-azure-devops-part-1/">this tutorial</a>.</p>
<p>For any queries you can leave a comment below. Please do follow my page for many more upcoming tutorials.</p>
]]></content:encoded></item><item><title><![CDATA[Continuous Deployment: CI/CD for iOS on Azure DevOps (Part 2)]]></title><description><![CDATA[In the  last part we created a build using the pipeline feature of  Azure DevOps. In this tutorial we are going to use the Azure's Release pipeline to push the build to the App Store. This is the continuous deployment part of CI/CD.
Open your project...]]></description><link>https://blog.royalecheese.com/continuous-deployment-cicd-for-ios-on-azure-devops-part-2</link><guid isPermaLink="true">https://blog.royalecheese.com/continuous-deployment-cicd-for-ios-on-azure-devops-part-2</guid><category><![CDATA[ci-cd]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Devops]]></category><category><![CDATA[continuous deployment]]></category><category><![CDATA[ios apps]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Sat, 13 Mar 2021 13:43:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615632593200/6k7j1d6lW.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the  <a target="_blank" href="https://arjavdave.com/2021/03/11/continuous-integration-cicd-for-ios-on-azure-devops-part-1/">last part</a> we created a build using the pipeline feature of  <a target="_blank" href="https://dev.azure.com">Azure DevOps</a>. In this tutorial we are going to use the Azure's Release pipeline to push the build to the App Store. This is the continuous deployment part of CI/CD.</p>
<p>Open your project on DevOps and select "Releases" under the "Pipelines" section in the left panel. Select the "New  Pipeline"  which will open up a dialog for selecting a template. </p>
<h2 id="template-selection-and-setup">Template Selection &amp; Setup</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615636542998/I0-0EM6zU.png" alt="Screenshot 2021-03-13 at 4.41.25 PM.png" /></p>
<p>We are going to select an empty job, since there is no pre-defined template for uploading a build to the App Store.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615636550866/pHhBtw5_8.png" alt="Screenshot 2021-03-13 at 4.42.14 PM.png" /></p>
<p>Enter the stage name as "Deployment". It can be whatever you want but for clarification purposes I set it as Deployment.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615637104221/-wTGplkTS.png" alt="Screenshot 2021-03-13 at 4.42.30 PM.png" /></p>
<p>We will now select an Artifact which we want to upload. An artifact is something that is created whan an Azure Build Pipeline runs. So in our case the artifact is the one that is generated from our previous tutorial.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615638284165/wgsslrx8p.png" alt="Screenshot 2021-03-13 at 5.52.54 PM.png" /></p>
<p>Click the <em>Add</em> button besides the <em>Artifacts</em> label or click on <em>Add an artifact</em>. This will open a new dialog on the right.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615638419126/HX3rreArV.png" alt="Screenshot 2021-03-13 at 5.54.56 PM.png" /></p>
<p>In the drop down you will see our CI setup from the last tutorial. Select that and click the <em>Add</em> button.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615638572412/rNnxztrXf.png" alt="Screenshot 2021-03-13 at 5.59.06 PM.png" /></p>
<p>After  adding the artifact our release pipeline will look something like below. 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615638586493/ds7BJ1WnQ.png" alt="Screenshot 2021-03-13 at 5.59.16 PM.png" /></p>
<p>Now select the <em>lightning</em> icon for the artifact we just added. This will open a dialog on the right which will have a setting to enable/disable the Continuous Deployment trigger. When the build trigger is enabled our release pipeline will automatically start when a new build is completed by our build (CI) pipeline. We are not going to enable the Pull Request trigger.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615638873150/gt8YWkwn2.png" alt="Screenshot 2021-03-13 at 6.00.48 PM.png" /></p>
<p>So far we have setup the pipeline to start automatically when a new build is available in our build pipeline. We have also setup a <em>Deployment</em> stage to which we will add tasks for the actual deployment in the next section.</p>
<h2 id="deployment-task">Deployment task</h2>
<p>Select <em>1 job 0 task</em> hyperlink which will switch to the Tasks tab. Select the + plus besides the <em>Agent Job</em> to open a list of new tasks. Search for <em>Apple App Store</em>. If you don't see any tasks you will have to install the application from the Marketplace. Once installed when you search for <em>Apple App Store</em> again you will see a couple of new tasks as below. 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615639911817/BaqORyw05.png" alt="Screenshot 2021-03-13 at 6.20.37 PM.png" /></p>
<p>Add the <em>Apple App Store Release</em> task. It will add the task which requires a <em>Service Connection</em>. A service connection is basically a connection to the Apple's itunesconnect website.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615640180048/SX-YoJ0Eh.png" alt="Screenshot 2021-03-13 at 6.23.50 PM.png" /></p>
<p>We will now add a new service connection. Click on the <em>New</em> button besides Service Connection which will open a new dialog. In the new dialog enter the email &amp; password. Even though the <em>app-specific password</em> and <em>Fastlane Session</em> are marked as optional they are mandatory to automate the process of uploading the build to App Store. </p>
<h2 id="app-specific-password">App-Specific Password</h2>
<p>To create <em>app-specific password</em> visit https://appleid.apple.com/account/manage in a new tab and scroll down to the security section. Click on the <em>Generate password...</em> link below the label <em>APP-SPECIFIC PASSWORDS</em> which will open a dialog. Enter any name for the label and click <em>Create</em>. Copy the generated text and switch back to our Azure DevOps tab and paste it under App-specific password field.</p>
<h2 id="fastlane-session-token">Fastlane Session token</h2>
<p>As a prerequisite you should already have the fastlane installed on your machine. Fastlane is required to generate the session token so that our cloud machine can login automatically without our intervention. Time to open the terminal and fire some commands. </p>
<pre><code><span class="hljs-attribute">fastlane</span> spaceship -u xxxxxxx<span class="hljs-variable">@gmail</span>.com
</code></pre><p>You will will be asked for your password and then a 6 digit code as a part of two factor authentication. It will generate the session token for you and will ask if you want to copy it. Press 'y' and then switch to your Azure DevOps tab and paste the session token in the <em>Fastlane Session</em> field. </p>
<p>Enter the service connection name as per your preference and the description if you need it. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615641290599/x-oRECf6h.png" alt="Screenshot 2021-03-13 at 6.44.23 PM.png" /></p>
<h2 id="publish-details">Publish details</h2>
<p>You will see the service connection now set. Next enter your app's bundle id. Select <em>Skip Build Processing Wait</em> and <em> Skip Submission</em> so that our pipeline doesn't wait for the build to be made available, otherwise it will consume precious build minutes. Lastly enter the <em>App Specific Apple Id</em> which you can get by logging into <a target="_blank" href="https://appstoreconnect.apple.com">https://appstoreconnect.apple.com</a> and clicking your App to view it's details and then finally visiting <em>App Information</em> under <em>General</em> section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615641663902/o9ssM45eq.png" alt="Screenshot 2021-03-13 at 6.50.41 PM.png" /></p>
<p>That is it guys. Your hard work will finally pay off when you run your pipeline and see the green ticks going one by one as the tasks are getting completed. This would be one of the best feeling in the world.</p>
<p>Do you have any suggestions, optimisation and/or just want to connect let me know in the comments below. </p>
]]></content:encoded></item><item><title><![CDATA[Continuous Integration: CI/CD for iOS on Azure DevOps (Part 1)]]></title><description><![CDATA[DevOps & CI/CD are buzz word for a while now and they have really proven their value in todays' fast moving world and Agile development process. One understands the true value only when they have actually been a process of it and see for themselves t...]]></description><link>https://blog.royalecheese.com/continuous-integration-cicd-for-ios-on-azure-devops-part-1</link><guid isPermaLink="true">https://blog.royalecheese.com/continuous-integration-cicd-for-ios-on-azure-devops-part-1</guid><category><![CDATA[ci-cd]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Devops]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Apple]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Thu, 11 Mar 2021 13:44:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615439672831/_I12DqKkq.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>DevOps &amp; CI/CD are buzz word for a while now and they have really proven their value in todays' fast moving world and Agile development process. One understands the true value only when they have actually been a process of it and see for themselves the immense amount of time and headache it saves. </p>
<p>At  <a target="_blank" href="https://www.royalecheese.com">Royale Cheese</a> initially we had setup CI/CD for iOS via Microsoft's Visual Studio App Center, but last year they declared the  <a target="_blank" href="https://devblogs.microsoft.com/appcenter/app-center-mbaas-retirement/">retirement of MBaas</a> which got us worried about the overall future of VS App Center. That was one of the reasons we wanted to switch away from it. Secondly, the free tier provided around 400 minutes of build time per month per account which would had been sufficient for other technologies, but iOS takes around 15 minutes to create a single build and deploy. So having multiple apps (both iOS and Android) in the same account didn't fare well.</p>
<p>We were already using <a target="_blank" href="https://dev.azure.com/">Azure DevOps</a> for CI/CD for other technologies and it seemed promising. It's future plan had no issues of shutting down and it provided around 1800 minutes of free build time. We decided to give it a try and so far don't have any complaints. We have started loving and adoring Azure DevOps and hope it stays this way.</p>
<p>Alright! Enough with the issues, let's see how we can get the build &amp; deployment going.</p>
<h4 id="prerequisites">Prerequisites</h4>
<ul>
<li>Paid Apple developer account, an app added to appstoreconnect.apple.com &amp; an identifier added to the <a target="_blank" href="https://developer.apple.com/account/resources/identifiers/list">developer account</a>.</li>
<li>Azure DevOps account and an iOS repository created</li>
<li><a target="_blank" href="https://fastlane.tools/">fastlane</a> installed locally (for generating session token)</li>
</ul>
<p>Since for iOS CI and CD are two separate and lengthy processes we will divide the tutorial in two parts. Part 1: Continuous Integration and Part 2: Continuous Deployment</p>
<h4 id="continuous-integration">Continuous Integration</h4>
<p>Let's start with CI part of CI/CD. In this section we will create a build using the pipeline feature. For demo purposes I have created a project named 'iOS' in Azure DevOps. Open your project and navigate to "Pipelines" on the left panel as in the screenshot below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615444450764/D6l6RWO_y.png" alt="Screenshot 2021-03-11 at 12.01.24 PM.png" /></p>
<p>Click on "Create Pipeline" which will open the below page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615444514171/0smxK6t_0.png" alt="Screenshot 2021-03-11 at 12.01.50 PM.png" /></p>
<p>Here we are going to select "Use the classic editor" which is easier than the other options. The other options will eventually lead to writing yml files for configuring your pipelines.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615444721941/gm7vqOjW4.png" alt="Screenshot 2021-03-11 at 12.06.39 PM.png" /></p>
<p>Select your branch from you which you want to ru the build.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615444814490/Cf30x2aSD.png" alt="Screenshot 2021-03-11 at 12.06.50 PM.png" /></p>
<p>Select "Xcode" from the list of featured items and click Apply.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615445063492/S8c8UgvNU.png" alt="Screenshot 2021-03-11 at 12.12.35 PM.png" /></p>
<p>This will automatically create a build configuration with some basic settings, but we are still a long way. For simplicity we are not going to use "Xcode test" so you can right click on it and select "Remove selected tasks". Also since we are not going to use VS App Center you can remove those tasks as well. We will be distributing our app on TestFlight. So enable the Apple Certificate &amp; Apple Provisioning Profile tasks. It will show some error but we will get there. Our pipeline page will look like below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615445265747/cecilbUpJ.png" alt="Screenshot 2021-03-11 at 12.17.11 PM.png" /></p>
<h4 id="apple-certificate">Apple Certificate</h4>
<p>Let's get the Apple .p12 certificate which is used for signing the build. <strong>On your Mac</strong> open Keychain app and in the top bar select Keychain Access -&gt; Certificate Assistant -&gt; Request a Certificate from Certificate Authority... Enter the information for the certificate and select "Saved to disk" and continue. It will ask you to save a CertificateSigningRequest.certSigningRequest file. Save it some place accessible on your machine. </p>
<p>We will now add it the above generated file in your apple account. Visit <a target="_blank" href="https://developer.apple.com/account/resources/certificates/add">this page</a> from where we will be adding the certificate. Select "Apple Distribution" and continue. The next two pages are self explanatory (see the screenshots below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615446192297/FraCJa9Uz.png" alt="Screenshot 2021-03-11 at 12.31.42 PM.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615446202090/xlvX1ji9n.png" alt="Screenshot 2021-03-11 at 12.31.58 PM.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615449184442/yGd5W6oL4.png" alt="Screenshot 2021-03-11 at 1.21.15 PM.png" /></p>
<p>Download the certificate file as "distribution.cer". Click on this file which will open the Keychain access and add the certificate file to your Keychain. You can find an entry "Apple Distribution: Your account name" in your Keychain certificates.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615446886635/G5doX8i4F.png" alt="Screenshot 2021-03-11 at 12.35.43 PM.png" /></p>
<p>Right click on it and select "Export "Apple Distrubtion: name". Save the file as .p12 with a strong password. We have successfully generated the .p12 file required for signing the build.</p>
<h4 id="apple-provisioning-profile">Apple Provisioning Profile</h4>
<p>For this you need to have an identifier added to your developer account. Check prerequisites. Visit <a target="_blank" href="https://developer.apple.com/account/resources/profiles/list">your profiles</a> and create a new profile.
Select App Store in the distribution section and continue. Select your App ID and continue. Select the certificate which we uploaded in the above step and click continue. Enter the name of your provisioning profile and click "Generate". This will download the provisioning profile on your machine.</p>
<h4 id="back-to-devops">Back to DevOps</h4>
<p>Phew! that was a long process. Okay, going back to DevOps let's upload the files to their respective tasks. 
Select the "Install an Apple Certificate" task and click on the gear icon on the right besides the "Certificate (P12)" field. It will ask you to upload the p12 file which we generated above. Once set it will look like below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615447634837/3Wxnz6ao8.png" alt="Screenshot 2021-03-11 at 12.56.48 PM.png" /></p>
<p>Select "Variables" from the top tabs. The variables really come in handy when you want to store keys, secrets or passwords securely. There will already be an entry for your P12password variable. Enter your password in Value and make sure to lock the icon so that you or other people cannot see it once you save the build. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615447769041/-qU_k7hsP.png" alt="Screenshot 2021-03-11 at 12.54.54 PM.png" /></p>
<p>Time to add the provisioning profile. Go back to tasks tab and select "Install an Apple provisioning profile task". Upload your provisioning profile which will now remove any error. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615447981448/pEBy4KGPP.png" alt="Screenshot 2021-03-11 at 12.56.48 PM.png" /></p>
<p>Since we want to sign the build we need to tell the "Xcode build" task to manually sign it using the certificate and provisioning profile we just uploaded. In order to do so select the "Xcode build" task and select "Manual Signing" under "Signing &amp; Provisioning". This will display 3 fields. Enter <strong>$(APPLE_CERTIFICATE_SIGNING_IDENTITY)</strong> in "Signing Identity" field and <strong>$(APPLE_PROV_PROFILE_UUID)</strong> in Provisioning Profile UUID field. Finally select the checkbox "Create app package".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615448942949/AEO9Ejr6n.png" alt="Screenshot 2021-03-11 at 1.18.20 PM.png" /></p>
<p>Lastly if you are using pods in your project you need to add a CocoaPods task in your pipeline on top. Select the + icon besides "Agent Job 1" to add a new task. Search for CocoaPods and click Add. Drag the task above "Install an Apple Certificate" so that it is the first task that is being executed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615448955082/z2enNTg4C.png" alt="Screenshot 2021-03-11 at 1.18.41 PM.png" /></p>
<p>You can now save and queue your build by clicking "Save &amp; queue" button. Make sure to select Mac OS in the Agent Specification other wise it will give an error. </p>
<p>In the  <a target="_blank" href="https://arjavdave.com/2021/03/13/continuous-deployment-ci-cd-for-ios-on-azure-devops-part-2/">next part</a>  we will have a look at how to setup Continuous Deployment to the TestFlight using release pipelines.</p>
]]></content:encoded></item><item><title><![CDATA[SSL Advanced Configuration: NGINX on MAC (Part 4)]]></title><description><![CDATA[The previous 3 parts are available here:  Installing Nginx,  Simple Configuration &  Self-Signed SSL. 
In this part we will continue our quest to a more better and secure Nginx configuration by setting directives that will help with security & perfor...]]></description><link>https://blog.royalecheese.com/ssl-advanced-configuration-nginx-on-mac-part-4</link><guid isPermaLink="true">https://blog.royalecheese.com/ssl-advanced-configuration-nginx-on-mac-part-4</guid><category><![CDATA[SSL]]></category><category><![CDATA[nginx]]></category><category><![CDATA[Security]]></category><category><![CDATA[https]]></category><category><![CDATA[Cryptography]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Tue, 09 Mar 2021 10:26:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615273634420/PARazUSeS.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The previous 3 parts are available here:  <a target="_blank" href="https://blog.royalecheese.com/installing-nginx-on-mac-part-1">Installing Nginx</a>,  <a target="_blank" href="https://blog.royalecheese.com/simple-configuration-of-nginx-on-mac-part-2">Simple Configuration</a> &amp;  <a target="_blank" href="https://blog.royalecheese.com/self-signed-ssl-nginx-on-mac-part-3">Self-Signed SSL</a>. </p>
<p>In this part we will continue our quest to a more better and secure Nginx configuration by setting directives that will help with security &amp; performance optimisations.</p>
<p>Here is the file from previous tutorial. We will be adding directives inside the HTTPS server context.</p>
<pre><code><span class="hljs-section">events</span> {

}

<span class="hljs-section">http</span> {
    <span class="hljs-comment"># HTTP server</span>
    <span class="hljs-section">server</span> {
        <span class="hljs-attribute">listen</span>       <span class="hljs-number">80</span>;
        <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> https://localhost:443;
    }


    <span class="hljs-comment"># HTTPS server</span>
    <span class="hljs-section">server</span> {
       <span class="hljs-attribute">listen</span>       <span class="hljs-number">443</span> ssl;

       <span class="hljs-attribute">ssl_certificate</span> /usr/local/etc/ssl/certs/self-signed.crt;
       <span class="hljs-attribute">ssl_certificate_key</span> /usr/local/etc/ssl/private/self-signed.key;

       <span class="hljs-attribute">location</span> / {
           <span class="hljs-attribute">root</span>   /Users/arjav/Desktop/www;
           <span class="hljs-attribute">index</span>  index.html index.htm;
       }
    }
}
</code></pre><h4 id="enable-only-new-tls-versions">Enable only new TLS versions</h4>
<p>Transport Layer Security (TLS) are a set of cyrptographic protocols to communicate securely over a computer network. TLS v1.1 and older uses  <a target="_blank" href="https://en.wikipedia.org/wiki/Cipher_suite">cipher suites</a>  that are insecure in today's world. So let's just enable TLS 1.2 &amp; 1.3. Add the new directive just below <code>listen 443 ssl;</code></p>
<pre><code><span class="hljs-attribute">ssl_protocols</span> TLSv<span class="hljs-number">1</span>.<span class="hljs-number">2</span> TLSv<span class="hljs-number">1</span>.<span class="hljs-number">3</span>;
</code></pre><h4 id="server-ciphers">Server ciphers</h4>
<p>TLSv1.2  and TLSv1.3 have secure enough cipher suites. So it would be okay to have the below option as off. What it basically says is allow the client to select the cipher algorithm that is best for itself but defined in TLSv1.2 and TLSv1.3 only.  </p>
<pre><code><span class="hljs-attribute">ssl_prefer_server_ciphers</span> <span class="hljs-literal">off</span>;
</code></pre><p>In case you are as skeptical as me and want to allow a specific cipher suites only, you can turn it on as below and mention the list of ciphers. Do remember that the order of the cipher matters and the client will make selection in that order. </p>
<pre><code><span class="hljs-attribute">ssl_prefer_server_ciphers</span> <span class="hljs-literal">on</span>;
<span class="hljs-attribute">ssl_ciphers</span> <span class="hljs-string">"EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"</span>;
</code></pre><h4 id="ocsp-stapling">OCSP Stapling</h4>
<p>When a client communicates with a server it needs to verify the server certificate either via Certificate Revocation List (CRL) or an Online Certificate Status Protocol (OCSP). The CRL process is bandwidth consuming and resource heavy while OCSP is much more light weight but with its own issues. Since we are using a self-signed certificate on a local machine it won't be an issue, but for 3rd party certificates all the intermediate certificates needs to be present in your ssl certificate. This can alleviated by using <code>ssl_trusted_certificate</code> directive. You can read more about it <a target="_blank" href="https://raymii.org/s/tutorials/OCSP_Stapling_on_nginx.html">here</a>.</p>
<pre><code><span class="hljs-attribute">ssl_stapling</span> <span class="hljs-literal">on</span>;
<span class="hljs-attribute">ssl_stapling_verify</span> <span class="hljs-literal">on</span>;
</code></pre><h4 id="diffie-hellman-parameters">Diffie-Hellman Parameters</h4>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">Diffie-Hellman</a> is one of the most common key exchange used in <a target="_blank" href="https://en.wikipedia.org/wiki/Forward_secrecy">Perfect Forward Secrecy (PFS)</a>. Open terminal and run the below command to generate parameters for Diffie-Hellman key exchange. Note: It will take a long time to generate the parameters.</p>
<pre><code>sudo openssl dhparam -<span class="hljs-keyword">out</span> /usr/<span class="hljs-keyword">local</span>/etc/ssl/certs/dhparam.pem <span class="hljs-number">4096</span>
</code></pre><p>Once generated you can use these parameters by adding the following directive in Nginx</p>
<pre><code>ssl_dhparam /usr/<span class="hljs-keyword">local</span>/etc/ssl/certs/dhparam.pem;
</code></pre><h4 id="response-headers">Response Headers</h4>
<p>The below directive tells the client that you are allowed to contact the server only via secure HTTPS protocol. Our server is like: <code>Till 'max-age' time passes you cannot contact me via un-secure channels.</code>. If you don't want to include subdomains you can remove <code>includeSubdomains</code></p>
<pre><code>add_header <span class="hljs-keyword">Strict</span>-Transport-<span class="hljs-keyword">Security</span> "max-age=63072000; includeSubdomains" <span class="hljs-keyword">always</span>;
</code></pre><p>The next directive is used when you don't want to allow your side to be embedded in an iFrame. For more frame options you can visit <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options">mozilla's site</a>.</p>
<pre><code>add_header X-Frame-<span class="hljs-keyword">Options</span> DENY;
</code></pre><p>Lastly we disable <a target="_blank" href="https://en.wikipedia.org/wiki/Content_sniffing">Content or MIME sniffing</a> by applying the below directive. It is one of the key things for avoiding XSS attacks. Also if you will get your site audited it is one of the response headers that needs to be set. </p>
<pre><code>add_header X-Content-<span class="hljs-keyword">Type</span>-<span class="hljs-keyword">Options</span> nosniff;
</code></pre><p>There are a lot of other response headers you can set to optimise the website and increase security. For further details Mozilla has great resources for the same: <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers</a>.</p>
<h4 id="session-caching">Session Caching</h4>
<p>Caching a TLS connection is helpful to avoid unnecessary handshakes in turn increasing your performance of the site. The caching can be set with the help of below directives.</p>
<pre><code><span class="hljs-selector-tag">ssl_session_cache</span> <span class="hljs-selector-tag">shared</span><span class="hljs-selector-pseudo">:SSL</span><span class="hljs-selector-pseudo">:10m</span>;
<span class="hljs-selector-tag">ssl_session_timeout</span> <span class="hljs-selector-tag">1h</span>;
</code></pre><p>The directives say that the session cache is shared among all worker processes and can use upto 10MB of space. The time out for each session is 1h.</p>
<h4 id="conclusion">Conclusion</h4>
<p>These SSL directives will make your website secure and perform better. Our final nginx.conf file will be as below:</p>
<pre><code><span class="hljs-section">events</span> {

}

<span class="hljs-section">http</span> {
    <span class="hljs-comment"># HTTP server</span>
    <span class="hljs-section">server</span> {
        <span class="hljs-attribute">listen</span>       <span class="hljs-number">80</span>;
        <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> https://localhost:443;
    }


    <span class="hljs-comment"># HTTPS server</span>
    <span class="hljs-section">server</span> {
       <span class="hljs-attribute">listen</span>       <span class="hljs-number">443</span> ssl;

       <span class="hljs-attribute">ssl_certificate</span> /usr/local/etc/ssl/certs/self-signed.crt;
       <span class="hljs-attribute">ssl_certificate_key</span> /usr/local/etc/ssl/private/self-signed.key;

      <span class="hljs-comment"># Enable only TLSv1.2 TLSv1.3;</span>
      <span class="hljs-attribute">ssl_protocols</span> TLSv1.<span class="hljs-number">2</span> TLSv1.<span class="hljs-number">3</span>;

      <span class="hljs-comment"># Enable cipher suites</span>
      <span class="hljs-attribute">ssl_prefer_server_ciphers</span> <span class="hljs-literal">on</span>;
      <span class="hljs-attribute">ssl_ciphers</span> <span class="hljs-string">"EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"</span>;

      <span class="hljs-comment"># Enable OCSP stapling</span>
      <span class="hljs-attribute">ssl_stapling</span> <span class="hljs-literal">on</span>;
      <span class="hljs-attribute">ssl_stapling_verify</span> <span class="hljs-literal">on</span>;


      <span class="hljs-comment"># Set DH parameters</span>
      <span class="hljs-attribute">ssl_dhparam</span> /usr/local/etc/ssl/certs/dhparam.pem;


      <span class="hljs-comment"># Add Response Headers</span>
      <span class="hljs-attribute">add_header</span> Strict-Transport-Security <span class="hljs-string">"max-age=63072000; includeSubdomains"</span> always;
      <span class="hljs-attribute">add_header</span> X-Frame-Options DENY;
      <span class="hljs-attribute">add_header</span> X-Content-Type-Options <span class="hljs-string">"nosniff"</span>;

      <span class="hljs-comment"># Cache Connections</span>
      <span class="hljs-attribute">ssl_session_cache</span> shared:SSL:<span class="hljs-number">10m</span>;

       <span class="hljs-attribute">location</span> / {
           <span class="hljs-attribute">root</span>   /Users/arjav/Desktop/www;
           <span class="hljs-attribute">index</span>  index.html index.htm;
       }
    }
}
</code></pre><p>PS: Since Nginx is cross-platform these directives can work on any OS. Just make sure that you select the paths specific to OS.</p>
<p>That concludes our 4 part series of NGINX on MAC. Hope you have enjoyed it. If you are looking for installing &amp; configuring Nginx on Linux to host a laravel project digital ocean has a nice tutorial <a target="_blank" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-laravel-with-nginx-on-ubuntu-20-04">Installing &amp; Configuring Laravel</a>. Make sure to go through the pre-requisites.</p>
]]></content:encoded></item><item><title><![CDATA[Self-Signed SSL: Nginx on Mac (Part 3)]]></title><description><![CDATA[Till now, we have  installed Nginx and did a  simple configuration to host an html file locally.
In this part we will be configuring Nginx with a self-signed certificate. We will be creating a self signed certificate using openssl and make Nginx use ...]]></description><link>https://blog.royalecheese.com/self-signed-ssl-nginx-on-mac-part-3</link><guid isPermaLink="true">https://blog.royalecheese.com/self-signed-ssl-nginx-on-mac-part-3</guid><category><![CDATA[SSL]]></category><category><![CDATA[https]]></category><category><![CDATA[macOS]]></category><category><![CDATA[nginx]]></category><category><![CDATA[web servers]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Sun, 07 Mar 2021 12:45:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615106685901/-15DTOtUY.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Till now, we have  <a target="_blank" href="https://blog.royalecheese.com/installing-nginx-on-mac-part-1">installed Nginx</a> and did a  <a target="_blank" href="https://blog.royalecheese.com/simple-configuration-of-nginx-on-mac-part-2">simple configuration</a> to host an html file locally.</p>
<p>In this part we will be configuring Nginx with a self-signed certificate. We will be creating a self signed certificate using openssl and make Nginx use it for serving content over https. Let's get our hands dirty. Open our pal, Terminal and lets create a couple of folders to store our key and certificate. Fire the following commands:</p>
<pre><code><span class="hljs-keyword">mkdir</span> -p /usr/<span class="hljs-keyword">local</span>/etc/ssl/private
<span class="hljs-keyword">mkdir</span> -p /usr/<span class="hljs-keyword">local</span>/etc/ssl/certs
</code></pre><p>Ideally you can create these folders anywhere but it's a good practice to have them at the above given path. We will now create key and certificate by running the below command:</p>
<pre><code>sudo openssl req \
  -x509 -nodes -days <span class="hljs-number">365</span> -newkey rsa:<span class="hljs-number">2048</span> \
  -keyout /usr/local/etc/ssl/private/<span class="hljs-keyword">self</span>-<span class="hljs-keyword">signed</span>.key \
  -<span class="hljs-keyword">out</span> /usr/local/etc/ssl/certs/<span class="hljs-keyword">self</span>-<span class="hljs-keyword">signed</span>.crt
</code></pre><p>Let's alter our server context from previous tutorial. The updated file is as below.</p>
<pre><code>events {

}


http {
    <span class="hljs-keyword">server</span> {
        # <span class="hljs-keyword">Listen</span> <span class="hljs-keyword">on</span> port <span class="hljs-number">80</span> which <span class="hljs-keyword">is</span> the <span class="hljs-keyword">default</span> http port
        <span class="hljs-keyword">listen</span> <span class="hljs-number">80</span>;

        # <span class="hljs-keyword">Set</span> a permanent redirection <span class="hljs-keyword">from</span> http <span class="hljs-keyword">to</span> https
        <span class="hljs-keyword">return</span> <span class="hljs-number">301</span> https://localhost:<span class="hljs-number">443</span>;
    }
}
</code></pre><p>Add another server context inside http context with configuration and locations relating to SSL</p>
<pre><code> <span class="hljs-keyword">server</span> {
       <span class="hljs-keyword">listen</span>       <span class="hljs-number">443</span> ssl;

       # <span class="hljs-keyword">location</span> <span class="hljs-keyword">of</span> ssl certificate
       ssl_certificate /usr/<span class="hljs-keyword">local</span>/etc/ssl/certs/self-signed.crt;

       # <span class="hljs-keyword">location</span> <span class="hljs-keyword">of</span> ssl key
       ssl_certificate_key /usr/<span class="hljs-keyword">local</span>/etc/ssl/private/self-signed.key;
    }
</code></pre><p>Add location context inside the ssl server context</p>
<pre><code><span class="hljs-keyword">location</span> / {
    root   /Users/arjav/Desktop/www;
    <span class="hljs-keyword">index</span>  <span class="hljs-keyword">index</span>.html <span class="hljs-keyword">index</span>.htm;
}
</code></pre><p>This is the whole configuration file:</p>
<pre><code><span class="hljs-section">events</span> {

}

<span class="hljs-section">http</span> {
    <span class="hljs-comment"># HTTP server</span>
    <span class="hljs-section">server</span> {
        <span class="hljs-attribute">listen</span>       <span class="hljs-number">80</span>;
        <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> https://localhost:443;
    }


    <span class="hljs-comment"># HTTPS server</span>
    <span class="hljs-section">server</span> {
       <span class="hljs-attribute">listen</span>       <span class="hljs-number">443</span> ssl;

       <span class="hljs-attribute">ssl_certificate</span> /usr/local/etc/ssl/certs/self-signed.crt;
       <span class="hljs-attribute">ssl_certificate_key</span> /usr/local/etc/ssl/private/self-signed.key;

       <span class="hljs-attribute">location</span> / {
           <span class="hljs-attribute">root</span>   /Users/arjav/Desktop/www;
           <span class="hljs-attribute">index</span>  index.html index.htm;
       }
    }
}
</code></pre><p>As a last step we will need to add the self-signed certificate to the system keychain. Run the below command in your terminal.</p>
<pre><code>sudo <span class="hljs-keyword">security</span> <span class="hljs-keyword">add</span>-<span class="hljs-keyword">trusted</span>-cert \
  -d -r trustRoot \
  -k /Library/Keychains/<span class="hljs-keyword">System</span>.keychain /usr/<span class="hljs-keyword">local</span>/etc/ssl/certs/self-signed.crt
</code></pre><p>Voila! That's it. In your terminal verify your configuration file by running <code>nginx -t</code> and if everything looks okay reload your Nginx server by running <code>nginx -s reload</code>.
Visit  <a target="_blank" href="https://127.0.0.1">https://127.0.0.1</a>. You will still see a red flag or "Not secure" sign in your browser saying that your certificate is invalid, but that it's because not signed by a third-part authority. Rest assured the content is served over secure channels.</p>
<p>In the <a target="_blank" href="https://blog.royalecheese.com/ssl-advanced-configuration-nginx-on-mac-part-4">next chapter</a> we will look at some advanced ssl configuration options for better security, caching and optimisation.</p>
]]></content:encoded></item><item><title><![CDATA[Simple Configuration of NGINX on Mac: Part (2)]]></title><description><![CDATA[This is the 2nd part in the series of NGINX on Mac. You can visit the 1st part  here. In this part we are going to understand the configuration file of Nginx and tweak as per our requirements.
Configuration file
Your Nginx configuration file will be ...]]></description><link>https://blog.royalecheese.com/simple-configuration-of-nginx-on-mac-part-2</link><guid isPermaLink="true">https://blog.royalecheese.com/simple-configuration-of-nginx-on-mac-part-2</guid><category><![CDATA[HTML]]></category><category><![CDATA[nginx]]></category><category><![CDATA[web servers]]></category><category><![CDATA[macOS]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Sun, 07 Mar 2021 07:02:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615096299392/0OkG0ciVE.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the 2nd part in the series of NGINX on Mac. You can visit the 1st part  <a target="_blank" href="https://blog.royalecheese.com/installing-nginx-on-mac-part-1">here</a>. In this part we are going to understand the configuration file of Nginx and tweak as per our requirements.</p>
<h3 id="configuration-file">Configuration file</h3>
<p>Your Nginx configuration file will be located at <code>/usr/local/etc/nginx/nginx.conf</code>. You can fire <code>nginx -t</code> to run a test configuration just to verify the location. Open the nginx.conf file to see it's contents. I usually use Microsoft's VS Code to edit any text files, so I ran the below command to open the file in VS Code.  You can use any text editor you want.</p>
<pre><code>code /usr/<span class="hljs-keyword">local</span>/etc/nginx.conf
</code></pre><p>Once open you will see a the config file already has content in it most of which is commented out. You can clear everything so that we can take one step at a time. Run <code>nginx -t</code> to test the configuration file we just cleared out. It will give an error saying <code>no "events" section in configuration</code> which simply means there is no events context. Add the events context as follows:</p>
<pre><code><span class="hljs-selector-tag">events</span> {

}
</code></pre><p>The events context helps you with configuration of the server like how many worker connections can be open at the same time and how polling happens among them. </p>
<p>Now when you run <code>nginx -t</code> you will not get any error but when you open  <a target="_blank" href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> you will be faced with <code>This site can’t be reached</code> error since we have not set our server locations yet. Time to do that! </p>
<p>Below your events context add another context with name <code>http</code>. Inside http context add context with name <code>server</code>. Finally, inside server context add context named <code>location</code>. It should look like below.</p>
<pre><code><span class="hljs-section">events</span> {
}

<span class="hljs-section">http</span> {
    <span class="hljs-section">server</span> {
        <span class="hljs-comment"># We are configuring for the / location</span>
        <span class="hljs-attribute">location</span> / {

        }
    }    
}
</code></pre><p>We have created a skeleton inside which we will now put some flesh and bones. Add a <code>root</code> directive inside the location context with a path pointing to your html directory. For demo purposes I have created a <code>www</code> folder on my Desktop. You can place your folder where ever you like. In the folder I have created an index.html with content <code>&lt;h1&gt;HELLO WORLD&lt;/h1&gt;</code>.  Here is the overall config file</p>
<pre><code>events {
}

http {
    <span class="hljs-keyword">server</span> {
        # <span class="hljs-keyword">listen</span> <span class="hljs-keyword">on</span> the port <span class="hljs-number">8080</span>
        <span class="hljs-keyword">listen</span> <span class="hljs-number">8080</span>;

        # <span class="hljs-keyword">When</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">8080</span> <span class="hljs-keyword">is</span> visited, serve content <span class="hljs-keyword">from</span> www <span class="hljs-keyword">on</span> Desktop
        <span class="hljs-keyword">location</span> / {
            root /Users/arjav/Desktop/www;
        }
    }    
}
</code></pre><p>That's it!!! This is the simplest configuration you can have in nginx. From your terminal run <code>nginx -s reload</code> to reload nginx and visit <a target="_blank" href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> in your browser and you will see a nice big HELLO WORLD. Press Cmd+Shift+R to hard refresh your browser in case you are seeing some cached content.</p>
<p>Next -&gt;  <a target="_blank" href="https://blog.royalecheese.com/self-signed-ssl-nginx-on-mac-part-3">Configure self-signed SSL</a>. </p>
]]></content:encoded></item><item><title><![CDATA[Installing NGINX on MAC (Part 1)]]></title><description><![CDATA[Nginx is one of the most widely used web servers in the world. In addition to being a web server it has also become very popular for reverse proxy, HTTP Caching & Load Balancing. 
This is a multipart part series where we will be installing, configuri...]]></description><link>https://blog.royalecheese.com/installing-nginx-on-mac-part-1</link><guid isPermaLink="true">https://blog.royalecheese.com/installing-nginx-on-mac-part-1</guid><category><![CDATA[nginx]]></category><category><![CDATA[macOS]]></category><category><![CDATA[web servers]]></category><category><![CDATA[Homebrew]]></category><dc:creator><![CDATA[Arjav Dave]]></dc:creator><pubDate>Sun, 07 Mar 2021 05:35:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615095181363/tGXqw3r_E.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Nginx is one of the most widely used web servers in the world. In addition to being a web server it has also become very popular for reverse proxy, HTTP Caching &amp; Load Balancing. </p>
<p>This is a multipart part series where we will be installing, configuring and deploying Nginx on a local machine with Mac OS installed as it were a production machine. </p>
<p>Let's start with Part 1: Installing Nginx. </p>
<h3 id="installing-homebrew">Installing Homebrew</h3>
<p>We will be using Homebrew to install Nginx. To install Homebrew open your terminal and fire the below command:</p>
<pre><code>/bin/bash -c <span class="hljs-string">"<span class="hljs-subst">$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)</span>"</span>
</code></pre><p>You can visit https://brew.sh/ for more details</p>
<p>If Homebrew is already installed you can use the below command to update the repository index</p>
<pre><code>brew <span class="hljs-keyword">update</span>
</code></pre><h3 id="installing-nginx">Installing Nginx</h3>
<p>Once Homebrew is installed, installing Nginx is as easy as </p>
<pre><code><span class="hljs-attribute">brew</span> install nginx
</code></pre><p>Once installed you can see the version of the Nginx that is installed in the Summary.</p>
<h3 id="starting-and-stopping-nginx">Starting and Stopping Nginx</h3>
<p>To start the Nginx server fire the below command. </p>
<pre><code>nginx
</code></pre><p>It will start the web server. You can then visit <a target="_blank" href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> in your browser to see a welcome message by Nginx.</p>
<p>To stop the Nginx server add the stop signal as below.</p>
<pre><code>nginx -s <span class="hljs-keyword">stop</span>
</code></pre><p>Next -&gt;  <a target="_blank" href="https://blog.royalecheese.com/simple-configuration-of-nginx-on-mac-part-2">Part 2: Simple Configuration Nginx on Mac</a> </p>
]]></content:encoded></item></channel></rss>