Alexander Zeitler2024-02-21T18:00:00Zhttps://alexanderzeitler.com/Alexander Zeitleralexander.zeitler@pdmlab.comDealing with C# records in ASP.NET Core model binding2024-02-21T18:00:00Zhttps://alexanderzeitler.com/articles/dealing-with-csharp-records-in-aspnet-core-model-binding/<meta name="description" content="Dealing with C# records in ASP.NET Core model binding" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Dealing with C# records in ASP.NET Core model binding" />
<meta name="twitter:description" content="Dealing with C# records in ASP.NET Core model binding" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/dealing-with-csharp-records-in-aspnet-core-model-binding/viktor-forgacs--0rQ6AbnqeQ-unsplash.jpg" />
<meta property="og:title" content="Dealing with C# records in ASP.NET Core model binding" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/dealing-with-csharp-records-in-aspnet-core-model-binding/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/dealing-with-csharp-records-in-aspnet-core-model-binding/viktor-forgacs--0rQ6AbnqeQ-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@sonance?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
Viktor Forgacs</a>
on <a href="https://unsplash.com/photos/green-grass-field-with-trees-and-a-black-and-white-cross--0rQ6AbnqeQ?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/dealing-with-csharp-records-in-aspnet-core-model-binding/viktor-forgacs--0rQ6AbnqeQ-unsplash.jpg" alt="Recycling" /></p>
<p>C# records are a great addition to the language. They are immutable, have value semantics, and are great for modeling
data. But when it comes to model binding in ASP.NET Core, things are getting a bit more complicated. Let's see how we
can deal with C# records in ASP.NET Core model binding.</p>
<h2>What are C# records?</h2>
<p>C# records are a new feature in C# 9. They are a reference type, but they have value semantics. This means that they are
immutable and can be compared by value. They are great for modeling data, and they are a great addition to the language.</p>
<p>Here is an example of a C# record:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">string</span></span> FirstName<span class="token punctuation">,</span> <span class="token class-name"><span class="token keyword">string</span></span> LastName<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In this example, we have a <code>Person</code> record with two properties: <code>FirstName</code> and <code>LastName</code>.</p>
<h2>Model binding in ASP.NET Core</h2>
<p>Model binding is the process of mapping data from an HTTP request to an object in your application. It is a fundamental
part of building web applications, and it is used to handle form submissions, query string parameters, and other types
of data.</p>
<p>In ASP.NET Core, model binding is done automatically by the framework. When you create a controller action that takes a
parameter, the framework will try to bind data from the request to that parameter.</p>
<p>Here is an example of a controller action that takes a <code>Person</code> parameter:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">HttpPost</span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IActionResult</span> <span class="token function">CreatePerson</span><span class="token punctuation">(</span><span class="token class-name">Person</span> person<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span></code></pre>
<p>In this example, the framework will try to bind data from the request to a <code>Person</code> object. This is done automatically
by the framework, and it is a very powerful feature.</p>
<h2>Dealing with C# records in ASP.NET Core model binding</h2>
<p>At a first glance, everything seems to be easy with C# records. But when it comes to model binding in ASP.NET Core,
things are getting a bit more complicated.</p>
<p>Now there are two issues with C# records and model binding in ASP.NET Core:</p>
<ol>
<li>The model binding needs a parameterless constructor</li>
<li>If you want to translate property names or set display error messages, you need to use the <code>Display</code> and <code>Required</code>
attributes.</li>
</ol>
<p>The shortest way (I know) to have a parameterless constructor while maintaining the positional constructor is this one -
I want to avoid embrace a <code>class</code> like style:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Person</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">?</span></span> FirstName<span class="token punctuation">,</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">?</span></span> LastName
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">(</span>
<span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>The <code>Display</code> and <code>Required</code> attributes are used to set display error messages and translate property names. Here is an
naive approach to use them:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Person</span><span class="token punctuation">(</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Display</span><span class="token attribute-arguments"><span class="token punctuation">(</span>Name <span class="token operator">=</span> <span class="token string">"First Name"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Required</span><span class="token attribute-arguments"><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"The first name is required"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">?</span></span> FirstName<span class="token punctuation">,</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Display</span><span class="token attribute-arguments"><span class="token punctuation">(</span>Name <span class="token operator">=</span> <span class="token string">"Last Name"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Required</span><span class="token attribute-arguments"><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"The last name is required"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">?</span></span> LastName
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">(</span>
<span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token keyword">null</span>
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This won't work, because the <code>Display</code> and <code>Required</code> attributes are not recognized by the model binding. You need to do it that way:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Person</span><span class="token punctuation">(</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token target keyword">property</span><span class="token punctuation">:</span><span class="token class-name">Display</span><span class="token attribute-arguments"><span class="token punctuation">(</span>Name <span class="token operator">=</span> <span class="token string">"First Name"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token target keyword">property</span><span class="token punctuation">:</span><span class="token class-name">Required</span><span class="token attribute-arguments"><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"The first name is required"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">?</span></span> FirstName<span class="token punctuation">,</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token target keyword">property</span><span class="token punctuation">:</span><span class="token class-name">Display</span><span class="token attribute-arguments"><span class="token punctuation">(</span>Name <span class="token operator">=</span> <span class="token string">"Last Name"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token target keyword">property</span><span class="token punctuation">:</span><span class="token class-name">Required</span><span class="token attribute-arguments"><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"The last name is required"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">?</span></span> LastName
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">(</span>
<span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token keyword">null</span>
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>The reason for this is, without the <code>property:</code> prefix, the attributes are not recognized by the model binding because <code>FirstName</code> and <code>LastName</code> are not properties of the <code>Person</code> but constructor parameters. By adding the <code>property:</code> prefix, the attributes are emitted for generated properties instead and the model binding can recognize them.</p>
Listening to HTMX HX-Trigger response header events from Alpine.js2024-02-11T00:00:00Zhttps://alexanderzeitler.com/articles/listening-to-htmx-hx-trigger-response-header-events-from-alpine-js/<meta name="description" content="HTMX and Alpine.js are a great combination. Here's how to listen to HTMX HX-Trigger response header events from Alpine.js." />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Listening to HTMX HX-Trigger response header events from Alpine.js" />
<meta name="twitter:description" content="Listening to HTMX HX-Trigger response header events from Alpine.js" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/listening-to-htmx-hx-trigger-response-header-events-from-alpine-js/vincent-van-zalinge-nq5bgxSg8gE-unsplash.jpg" />
<meta property="og:title" content="Listening to HTMX HX-Trigger response header events from Alpine.js" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/listening-to-htmx-hx-trigger-response-header-events-from-alpine-js/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/listening-to-htmx-hx-trigger-response-header-events-from-alpine-js/vincent-van-zalinge-nq5bgxSg8gE-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@vincentvanzalinge?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Vincent van Zalinge</a> on <a href="https://unsplash.com/photos/brown-rabbit-standing-on-green-grass-field-nq5bgxSg8gE?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/listening-to-htmx-hx-trigger-response-header-events-from-alpine-js/vincent-van-zalinge-nq5bgxSg8gE-unsplash.jpg" alt="Recycling" /></p>
<p>HTMX and Alpine.js are a great combination.</p>
<p>Here's how to listen to HTMX HX-Trigger response header events from Alpine.js.</p>
<p>HTMX allows you to listen for Events from the HTTP Response using the <code>HX-Trigger</code> response header (the last line matters) like this:</p>
<pre class="language-http"><code class="language-http"><span class="token response-status"><span class="token http-version property">HTTP/1.1</span> <span class="token status-code number">200</span> <span class="token reason-phrase string">OK</span></span>
<span class="token header"><span class="token header-name keyword">Content-Type</span><span class="token punctuation">:</span> <span class="token header-value">text/html; charset=utf-8</span></span>
<span class="token header"><span class="token header-name keyword">Date</span><span class="token punctuation">:</span> <span class="token header-value">Sat, 10 Feb 2024 22:46:58 GMT</span></span>
<span class="token header"><span class="token header-name keyword">Server</span><span class="token punctuation">:</span> <span class="token header-value">Kestrel</span></span>
<span class="token header"><span class="token header-name keyword">Transfer-Encoding</span><span class="token punctuation">:</span> <span class="token header-value">chunked</span></span>
<span class="token header"><span class="token header-name keyword">HX-Trigger</span><span class="token punctuation">:</span> <span class="token header-value">product-added-to-shopping-cart</span></span></code></pre>
<p>This indicates that a product has been added to the shopping cart.</p>
<p>HTMX itself can respond to this type of "events" like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">hx-get</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/shopping-cart-preview<span class="token punctuation">"</span></span>
<span class="token attr-name">hx-target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#shopping-cart-preview<span class="token punctuation">"</span></span>
<span class="token attr-name">hx-trigger</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>product-added-to-shopping-cart from:body<span class="token punctuation">"</span></span>
<span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shopping-cart-preview<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>This will reload the shopping cart preview.</p>
<p>No we might want to display a toast or the like using Alpine.js when the product has been added.</p>
<p>HTMX is publishing the events it receives from the server as global events, so you can just listen to it from vanilla JavaScript:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator"><</span>script type<span class="token operator">=</span><span class="token string">"text/javascript"</span><span class="token operator">></span>
document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'product-added-to-shopping-cart'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'product-added-to-shopping-cart'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span></code></pre>
<p>Alpine.js allows you to <a href="https://alpinejs.dev/essentials/events#listening-for-events-on-window">listen for events on the global <code>window</code> object</a> like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">@some-event.window</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>console.log('some-event was dispatched')<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>So we can listen to <code>product-added-to-shopping-cart</code> using Alpine like this:</p>
<pre class="language-html"><code class="language-html">
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{showToast:false}<span class="token punctuation">"</span></span>
<span class="token attr-name">@product-added-to-shopping-cart.window</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>showToast = true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
...
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>Straight forward and simple, yet asked quite often.</p>
Rotating nginx logs using Docker Compose and logrotate2023-12-30T15:00:00Zhttps://alexanderzeitler.com/articles/rotating-nginx-logs-with-docker-compose/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Rotating nginx logs using Docker Compose and logrotate" />
<meta name="twitter:description" content="Rotating nginx logs using Docker Compose and logrotate" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/rotating-nginx-logs-with-docker-compose/sigmund-aI4RJ--Mw4I-unsplash.jpg" />
<meta property="og:title" content="Rotating nginx logs using Docker Compose and logrotate" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/rotating-nginx-logs-with-docker-compose/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/rotating-nginx-logs-with-docker-compose/sigmund-aI4RJ--Mw4I-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@sigmund?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Sigmund</a> on <a href="https://unsplash.com/photos/text-aI4RJ--Mw4I?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/rotating-nginx-logs-with-docker-compose/sigmund-aI4RJ--Mw4I-unsplash.jpg" alt="Recycling" /></p>
<p>If you're running nginx in a Docker container, you might want to rotate the logs. Here's how to do it using Docker Compose and <code>logrotate</code>.</p>
<p>Consider the following <code>docker-compose.yml</code>:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">'3.7'</span>
<span class="token key atrule">services</span><span class="token punctuation">:</span>
<span class="token key atrule">nginx</span><span class="token punctuation">:</span>
<span class="token key atrule">image</span><span class="token punctuation">:</span> nginx<span class="token punctuation">:</span>1.19.6<span class="token punctuation">-</span>alpine
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token datetime number">80:80</span>
<span class="token key atrule">volumes</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> ./nginx.conf<span class="token punctuation">:</span>/etc/nginx/nginx.conf
<span class="token punctuation">-</span> ./logs<span class="token punctuation">:</span>/var/log/nginx</code></pre>
<p>The default location for nginx logs is <code>/var/log/nginx</code>. We're mounting the <code>logs</code> directory from the host into the container. This way, we can access the logs from the host.</p>
<p>We can now use <code>logrotate</code> to rotate the logs. Create a <a href="https://manpages.ubuntu.com/manpages/jammy/en/man8/logrotate.8.html">logrotate configuration</a> file called that's called like your service in <code>/etc/logrotate.d/</code>, in this case <code>nginx</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> /etc/logrotate.d/nginx</code></pre>
<p>Add the following content to the file:</p>
<pre class="language-bash"><code class="language-bash">/var/opt/deploy/logs/*.log <span class="token punctuation">{</span>
daily
missingok
rotate <span class="token number">31</span>
dateext
compress
delaycompress
notifempty
sharedscripts
postrotate
<span class="token builtin class-name">cd</span> /var/opt/deploy <span class="token punctuation">\</span>
<span class="token operator">&&</span> /usr//bin/docker compose <span class="token function">kill</span> <span class="token parameter variable">-s</span> USR1 nginx
endscript
<span class="token punctuation">}</span></code></pre>
<p>This will rotate the logs daily, keep 31 days of logs, compress the logs, and send a <code>USR1</code> signal to the nginx process in the container. This will cause nginx to reopen the log files.</p>
<p>The <code>dateext</code> option will append the date to the rotated log files. For example, <code>access.log</code> will become <code>access.log-20231230</code>.</p>
<p>Make sure to match the path to your logs. In this case, the logs are located in <code>/var/opt/deploy/logs/</code> on the host. The logs are mounted into the container at <code>/var/log/nginx/</code>. The logs are located in <code>/var/log/nginx/</code> in the container.</p>
<p>Also make sure to match the path to your <code>docker-compose.yml</code>. In this case, the <code>docker-compose.yml</code> is located in <code>/var/opt/deploy/</code> on the host.</p>
<p>The <code>postrotate</code> script will change into the directory where the <code>docker-compose.yml</code> is located and send the <code>USR1</code> signal to the nginx process in the container. This will cause nginx to reopen the log files.</p>
<p>The <code>USR1</code> signal is sent to the nginx process in the container using <code>docker compose kill</code>. The <code>USR1</code> signal is not sent to the container itself, but to the process running inside the container. The process running inside the container is nginx.</p>
<p>You can test the log rotation by running <code>logrotate</code> manually:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">logrotate</span> <span class="token parameter variable">-f</span> /etc/logrotate.d/nginx</code></pre>
<p>And that's it. Your nginx logs will now be rotated daily, compressed, and kept for 31 days.</p>
Setting Displays to monochrome / grayscale (Saturation 0) on Xubuntu / XFCE2023-12-25T20:00:00Zhttps://alexanderzeitler.com/articles/setting-display-monochrome-saturation-0-on-xubuntu-xfce/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Setting Displays to monochrome / grayscale (Saturation 0) on Xubuntu / XFCE" />
<meta name="twitter:description" content="Setting Displays to monochrome / grayscale (Saturation 0) on Xubuntu / XFCE" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/setting-display-monochrome-saturation-0-on-xubuntu-xfce/chris-ried-ieic5Tq8YMk-unsplash.jpg" />
<meta property="og:title" content="Setting Displays to monochrome / grayscale (Saturation 0) on Xubuntu / XFCE" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/setting-display-monochrome-grayscale-saturation-0-on-xubuntu-xfce/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/setting-display-monochrome-saturation-0-on-xubuntu-xfce/chris-ried-ieic5Tq8YMk-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@cdr6934?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Chris Ried</a> on <a href="https://unsplash.com/photos/a-computer-screen-with-a-bunch-of-code-on-it-ieic5Tq8YMk?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/setting-display-monochrome-saturation-0-on-xubuntu-xfce/chris-ried-ieic5Tq8YMk-unsplash.jpg" alt="Grayscale..." /></p>
<p>For some tasks like writing I prefer to have my displays set to monochrome / grayscale. This is how I do it on Xubuntu / XFCE.</p>
<p>There are several solutions out there that use <code>xrandr</code> or <code>ddcutil</code> but none of them worked for me. I'm using Xubuntu 22.04.2 LTS on a late 2012 Mac mini for some tasks.</p>
<p>So here is how you would do it using <code>xrandr</code>:</p>
<pre class="language-bash"><code class="language-bash">xrandr <span class="token parameter variable">--output</span> HDMI-3 <span class="token parameter variable">--set</span> Saturation <span class="token number">0</span></code></pre>
<p><code>HDMI-3</code> is the name of my display.</p>
<p>You can get the name of your display by running <code>xrandr</code> without any arguments.</p>
<p>Connected displays will be listed like this:</p>
<pre class="language-bash"><code class="language-bash">HDMI-3 connected 1920x1200+1920+0 <span class="token punctuation">(</span>normal left inverted right x axis y axis<span class="token punctuation">)</span> 518mm x 324mm</code></pre>
<p>The solution that actually worked for me was to use <code>libvibrant</code> and its source can be found on <a href="https://github.com/libvibrant/libvibrant">GitHub</a>.</p>
<p>It's <a href="https://github.com/libvibrant/libvibrant#basic-building">said</a> to be build and installed this way:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> build
<span class="token builtin class-name">cd</span> build
cmake <span class="token punctuation">..</span>
<span class="token function">make</span>
<span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span></code></pre>
<p>However, I had to do few more things to get it working:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> build
<span class="token builtin class-name">cd</span> build
<span class="token comment"># fixes: Could not find X11</span>
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libx11-dev
<span class="token comment"># fixes: The RandR library and headers were not found</span>
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> xorg-dev libglu1-mesa-dev
<span class="token comment"># fixes: fatal error: NVCtrl/NVCtrlLib.h: No such file or directory</span>
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libxnvctrl-dev
cmake <span class="token punctuation">..</span>
<span class="token function">make</span>
<span class="token comment"># fixes: libvibrant.so.1 cannot open shared object file</span>
<span class="token function">sudo</span> /sbin/ldconfig -v*
<span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span></code></pre>
<p>After that I was able to run <code>vibrant-cli</code> and it worked like a charm:</p>
<pre class="language-bash"><code class="language-bash">vibrant-cli HDMI-3 <span class="token number">0</span>
vibrant-cli DP-1 <span class="token number">0</span></code></pre>
<p>To reset the saturation to 100%:</p>
<pre class="language-bash"><code class="language-bash">vibrant-cli HDMI-3 <span class="token number">1</span>
vibrant-cli DP-1 <span class="token number">1</span></code></pre>
<p>Running this will make it look even better:</p>
<pre class="language-bash"><code class="language-bash">xgamma <span class="token parameter variable">-gamma</span> <span class="token number">0.8</span></code></pre>
<p>If you create a <code>.desktop</code> entry for both commands, you easily run them using a launcher like <a href="https://albertlauncher.github.io/">Albert</a>:</p>
<p><img src="https://alexanderzeitler.com/articles/setting-display-monochrome-saturation-0-on-xubuntu-xfce/monochrome-mode-on.png" alt="Monochrome mode launcher" /></p>
Annotating Podcasts and MP3 files using Logseq2023-12-23T20:00:00Zhttps://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Annotating Podcasts and MP3 files using Logseq" />
<meta name="twitter:description" content="Annotating Podcasts and MP3 files using Logseq" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/soundtrap-h6PDEdr9IZo-unsplash.jpg" />
<meta property="og:title" content="Annotating Podcasts and MP3 files using Logseq" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/soundtrap-h6PDEdr9IZo-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@soundtrap?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Soundtrap</a> on <a href="https://unsplash.com/photos/macbook-pro-on-brown-wooden-table-h6PDEdr9IZo?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/soundtrap-h6PDEdr9IZo-unsplash.jpg" alt="Podcast Recording" /></p>
<p>A while ago I posted how to <a href="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre">annotate epub using Logseq</a>. This time I'll show you, how to annotate Podcasts or more generic, MP3 files - using Linux.</p>
<p>First of all we need to create a <code>.mp3</code> file from a Podcast.</p>
<p>I'm using the Podcasts browser extension (available for Firefox and Chrome) from <a href="https://podcasts.bluepill.life/index.html">here</a>.</p>
<p>It looks a bit like a lightweight version of Spotify. You can search for Podcasts and subscribe to them. You can also download episodes and play them.</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/podcasts-extension-firefox.png" alt="Podcasts browser extension" /></p>
<p>So you can open an episode:</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/podcasts-extension-firefox-episode-playing.png" alt="Playing an episode" /></p>
<p>And then you can download it by hovering over the episode and clicking the download button:</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/podcasts-extension-firefox-episode-download.png" alt="Downloading an episode" /></p>
<p>Open a terminal and change to the directory where you downloaded the episode. Then run:</p>
<pre class="language-bash"><code class="language-bash">ffmpeg <span class="token parameter variable">-loop</span> <span class="token number">1</span> <span class="token parameter variable">-i</span> ./cover.jpg <span class="token punctuation">\</span>
<span class="token parameter variable">-i</span> ./episode-12.mp3 <span class="token punctuation">\</span>
<span class="token parameter variable">-vf</span> <span class="token string">"scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1:color=black,setsar=1,format=yuv420p"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">-shortest</span> <span class="token parameter variable">-fflags</span> +shortest <span class="token punctuation">\</span>
episode-12.mp4</code></pre>
<p>Make sure to also have cover image named <code>cover.jpg</code> in the same directory. This will create a <code>.mp4</code> file from the <code>.mp3</code> file and the cover image.</p>
<p>Depending on the length of the episode and your machine, this can take a while, but you should get a result in under 10 minutes for a 20 minutes episode.</p>
<p>Now we can upload this video to YouTube, mark it as unlisted (so nobody beside you can see it until they get the link) and use the YouTube video to annotate it using Logseq (just past the video link into Logseq).</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-podcasts-or-mp3-in-logseq/logseq-podcast.png" alt="Annotating the YouTube video" /></p>
<p>You can add a timestamp by stopping the video at the desired position and then typing <code>/timestamp</code> in the Logseq editor. This will show the menu where you can pick the "Embed YouTube timestamp" option with the number of seconds at which you paused the video.</p>
<p>As said, make sure to have an background image added, otherwise the video will not be processed by YouTube.</p>
<p>Of course, you can also create <code>.mp4</code> files from <code>.mp3</code> files on macOS using iMovie (and I guess there's something similar available on Windows, too).</p>
<p>And of course, if the Podcast provides a YouTube version of the episode, you can use that instead of creating a video from the audio file and uploading it to YouTube.</p>
<p>The Podcast extension also offers a paid service to transcribe the audio file. I haven't tried that yet because I want to annotate in my own words. I just need the timestamps feature.</p>
<p>I also tried <a href="https://www.snipd.com/">Snipd</a> but their transcription service is not available for German language yet.</p>
Using aliases in scripts with zsh2023-12-17T22:00:00Zhttps://alexanderzeitler.com/articles/using-aliases-in-zsh-scripts/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Using aliases in subshells with zsh" />
<meta name="twitter:description" content="Using aliases in subshells with zsh" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/using-aliases-in-zsh-scripts/ahmed-zayan-yB_e0q_3kp0-unsplash.jpg" />
<meta property="og:title" content="Using aliases in subshells with zsh" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/using-aliases-in-zsh-scripts/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/using-aliases-in-zsh-scripts/ahmed-zayan-yB_e0q_3kp0-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/@zayyerrn?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Ahmed Zayan</a> on <a href="https://unsplash.com/photos/man-in-black-jacket-and-guy-fawkes-mask-yB_e0q_3kp0?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/using-aliases-in-zsh-scripts/ahmed-zayan-yB_e0q_3kp0-unsplash.jpg" alt="Using aliases" /></p>
<p>We often use shell aliases to make our life easier. However, when we try to use them in shell scripts (hence, subshells) it seems to get harder.</p>
<p>Let's say we have the following alias to run the AWS CLI in a docker container that is using the current directory as the working directory:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">aws</span><span class="token operator">=</span><span class="token string">'docker run --rm -it -v ~/.aws:/root/.aws -v $(pwd):/aws amazon/aws-cli'</span></code></pre>
<p>If we try to use this alias in a script, it will fail:</p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/bin/zsh</span>
aws <span class="token parameter variable">--profile</span> my-aws-profile s3 <span class="token function">cp</span> s3://my-bucket/path/to/file <span class="token builtin class-name">.</span></code></pre>
<p>This will result in the following error:</p>
<pre class="language-bash"><code class="language-bash">zsh: <span class="token builtin class-name">command</span> not found: aws</code></pre>
<p>There are a few things to know:</p>
<ul>
<li>aliases are <a href="https://www.gnu.org/software/bash/manual/html_node/Aliases.html#Aliases">not expanded in non-interactive shells</a></li>
<li>functions <a href="https://www.gnu.org/software/bash/manual/html_node/Aliases.html">are preferred over aliases</a> in bash</li>
</ul>
<p>So, let's try to use a function instead of an alias:</p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/bin/zsh</span>
<span class="token function-name function">aws</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">docker</span> run <span class="token parameter variable">--rm</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">-v</span> ~/.aws:/root/.aws <span class="token parameter variable">-v</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>:/aws amazon/aws-cli <span class="token string">"<span class="token variable">$@</span>"</span>
<span class="token punctuation">}</span></code></pre>
<p>Now we could add the function to our <code>.zshrc</code> and source it and try to use it in our script and get new errors.</p>
<p>Long story short, there's a file named <code>~/.zshenv</code> (if it doesn't exist, just create it) that is sourced on every invocation of <code>zsh</code>. So, we can add the function to this file and it will be available in our scripts.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function-name function">aws</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">docker</span> run <span class="token parameter variable">--rm</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">-v</span> ~/.aws:/root/.aws <span class="token parameter variable">-v</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>:/aws amazon/aws-cli <span class="token string">"<span class="token variable">$@</span>"</span>
<span class="token punctuation">}</span></code></pre>
<p>Now we can use the function in our script:</p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/bin/zsh</span>
aws <span class="token parameter variable">--profile</span> my-aws-profile s3 <span class="token function">cp</span> s3://my-bucket/path/to/file <span class="token builtin class-name">.</span></code></pre>
<p>And it will work as expected 🎉.</p>
Parsing nginx logs with GoAccess and creating a HTML report in a cron job2023-11-02T11:00:00Zhttps://alexanderzeitler.com/articles/parsing-nginx-logs-with-goaccess-and-creating-a-html-report-using-a-cron-job/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Parsing nginx logs with GoAccess and creating a HTML report using a cron job" />
<meta name="twitter:description" content="Parsing nginx logs with GoAccess and creating a HTML report using a cron job" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/parsing-nginx-logs-with-goaccess-and-creating-a-html-report-using-a-cron-job/luke-chesser-JKUTrJ4vK00-unsplash.jpg" />
<meta property="og:title" content="Parsing nginx logs with GoAccess and creating a HTML report using a cron job" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/parsing-nginx-logs-with-goaccess-and-creating-a-html-report-using-a-cron-job/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/parsing-nginx-logs-with-goaccess-and-creating-a-html-report-using-a-cron-job/luke-chesser-JKUTrJ4vK00-unsplash.jpg" />
<p><img src="https://alexanderzeitler.com/articles/parsing-nginx-logs-with-goaccess-and-creating-a-html-report-using-a-cron-job/luke-chesser-JKUTrJ4vK00-unsplash.jpg" alt="Server logs" /></p>
<p><small><small>
Photo by <a href="https://unsplash.com/@lukechesser?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Luke Chesser</a> on <a href="https://unsplash.com/photos/graphs-of-performance-analytics-on-a-laptop-screen-JKUTrJ4vK00?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
</small></small></p>
<p>Gyula recently asked for a decent cookieless analytics tool on Twitter:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Okay. I don't wanna use Google analytics, I don't wanna anger my users with cookie popups, but wanna be GDPR compliant and I wanna have analytics on my website at the same time.<br /><br />What should I do? Any recommendations?</p>— GYN (@gyulanemeth85) <a href="https://twitter.com/gyulanemeth85/status/1719802160702013925?ref_src=twsrc%5Etfw">November 1, 2023</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I suggested to use <a href="https://goaccess.io/">GoAccess</a> to parse the nginx logs and create a HTML report and Hannes asked for a quickstart - so here we go.</p>
<p>Considering we're a running a Linux server with nginx and Docker, here's how to get started and our blog is static HTML generated with 11ty it is as easy as this to have a cron job running every day at 22:05 to create a HTML report of the nginx logs:</p>
<pre class="language-bash"><code class="language-bash"><span class="token number">5</span> <span class="token number">22</span> * * * <span class="token function">cat</span> /var/opt/deploy/logs/nginx/access.log <span class="token operator">|</span> /usr/bin/docker run <span class="token parameter variable">--rm</span> <span class="token parameter variable">-i</span> <span class="token parameter variable">-e</span> <span class="token assign-left variable"><span class="token environment constant">LANG</span></span><span class="token operator">=</span><span class="token environment constant">$LANG</span> allinurl/goaccess <span class="token parameter variable">-a</span> <span class="token parameter variable">-o</span> html --log-format COMBINED - <span class="token operator">></span> /var/opt/deploy/www/_site/report.html</code></pre>
<p>The command in detail:</p>
<p><code>5 22 * * *</code> - run the command every day at 22:05</p>
<p><code>cat /var/opt/deploy/logs/nginx/access.log</code> - read the nginx access log</p>
<p><code>|</code> - pipe the output to the next command</p>
<p><code>/usr/bin/docker run --rm -i -e LANG=$LANG allinurl/goaccess -a -o html --log-format COMBINED -</code> - run the docker container with the GoAccess image and parse the nginx access log and create a HTML report</p>
<p><code>> /var/opt/deploy/www/_site/report.html</code> - write the HTML report to the file system</p>
<p>The report:</p>
<p><img src="https://alexanderzeitler.com/articles/parsing-nginx-logs-with-goaccess-and-creating-a-html-report-using-a-cron-job/goaccesslog.png" alt="the report" /></p>
Installing the latest Elixir and Erlang versions on Xubuntu 22.042023-04-16T20:00:00Zhttps://alexanderzeitler.com/articles/installing-latest-elixir-erlang-version-on-xubuntu-ubuntu-lts-22.04-asdf/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Installing the latest Elixir and Erlang versions on Xubuntu 22.04" />
<meta name="twitter:description" content="Installing the latest Elixir and Erlang versions on Xubuntu 22.04" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/installing-latest-elixir-erlang-version-on-xubuntu-ubuntu-lts-22.04-asdf/mika-baumeister-9Qq_G14hNC8-unsplash.jpg" />
<meta property="og:title" content="Installing the latest Elixir and Erlang versions on Xubuntu 22.04" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/installing-latest-elixir-erlang-version-on-xubuntu-ubuntu-lts-22.04-asdf/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/installing-latest-elixir-erlang-version-on-xubuntu-ubuntu-lts-22.04-asdf/mika-baumeister-9Qq_G14hNC8-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@mbaumi?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Mika Baumeister</a> on <a href="https://unsplash.com/photos/9Qq_G14hNC8?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/installing-latest-elixir-erlang-version-on-xubuntu-ubuntu-lts-22.04-asdf/mika-baumeister-9Qq_G14hNC8-unsplash.jpg" alt="Elixir artwork" /></p>
<p>After <a href="https://alexanderzeitler.com/articles/learning-elixir">dabbling around with some Elixir courses</a> I wanted to dig into the event sourcing sample named "<a href="https://github.com/zorn/franklin">franklin</a>" by <a href="https://mikezornek.com/">Mike Zornek</a>.</p>
<p>Mike provides a <code>README</code>, so let's just run <code>mix setup</code> and see what happens:</p>
<pre class="language-bash"><code class="language-bash">** <span class="token punctuation">(</span>Mix<span class="token punctuation">)</span> You're trying to run :franklin on Elixir v1.12.2 but it has declared <span class="token keyword">in</span> its mix.exs <span class="token function">file</span> it supports only Elixir ~<span class="token operator">></span> <span class="token number">1.14</span>.0</code></pre>
<p>To be honest, I didn't care exactly about which versions of Elixir and Erlang I had installed using <code>apt-get install erlang elixir</code> and <code>IEx</code> experiments did work fine so far.</p>
<p>Ok, the latest stable versions seem to be Erlang <code>25.3</code> and Elixir <code>1.14.4</code>.</p>
<p>Maybe <code>mix</code> was right...</p>
<p>After looking around at Elixir Forum, several people suggested using <code>asdf</code> which I've installed already (but I'll explain it from scratch anyway).</p>
<p>So, here we go - first uninstalling the old versions:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">apt</span> remove erlang elixir </code></pre>
<p>Then install <code>asdf</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> clone https://github.com/asdf-vm/asdf.git ~/.asdf</code></pre>
<p>As I'm using <code>oh-my-zsh</code> / <code>zsh</code>, I've also enabled the <code>asdf</code> plugin in <code>~/.zshrc</code>:</p>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">plugins</span><span class="token operator">=</span><span class="token punctuation">(</span>
asdf
<span class="token function">git</span>
zsh-autosuggestions
zsh-syntax-highlighting
<span class="token punctuation">)</span></code></pre>
<p>Next, I installed the <code>erlang</code> and <code>elixir</code> plugins for <code>asdf</code>:</p>
<pre class="language-bash"><code class="language-bash">asdf plugin <span class="token function">add</span> erlang https://github.com/asdf-vm/asdf-erlang.git
asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git</code></pre>
<p>Then, some build dependencies are required:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libssl-dev automake autoconf libncurses5-dev</code></pre>
<p>And finally, install Erlang:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">KERL_CONFIGURE_OPTIONS</span><span class="token operator">=</span><span class="token string">"--disable-debug --without-javac"</span>
asdf <span class="token function">install</span> erlang <span class="token number">25.3</span>
asdf_25.3 is not a kerl-managed Erlang/OTP installation
The asdf_25.3 build has been deleted
Extracting <span class="token builtin class-name">source</span> code
Building Erlang/OTP <span class="token number">25.3</span> <span class="token punctuation">(</span>asdf_25.3<span class="token punctuation">)</span>, please wait<span class="token punctuation">..</span>.
APPLICATIONS DISABLED <span class="token punctuation">(</span>See: /home/alexzeitler/.asdf/plugins/erlang/kerl-home/builds/asdf_25.3/otp_build_25.3.log<span class="token punctuation">)</span>
* jinterface <span class="token builtin class-name">:</span> Java compiler disabled by user
* odbc <span class="token builtin class-name">:</span> ODBC library - <span class="token function">link</span> check failed
APPLICATIONS INFORMATION <span class="token punctuation">(</span>See: /home/alexzeitler/.asdf/plugins/erlang/kerl-home/builds/asdf_25.3/otp_build_25.3.log<span class="token punctuation">)</span>
* wx <span class="token builtin class-name">:</span> No OpenGL headers found, wx will NOT be usable
* No GLU headers found, wx will NOT be usable
* wxWidgets was not compiled with --enable-webview or wxWebView developer package is not installed, wxWebView will NOT be available
* wxWidgets must be installed on your system.
* Please check that wx-config is <span class="token keyword">in</span> path, the directory
* where wxWidgets libraries are installed <span class="token punctuation">(</span>returned by
* <span class="token string">'wx-config --libs'</span> or <span class="token string">'wx-config --static --libs'</span> <span class="token builtin class-name">command</span><span class="token punctuation">)</span>
* is <span class="token keyword">in</span> LD_LIBRARY_PATH or equivalent variable and
* wxWidgets version is <span class="token number">3.0</span>.2 or above.
DOCUMENTATION INFORMATION <span class="token punctuation">(</span>See: /home/alexzeitler/.asdf/plugins/erlang/kerl-home/builds/asdf_25.3/otp_build_25.3.log<span class="token punctuation">)</span>
* documentation <span class="token builtin class-name">:</span>
* xsltproc is missing.
* fop is missing.
* xmllint is missing.
* The documentation cannot be built.
Erlang/OTP <span class="token number">25.3</span> <span class="token punctuation">(</span>asdf_25.3<span class="token punctuation">)</span> has been successfully built
Cleaning up compilation products <span class="token keyword">for</span>
Cleaned up compilation products <span class="token keyword">for</span> under /home/alexzeitler/.asdf/plugins/erlang/kerl-home/builds</code></pre>
<p>And Elixir:</p>
<pre class="language-bash"><code class="language-bash">asdf <span class="token function">install</span> elixir <span class="token number">1.14</span>.4-otp-25
<span class="token operator">==</span><span class="token operator">></span> Checking whether specified Elixir release exists<span class="token punctuation">..</span>.
<span class="token operator">==</span><span class="token operator">></span> Downloading <span class="token number">1.14</span>.4-otp-25 to /home/alexzeitler/.asdf/downloads/elixir/1.14.4-otp-25/elixir-precompiled-1.14.4-otp-25.zip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
<span class="token number">100</span> 6516k <span class="token number">100</span> 6516k <span class="token number">0</span> <span class="token number">0</span> <span class="token number">12</span>.2M <span class="token number">0</span> --:--:-- --:--:-- --:--:-- <span class="token number">12</span>.2M
<span class="token operator">==</span><span class="token operator">></span> Copying release into place
franklin on main <span class="token punctuation">[</span>?<span class="token punctuation">]</span> is 📦 v0.1.0 via 💧 v1.14.4 <span class="token punctuation">(</span>OTP <span class="token number">25</span><span class="token punctuation">)</span>
❯ elixir <span class="token parameter variable">--version</span>
Erlang/OTP <span class="token number">25</span> <span class="token punctuation">[</span>erts-13.2<span class="token punctuation">]</span> <span class="token punctuation">[</span>source<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">64</span>-bit<span class="token punctuation">]</span> <span class="token punctuation">[</span>smp:8:8<span class="token punctuation">]</span> <span class="token punctuation">[</span>ds:8:8:10<span class="token punctuation">]</span> <span class="token punctuation">[</span>async-threads:1<span class="token punctuation">]</span> <span class="token punctuation">[</span>jit:ns<span class="token punctuation">]</span>
Elixir <span class="token number">1.14</span>.4 <span class="token punctuation">(</span>compiled with Erlang/OTP <span class="token number">25</span><span class="token punctuation">)</span></code></pre>
<p>Set the default versions:</p>
<pre class="language-bash"><code class="language-bash">asdf global elixir <span class="token number">1.14</span>.4-otp-25
version <span class="token number">1.14</span>.4 is not installed <span class="token keyword">for</span> elixir
asdf global erlang <span class="token number">25.3</span></code></pre>
<p>Result:</p>
<pre class="language-bash"><code class="language-bash">Erlang/OTP <span class="token number">25</span> <span class="token punctuation">[</span>erts-13.2<span class="token punctuation">]</span> <span class="token punctuation">[</span>source<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">64</span>-bit<span class="token punctuation">]</span> <span class="token punctuation">[</span>smp:8:8<span class="token punctuation">]</span> <span class="token punctuation">[</span>ds:8:8:10<span class="token punctuation">]</span> <span class="token punctuation">[</span>async-threads:1<span class="token punctuation">]</span> <span class="token punctuation">[</span>jit:ns<span class="token punctuation">]</span>
Elixir <span class="token number">1.14</span>.4 <span class="token punctuation">(</span>compiled with Erlang/OTP <span class="token number">25</span><span class="token punctuation">)</span></code></pre>
<p>Looks good and seems to work - however I may have done it wrong. If so, please let me know.</p>
Elixir syntax highlighting for Logseq2023-04-15T20:00:00Zhttps://alexanderzeitler.com/articles/logseq-code-syntax-highlighting-elixir/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Elixir syntax highlighting for Logseq" />
<meta name="twitter:description" content="Elixir syntax highlighting for Logseq" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/logseq-code-syntax-highlighting-elixir/elixir-in-logseq.jpg" />
<meta property="og:title" content="Elixir syntax highlighting for Logseq" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/logseq-code-syntax-highlighting-elixir/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/logseq-code-syntax-highlighting-elixir/elixir-in-logseq.jpg" />
<p>I'm journaling my <a href="https://alexanderzeitler.com/articles/learning-elixir">Elixir learnings</a> in Logseq.</p>
<p>Until today I've accepted the fact that LogSeq doesn't support Elixir syntax highlighting.</p>
<p>But today I came across the "Extensions plus" plugin for Logseq, which - besides some other features - adds syntax highlighting for Elixir:</p>
<p><img src="https://alexanderzeitler.com/articles/logseq-code-syntax-highlighting-elixir/extensions-plus-plugin-logseq.png" alt="Extensions Plus plugin" /></p>
<p><img src="https://alexanderzeitler.com/articles/logseq-code-syntax-highlighting-elixir/elixir-in-logseq.jpg" alt="Elixir syntax highlighting in Logseq" /></p>
<p>Make sure to restart Logseq to make the plugin work.</p>
<p>Also, make sure "elixir" is checked in the plugin settings:</p>
<p><img src="https://alexanderzeitler.com/articles/logseq-code-syntax-highlighting-elixir/plugin-settings.png" alt="Plugin settings" /></p>
Learning Elixir2023-04-10T20:00:00Zhttps://alexanderzeitler.com/articles/learning-elixir/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Learning Elixir" />
<meta name="twitter:description" content="Learning Elixir" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/learning-elixir/2157081.png" />
<meta property="og:title" content="Learning Elixir" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/learning-elixir/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/learning-elixir/2157081.png" />
<p><small><small>
Photo by Vlad Shagov on <a href="https://gitu.net/en/details/elixir-logo-icon-freebie">Gitu</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/learning-elixir/2157081.png" alt="Elixir artwork" /></p>
<p>I've decided to learn <a href="https://elixir-lang.org/">Elixir</a>.</p>
<p>Not just poking a bit into the language but to be able to build full stack Elixir distributed applications and eventually SaaS.</p>
<p>I don't want to talk much about the why for now but you can find a hint or two on my social media channels.</p>
<p>This post aims to summarize the learning materials I've used so far and the building blocks I intend to learn about and use and I would be more than happy about your feedback and preferences here.</p>
<p>I'll categorize stuff a bit - here we go:</p>
<h3>Language / General</h3>
<ul>
<li><a href="https://elixir-lang.org/">Elixir official website</a></li>
<li><a href="https://www.youtube.com/watch?v=gRQIPvDFuts">Elixir & Phoenix Fundamentals Full Course For Beginners</a> on YouTube. Almost 6 hours non-stop Elixir and Phoenix learnings for free.</li>
<li><a href="https://elixircasts.io/">ElixirCasts</a> [2023/04/17] - not everything is free but it's worth the money if you're serious abouit Elixir / Phoenix / OTP and GenServer</li>
<li><a href="https://elixirschool.com/">Elixir School</a> - multilingual website packed with Elixir topics from basic to pro covering OTP, Testing, Data Processing and Storage</li>
<li><a href="https://elixirforum.com/">Elixir Forum</a> - Discourse community for Elixir</li>
<li><a href="https://discord.gg/elixir">Elixir Discord</a> - Elixir community on Discord</li>
<li><a href="https://exercism.org/tracks/elixir/">Elixir Track on Exercism</a> [2023/04/15 via Fernando Canizo] - Free Elixir exercises</li>
<li><a href="https://github.com/elixir-lang/ex_doc">ExDoc</a> - A tool to generate documentation for your Elixir projects</li>
<li><a href="https://github.com/hexpm/hex">hex</a> - Package manager for the Erlang VM</li>
<li><a href="https://livebook.dev/">Livebook</a> - Share knowledge, explore code, visualize data, run machine learning models, and much more</li>
</ul>
<h3>Database</h3>
<ul>
<li><a href="https://github.com/elixir-ecto/ecto">ecto</a> - A toolkit for data mapping and language integrated query</li>
<li><a href="https://github.com/elixir-ecto/postgrex">postgrex</a> - PostgreSQL driver for Elixir</li>
</ul>
<h3>Web Applications</h3>
<ul>
<li><a href="https://github.com/elixir-plug/plug">Plug</a> - A specification for composing web applications with functions</li>
<li><a href="https://github.com/mtrudel/bandit">Bandit</a> - A pure Elixir HTTP server for Plug & WebSock applications</li>
<li><a href="https://www.phoenixframework.org/">Phoenix Framework</a> - Framework written in Elixir which implements the server-side Model View Controller (MVC) pattern</li>
</ul>
<h3>Scheduling</h3>
<ul>
<li><a href="https://github.com/sorentwo/oban">Oban</a> - Robust job processing in Elixir, backed by modern PostgreSQL or SQLite3</li>
</ul>
<h3>Event Sourcing / CQRS</h3>
<ul>
<li><a href="https://github.com/Nebo15/sage">saga</a> - A dependency-free tool to run distributed transactions in Elixir, inspired by Saga pattern</li>
<li><a href="https://github.com/commanded">commanded</a> - Event Sourcing library for Postgres, EventStoreDB and In-memory</li>
</ul>
<h3>Messaging</h3>
<ul>
<li><a href="https://elixir-broadway.org/">Broadway</a> - Build concurrent and multi-stage data ingestion and data processing pipelines with Elixir</li>
</ul>
<h3>Testing</h3>
<ul>
<li><a href="https://hexdocs.pm/ex_unit">ExUnit</a> - Unit testing framework for Elixir.</li>
</ul>
<h3>Identity / Authentication</h3>
<ul>
<li><a href="https://powauth.com/">Pow</a> - A robust, modular and extendable user management solution</li>
</ul>
<h3>Cloud</h3>
<ul>
<li><a href="https://github.com/aws-beam/aws-elixir">aws-elixir</a> - AWS clients for Elixir</li>
</ul>
Annotating and highlighting epub using Logseq2023-04-10T15:00:00Zhttps://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Annotating and highlighting epub using Logseq" />
<meta name="twitter:description" content="Annotating and highlighting epub using Logseq" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/kelly-sikkema-YnRNdB-XTME-unsplash.jpg" />
<meta property="og:title" content="Annotating and highlighting epub using Logseq" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/kelly-sikkema-YnRNdB-XTME-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@kellysikkema?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Kelly Sikkema</a> on <a href="https://unsplash.com/s/photos/book-note?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/kelly-sikkema-YnRNdB-XTME-unsplash.jpg" alt="Taking notes from a book" /></p>
<p>When possible, I'm trying to read books in epub format or as printed books. Most of my notes and journals live in Logseq.</p>
<p>So I was asking myself: "How could I bring my ebook highlights and notes to Logseq?"</p>
<p>While it isn't possible right now to load epub files into Logseq like you can do with <a href="https://alexanderzeitler.com/articles/printing-out-websites">PDF files</a>, it is possible to reference your <a href="https://calibre-ebook.com/">calibre</a> notes and highlights - here's how:</p>
<p>First, highlight a section in a book and maybe also jot down some notes in a book using calibre:</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/calibre-book-highlight.png" alt="" /></p>
<p>Next, click the "Export" button at the bottom right and select "Markdown":</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/book-highlights-export-settings.png" alt="" /></p>
<p>Now, click the "Copy to clipboard" button and paste the clipboard into your Logseq page:</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/logseq-book-highlight-notes.png" alt="" /></p>
<p>As you can see, the highlighted section and the notes taken are now in your Logseq page for the book.</p>
<p>Did you spot the link <code>10.04.23 17:19</code>? That's a deep link to your notes in calibre.</p>
<p>If you click the link, the book will be opened in calibre at the highlighted section. Neat, isn't it?</p>
<p><img src="https://alexanderzeitler.com/articles/annotating-highlighting-epub-with-logseq-calibre/calibre-book-highlight-deeplink.png" alt="" /></p>
<p>Of course, you could also <a href="https://manual.calibre-ebook.com/en/conversion.html">convert the whole book to PDF</a> using calibre and use the PDF annotation workflow <a href="https://alexanderzeitler.com/articles/printing-out-websites">I described recently</a>.</p>
<p>Another option could also be to just automate the whole stuff by reading the <code>metadata.opf</code> calibre file and push it to your Logseq markdown files...</p>
How to stop JetBrains Rider asking to add files to Git version control2023-04-07T21:00:00Zhttps://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="How to stop JetBrains Rider asking to add files to Git version control" />
<meta name="twitter:description" content="How to stop JetBrains Rider asking to add files to Git version control" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/dim-hou-BjD3KhnTIkg-unsplash.jpg" />
<meta property="og:title" content="How to stop JetBrains Rider asking to add files to Git version control" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/dim-hou-BjD3KhnTIkg-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@dimhou?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Dim Hou</a> on <a href="https://unsplash.com/photos/BjD3KhnTIkg?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/dim-hou-BjD3KhnTIkg-unsplash.jpg" alt="Stop adding my files to git, please" /></p>
<p>Whenever I've created a new project in Rider and added some files to the project, Rider was asking to add these file to Git / Version control:</p>
<p><img src="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/rider-asking-too-many-questions.png" alt="" /></p>
<p>Ticking "Don't ask again" only helps for this project, but of course there must be a global setting.</p>
<p>Indeed, a setting can be found in <code>File | Settings | Version Control | Confirmation</code>:</p>
<p><img src="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/rider-dont-ask-again.png" alt="" /></p>
<p>So let's create a new project and add a file, to see if this works...</p>
<p><img src="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/rider-asking-too-many-questions.png" alt="" /></p>
<p>Well, that didn't work... because there's another setting in <code>File | New Projects Setup | Settings for New Projects...</code> (you can also use actions modal) which looks absolutely the same but it is for <em>new</em> projects:</p>
<p><img src="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/rider-settings-for-new-projects.png" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/how-to-stop-jetbrains-rider-stop-asking-add-files-to-version-control-git/rider-dont-ask-again-actually.png" alt="" /></p>
<p>If you change "When files are created" to "Do not add", you won't be asked again, I'll promise 🤞️.</p>
Inspecting ASP.NET integration testing logs made easy with JetBrains Rider2023-04-05T21:00:00Zhttps://alexanderzeitler.com/articles/inspecting-aspnet-dotnet-integration-testing-logs-jetbrains-rider-grep-console/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Inspecting ASP.NET integration testing logs made easy with JetBrains Rider" />
<meta name="twitter:description" content="Inspecting ASP.NET integration testing logs made easy with JetBrains Rider" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/inspecting-aspnet-dotnet-integration-testing-logs-jetbrains-rider-grep-console/nathan-bingle-RB5ADoT-DZk-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@nathangbingle?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Nathan Bingle</a> on <a href="https://unsplash.com/photos/RB5ADoT-DZk?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/inspecting-aspnet-dotnet-integration-testing-logs-jetbrains-rider-grep-console/nathan-bingle-RB5ADoT-DZk-unsplash.jpg" alt="Trying to spot something meaningful" /></p>
<p>The other day I was integration testing a Saga in the kliento code base. The tests failed and I wanted to skim through the logs to find something meaningful.</p>
<p>Scrolling through thousands of lines of log in a monochrome style is no fun... (to me this approach sometimes is easier than debugging anyway).</p>
<p>First things first: how do I get this output?</p>
<p>When spinning up my tests in <a href="https://alexanderzeitler.com/articles/integration-testing-aspnet-core-mvc-identity-app-using-alba/">Alba</a>, I'm registering a XUnit sink for Serilog, so I get my logs redirected to the test output window in Rider:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">When_recording_a_lead_inquiry_from_email</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">IAsyncLifetime</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">When_recording_a_lead_inquiry_from_email</span><span class="token punctuation">(</span>
<span class="token class-name">ITestOutputHelper</span> testOutputHelper
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
_testOutputHelper <span class="token operator">=</span> testOutputHelper<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task</span> <span class="token function">InitializeAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> testServices <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">TestServices</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> configuration <span class="token operator">=</span> <span class="token keyword">await</span> testServices<span class="token punctuation">.</span><span class="token function">GetTestConfigurationRoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> serilogLogger <span class="token operator">=</span> Log<span class="token punctuation">.</span>Logger <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">LoggerConfiguration</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span>MinimumLevel<span class="token punctuation">.</span><span class="token function">Debug</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span>WriteTo<span class="token punctuation">.</span><span class="token function">Console</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span>WriteTo<span class="token punctuation">.</span><span class="token function">TestOutput</span><span class="token punctuation">(</span>_testOutputHelper<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">CreateLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> dotnetILogger <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">SerilogLoggerFactory</span><span class="token punctuation">(</span>serilogLogger<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">CreateLogger</span><span class="token generic class-name"><span class="token punctuation"><</span>Program<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> builder <span class="token operator">=</span> ConfigureHost<span class="token punctuation">.</span><span class="token function">GetHostBuilder</span><span class="token punctuation">(</span>
configuration<span class="token punctuation">,</span>
services <span class="token operator">=></span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddSingleton</span><span class="token generic class-name"><span class="token punctuation"><</span>ILogger<span class="token punctuation">></span></span></span><span class="token punctuation">(</span>dotnetILogger<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// arrange act assert...</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>My test output looks like this then:</p>
<p><img src="https://alexanderzeitler.com/articles/inspecting-aspnet-dotnet-integration-testing-logs-jetbrains-rider-grep-console/tail-black-white.png" alt="The joy of black text on white background" /></p>
<p>While scrolling through the look I was thinking "would be nice if there would be some color coding for the log in Rider...".</p>
<p>And the next step was to spin up a search which eventually ended up on the landing page for "<a href="https://plugins.jetbrains.com/plugin/7125-grep-console">GrepConsole</a>", a plugin for IntelliJ based IDEs like Rider which does exactly what I was looking for: colorize my test output logs.</p>
<p>After playing around with some settings, my logs now look like this:</p>
<p><img src="https://alexanderzeitler.com/articles/inspecting-aspnet-dotnet-integration-testing-logs-jetbrains-rider-grep-console/tail-grep-console.png" alt="much better, isn't it?" /></p>
<p>Much easier to spot exceptions, isn't it?</p>
<p>What's even better: you can add arbitrary expressions to filter and colorize fragments or lines.</p>
<p>So I set up a section for regular .NET log output colors and I added another one for <a href="http://wolverinefx.net/">Wolverine</a> status messages. That way I can easily spot successful or failed messages.</p>
<p>That's my GrepConsole config so far:</p>
<p><img src="https://alexanderzeitler.com/articles/inspecting-aspnet-dotnet-integration-testing-logs-jetbrains-rider-grep-console/grep-console-settings.png" alt="GrepConsole settings" /></p>
<p>Right now, there's only one downside: I didn't find a way in Rider to export/import these settings although this seems to work in other JetBrains IDEs.</p>
<h3>Update</h3>
<p>Looks like the settings get synced using repository sync 🎉️</p>
Exporting and importing Live Templates in JetBrains Rider2023-04-02T22:00:00Zhttps://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Exporting and importing Live Templates in JetBrains Rider" />
<meta name="twitter:description" content="Exporting and importing Live Templates in JetBrains Rider" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/cmanage-layers.png" />
<p>I love JetBrains Rider and I'm using it on every Platform: Xubuntu, macOS and Windows. That's great a great experience but sharing Live Templates between the platforms is a bit tricky.</p>
<p>Things might be easier if you're using <a href="https://www.jetbrains.com/help/rider/Sharing_Your_IDE_Settings.html#IDE_settings_sync">JetBrains sync</a> but I'm using the <a href="https://www.jetbrains.com/help/rider/Sharing_Your_IDE_Settings.html#settings-repository">repository sync for ages now...</a>.</p>
<p>The obvious option might be to use the "Save" button in the "Live Template" settings. However, if this would work, I wouldn't write this post.</p>
<p>The aforementioned option only export none .NET live templates.</p>
<p>So how do we do it for C# (and F# I guess)?</p>
<h3>Exporting</h3>
<p>First, open the "Manage Layers" dialog - I'm doing this via the Actions Panel (double shift keyboard shortcut). Then right click "This computer" (or where ever you want to export the live templates from) and select "Export to File...":</p>
<p><img src="https://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/manage-layers.png" alt="Manage Layers dialog" /></p>
<p>Then tick the checkbox "Live Templates":</p>
<p><img src="https://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/export-to-file.png" alt="Exporting the Live Templates" /></p>
<p>Next, assign a file name and you're done with exporting.</p>
<h3>Importing</h3>
<p>Bring up the "Manage Layers" dialog again and select "Import from" ➡️ "Import from File..."</p>
<p><img src="https://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/import-from-file.png" alt="Select import from File" /></p>
<p>Select a file containing exported Live Templates:</p>
<p><img src="https://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/select-import-file.png" alt="Select file to import" /></p>
<p>Tick the "LiveTemplates" checkbox</p>
<p><img src="https://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/select-live-templates-to-import.png" alt="Tick the Live Templates checkbox" /></p>
<p>And we're done 💥️</p>
<p>Just double check the templates have been imported:</p>
<p><img src="https://alexanderzeitler.com/articles/exporting-and-importing-jetbrains-rider-livetemplates/double-check-live-templates.png" alt="double check the templates have been imported" /></p>
I'm printing out websites - you might want to do it, too2023-04-01T20:00:00Zhttps://alexanderzeitler.com/articles/printing-out-websites/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="I'm printing out websites - you might want to do it, too" />
<meta name="twitter:description" content="I'm printing out websites - ou might want to do it, too" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/printing-out-websites/mufid-majnun-UdDCjj0rR7c-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@mufidpwt?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Mufid Majnun</a> on <a href="https://unsplash.com/photos/UdDCjj0rR7c?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/printing-out-websites/mufid-majnun-UdDCjj0rR7c-unsplash.jpg" alt="" /></p>
<p>I'm printing out websites on purpose.</p>
<p>This may sound like an April Fool's joke, but I'm serious.</p>
<p>But I don't print them on paper, I print them as PDF files for the following reasons:</p>
<p>I'm using <a href="https://logseq.com/">Logseq</a> as a "second brain".</p>
<p>Logseq comes with a ton of useful features and one of them are PDF annotations.</p>
<p>Here's the <a href="https://blog.logseq.com/newsletter-14-a-better-pdf-reader-sync-whiteboards-and-new-community-creations/">official video demoing</a> the feature:</p>
<div style="position: relative; padding-bottom: 62.5%; height: 0;"><iframe src="https://www.loom.com/embed/fa7ad4bb1dcd4d21b8f76e3e03c19d7f" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen="" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></iframe></div>
<p>Logseq to me by far is the most comfortable way to annotate websites across multiple computers and integrate them with my existing knowledge graph - if you print them to PDF and import them to Logseq.</p>
<p>Another win of this method: I won't loose the information if the website with the post goes dark at some point in time...</p>
<h3>The workflow</h3>
<p>Open an arbitrary blog post or article you want to annotate:</p>
<p><img src="https://alexanderzeitler.com/articles/printing-out-websites/website.png" alt="" /></p>
<p>Next, I'm activating Firefox Reader mode:</p>
<p><img src="https://alexanderzeitler.com/articles/printing-out-websites/website-reader-mode.png" alt="" /></p>
<p>Then, I'm calling the Print dialog and disable printing headers and footers:</p>
<p><img src="https://alexanderzeitler.com/articles/printing-out-websites/website-print.png" alt="" /></p>
<p>And finally, I can just drag and drop the PDF file into my Logseq page created for this post and start highlighting and annotating.</p>
<p><img src="https://alexanderzeitler.com/articles/printing-out-websites/logseq-pdf-annotation.png" alt="" /></p>
<p>How do you annotate and highlight websites?</p>
Integration testing an ASP.NET Core 7 app with ASP.NET Identity using Alba2023-03-20T22:00:00Zhttps://alexanderzeitler.com/articles/integration-testing-aspnet-core-mvc-identity-app-using-alba/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Integration testing an ASP.NET Core 7 app with ASP.NET Identity using Alba" />
<meta name="twitter:description" content="Integration testing an ASP.NET Core 7 app with ASP.NET Identity using Alba" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/integration-testing-aspnet-core-mvc-identity-app-using-alba/jeswin-thomas-dfRrpfYD8Iw-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@jeswinthomas?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Jeswin Thomas</a> on <a href="https://unsplash.com/photos/dfRrpfYD8Iw?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/integration-testing-aspnet-core-mvc-identity-app-using-alba/jeswin-thomas-dfRrpfYD8Iw-unsplash.jpg" alt="Wiring things up" /></p>
<p>As you might have noticed on Twitter that I started to evaluate <a href="http://wolverinefx.net/">Wolverine</a> (formerly known as "JasperFx") to become the Message Bus for my SaaS apps (e.g. kliento).</p>
<p>One of the benefits of Wolverine: it's part of the "Critter Stack for .NET".</p>
<p>If you want to understand what that means, I recommend <a href="https://jeremydmiller.com/2023/01/05/automating-integration-tests-using-the-critter-stack/">this blog post</a> by Jeremy D. Miller.</p>
<p>Another thing you might know about me (from <a href="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/">here</a>, <a href="https://alexanderzeitler.com/articles/aspnet-razor-pages-fragment-single-view-approach/">here</a> or <a href="https://alexanderzeitler.com/articles/serializing-dotnet-csharp-object-for-use-with-alpine-x-data-attribute/">here</a>), is that I'm using <a href="https://htmx.org/">HTMX</a> instead of SPA libraries like React.</p>
<p>This leads to the fact I'm using ASP.NET Identity for user management.</p>
<p>Now I wanted to refactor an ASP.NET Core 7 app with ASP.NET Identity to use Wolverine and being integration tested using Alba.</p>
<p>In a first step, I started to refactor my tests to Alba, so I could integration test async operations as described in the post by Jeremy and be prepared for a second refactoring to Wolverine.</p>
<p>And that's where things got interesting (read: went south).</p>
<p>The easiest way to get a test set up using Alba is possibly <a href="https://jasperfx.github.io/alba/guide/xunit.html">this</a>:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Fact</span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task</span> <span class="token function">should_say_hello_world</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment">// Alba will automatically manage the lifetime of the underlying host</span>
<span class="token keyword">await</span> <span class="token keyword">using</span> <span class="token class-name"><span class="token keyword">var</span></span> host <span class="token operator">=</span> <span class="token keyword">await</span> AlbaHost<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">For</span><span class="token generic class-name"><span class="token punctuation"><</span><span class="token keyword">global</span><span class="token punctuation">:</span><span class="token punctuation">:</span>Program<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// This runs an HTTP request and makes an assertion</span>
<span class="token comment">// about the expected content of the response</span>
<span class="token keyword">await</span> host<span class="token punctuation">.</span><span class="token function">Scenario</span><span class="token punctuation">(</span>_ <span class="token operator">=></span>
<span class="token punctuation">{</span>
_<span class="token punctuation">.</span>Get<span class="token punctuation">.</span><span class="token function">Url</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
_<span class="token punctuation">.</span><span class="token function">ContentShouldBe</span><span class="token punctuation">(</span><span class="token string">"Hello World!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
_<span class="token punctuation">.</span><span class="token function">StatusCodeShouldBeOk</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>That way the application under test will be bootstrapped in the exact same way like your production app.</p>
<p>Now you might want to run some tests in parallel using different databases (as you might have guessed, I'm using <a href="https://martendb.io/">Marten</a>) with dynamic connection strings being generated during test setup (note to self: this could be another blog post).</p>
<p>This means you need to have the same middleware pipeline etc. but with different parameters.</p>
<p>This all works pretty nice using Marten and other stuff but when it came to modifying the configuration for ASP.NET Identity at runtime or to add it with different configuration settings errors like these happened:</p>
<blockquote>
<p>InvalidOperationException: Scheme already exists: Identity.Application Microsoft.AspNetCore.Authentication.AuthenticationOptions.AddScheme(string name, Action configureBuilder)...</p>
</blockquote>
<p>So, I was in need of a different way to bootstrap my application under test - and in the end, the whole application (confirming my opinion that real world applications won't use the minimal APIs bootstrapping approach 🫢).</p>
<p>Lucky me, the Alba "<a href="https://jasperfx.github.io/alba/guide/gettingstarted.html">Getting started</a>" guide mentioned <a href="https://andrewlock.net/exploring-dotnet-6-part-2-comparing-webapplicationbuilder-to-the-generic-host/">this blog post</a> by Andrew Lock, in which he describes the evolution of ASP.NET Core application bootstrapping since the beginning of ASP.NET Core (if you follow all linked posts - you better grab some coffee before you start reading).</p>
<p>The solution I came up with is this - we'll start with a test asserting that a redirect to the login page happens when trying to access the application as an anonymous user:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">When_calling_app_as_anonymous_user</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">IAsyncLifetime</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token class-name">AlbaHost<span class="token punctuation">?</span></span> _host<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task</span> <span class="token function">InitializeAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> testServices <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">TestServices</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> testConfiguration <span class="token operator">=</span> <span class="token keyword">await</span> testServices<span class="token punctuation">.</span><span class="token function">GetTestConfigurationRoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> hostBuilder <span class="token operator">=</span> ConfigureHost<span class="token punctuation">.</span><span class="token function">GetHostBuilder</span><span class="token punctuation">(</span>testConfiguration<span class="token punctuation">)</span><span class="token punctuation">;</span>
_host <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">AlbaHost</span><span class="token punctuation">(</span>hostBuilder<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">await</span> _host<span class="token punctuation">.</span><span class="token function">MigrateIdentityDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Fact</span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task</span> <span class="token function">should_redirect_to_login</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">await</span> _host<span class="token punctuation">?.</span><span class="token function">Scenario</span><span class="token punctuation">(</span>
_ <span class="token operator">=></span>
<span class="token punctuation">{</span>
_<span class="token punctuation">.</span>Get<span class="token punctuation">.</span><span class="token function">Url</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
_<span class="token punctuation">.</span><span class="token function">Header</span><span class="token punctuation">(</span><span class="token string">"Location"</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">ShouldHaveValues</span><span class="token punctuation">(</span><span class="token string">"http://localhost/Identity/Account/Login?ReturnUrl=%2F"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
_<span class="token punctuation">.</span><span class="token function">StatusCodeShouldBe</span><span class="token punctuation">(</span><span class="token number">302</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task</span> <span class="token function">DisposeAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">await</span> _host<span class="token punctuation">.</span><span class="token function">DisposeAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p><code>TestServices</code> does a ton of stuff like handling the database bootstrapping/seeding mentioned above (yes, this really requires a blog post on it's own).</p>
<p><code>GetTestConfigurationRoot()</code>, as it's name suggests builds an in-memory configuration for things like databases, including the ASP.NET Identity database (each test has it's own test event store and identity database).</p>
<p>The key thing for this post however is <code>ConfigureHost.GetHostBuilder(testConfiguration)</code> which returns an <code>IHostBuilder</code> instance which Alba is happily using to create an <code>IAlbaHost</code> instance from:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ConfigureHost</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name">IHostBuilder</span> <span class="token function">GetHostBuilder</span><span class="token punctuation">(</span>
<span class="token class-name">IConfigurationRoot</span> configuration
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> hostBuilder <span class="token operator">=</span> Host<span class="token punctuation">.</span><span class="token function">CreateDefaultBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
hostBuilder<span class="token punctuation">.</span><span class="token function">AddLogging</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">SerilogLoggerFactory</span><span class="token punctuation">(</span>Log<span class="token punctuation">.</span>Logger<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">CreateLogger</span><span class="token generic class-name"><span class="token punctuation"><</span>Program<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
hostBuilder<span class="token punctuation">.</span><span class="token function">ConfigureWebHostDefaults</span><span class="token punctuation">(</span>
builder <span class="token operator">=></span>
<span class="token punctuation">{</span>
builder<span class="token punctuation">.</span><span class="token function">ConfigureServices</span><span class="token punctuation">(</span>collection <span class="token operator">=></span> collection<span class="token punctuation">.</span><span class="token function">ConfigureAppServices</span><span class="token punctuation">(</span>configuration<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span><span class="token function">UseConfiguration</span><span class="token punctuation">(</span>configuration<span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span><span class="token function">Configure</span><span class="token punctuation">(</span>
<span class="token punctuation">(</span>
context<span class="token punctuation">,</span>
app
<span class="token punctuation">)</span> <span class="token operator">=></span>
<span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>context<span class="token punctuation">.</span>HostingEnvironment<span class="token punctuation">.</span><span class="token function">IsDevelopment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
app<span class="token punctuation">.</span><span class="token function">UseExceptionHandler</span><span class="token punctuation">(</span><span class="token string">"/Error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseHsts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
app<span class="token punctuation">.</span><span class="token function">UseStaticFiles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseRouting</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseAuthentication</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseAuthorization</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseEndpoints</span><span class="token punctuation">(</span>
endpoints <span class="token operator">=></span>
<span class="token punctuation">{</span>
endpoints<span class="token punctuation">.</span><span class="token function">MapControllers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
endpoints<span class="token punctuation">.</span><span class="token function">MapRazorPages</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
endpoints<span class="token punctuation">.</span><span class="token function">MapDefaultControllerRoute</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">RequireAuthorization</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> hostBuilder<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This creates an ASP.NET Host with MVC, Controllers and Identity set up.</p>
<p>The magic happens in <code>collection.ConfigureAppServices(configuration))</code> - I removed the event store and other configuration stuff for brevity:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ConfigureServices</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name">IServiceCollection</span> <span class="token function">ConfigureAppServices</span><span class="token punctuation">(</span>
<span class="token keyword">this</span> <span class="token class-name">IServiceCollection</span> services<span class="token punctuation">,</span>
<span class="token class-name">IConfigurationRoot</span> configuration
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> identityTestConnectionString <span class="token operator">=</span> configuration<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"Identity"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddDbContext</span><span class="token generic class-name"><span class="token punctuation"><</span>IdentityDbContext<span class="token punctuation">></span></span></span><span class="token punctuation">(</span>
options <span class="token operator">=></span>
options<span class="token punctuation">.</span><span class="token function">UseNpgsql</span><span class="token punctuation">(</span>identityTestConnectionString<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddDefaultIdentity</span><span class="token generic class-name"><span class="token punctuation"><</span>AppUser<span class="token punctuation">></span></span></span><span class="token punctuation">(</span>options <span class="token operator">=></span> options<span class="token punctuation">.</span>SignIn<span class="token punctuation">.</span>RequireConfirmedAccount <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddEntityFrameworkStores</span><span class="token generic class-name"><span class="token punctuation"><</span>IdentityDbContext<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token function">AddControllersWithViews</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddRazorRuntimeCompilation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">Configure</span><span class="token generic class-name"><span class="token punctuation"><</span>RazorViewEngineOptions<span class="token punctuation">></span></span></span><span class="token punctuation">(</span>
o <span class="token operator">=></span> o<span class="token punctuation">.</span>ViewLocationExpanders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">FeatureFolderLocationExpander</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token function">AddMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> services<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>That way I can spin up concurrent async integration tests (the async part has to be validated when Wolverine is added to the game).</p>
<p>What's left is the application itself to be bootstrapped in the exact same way but with different configuration:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token class-name"><span class="token keyword">var</span></span> configuration <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ConfigurationBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddEnvironmentVariables</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddCommandLine</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddJsonFile</span><span class="token punctuation">(</span><span class="token string">"appsettings.json"</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddJsonFile</span><span class="token punctuation">(</span><span class="token interpolation-string"><span class="token string">$"appsettings.</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">Environment<span class="token punctuation">.</span><span class="token function">GetEnvironmentVariable</span><span class="token punctuation">(</span><span class="token string">"ASPNETCORE_ENVIRONMENT"</span><span class="token punctuation">)</span></span><span class="token punctuation">}</span></span><span class="token string">.json"</span></span><span class="token punctuation">,</span> <span class="token named-parameter punctuation">optional</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> builder <span class="token operator">=</span> ConfigureHost<span class="token punctuation">.</span><span class="token function">GetHostBuilder</span><span class="token punctuation">(</span>configuration<span class="token punctuation">)</span><span class="token punctuation">;</span>
builder
<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Well, that's it and works fine so far.</p>
<p>Maybe there's an easier / better solution - if so, please let me know!</p>
Entity Framework Core: Why you should never name your DbContext 'IdentityDbContext'2023-03-18T17:00:00Zhttps://alexanderzeitler.com/articles/entity-framework-core-why-you-should-never-name-your-db-context-identitydbcontext/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Entity Framework Core: Why you should never name your DbContent 'IdentityDbContext'" />
<meta name="twitter:description" content="Entity Framework Core: Why you should never name your DbContent 'IdentityDbContext'" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/entity-framework-core-why-you-should-never-name-your-db-context-identitydbcontext/no.png" />
<p><img src="https://alexanderzeitler.com/articles/entity-framework-core-why-you-should-never-name-your-db-context-identitydbcontext/no.png" alt="Don't do this" /></p>
<p>This is more or less a follow up to the <a href="https://alexanderzeitler.com/articles/identitygeneratortemplatemode2-does-not-contain-a-definition-for-usesqlite/">previous post</a>.</p>
<p>As said in the post already, I was playing around with <code>Startup.cs</code> style of application start up together with Entity Framework Core / ASP.NET Identity scaffolding.</p>
<p>So after I got Identity scaffolded, I re-added the <code>Startup.cs</code> and wanted to move on with migrations.</p>
<p>My <code>Program.cs</code> looked like this:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Program</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Main</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span></span> args
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token function">CreateHostBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name">IHostBuilder</span> <span class="token function">CreateHostBuilder</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span></span> args
<span class="token punctuation">)</span> <span class="token operator">=></span>
Host<span class="token punctuation">.</span><span class="token function">CreateDefaultBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">ConfigureWebHostDefaults</span><span class="token punctuation">(</span>webBuilder <span class="token operator">=></span> <span class="token punctuation">{</span> webBuilder<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">UseStartup</span><span class="token generic class-name"><span class="token punctuation"><</span>Startup<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This was the <code>Startup.cs</code>:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Startup</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IConfiguration</span> Configuration <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token function">Startup</span><span class="token punctuation">(</span>
<span class="token class-name">IHostEnvironment</span> env
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> builder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ConfigurationBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">SetBasePath</span><span class="token punctuation">(</span>env<span class="token punctuation">.</span>ContentRootPath<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddJsonFile</span><span class="token punctuation">(</span>
<span class="token string">"appsettings.json"</span><span class="token punctuation">,</span>
<span class="token named-parameter punctuation">optional</span><span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token named-parameter punctuation">reloadOnChange</span><span class="token punctuation">:</span> <span class="token boolean">true</span>
<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddJsonFile</span><span class="token punctuation">(</span><span class="token interpolation-string"><span class="token string">$"appsettings.</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">env<span class="token punctuation">.</span>EnvironmentName</span><span class="token punctuation">}</span></span><span class="token string">.json"</span></span><span class="token punctuation">,</span> <span class="token named-parameter punctuation">optional</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddEnvironmentVariables</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Configuration <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// This method gets called by the runtime. Use this method to add services to the container</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ConfigureServices</span><span class="token punctuation">(</span>
<span class="token class-name">IServiceCollection</span> services
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> connectionString <span class="token operator">=</span> Configuration<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"IdentityDbContextConnection"</span><span class="token punctuation">)</span> <span class="token operator">??</span>
<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">InvalidOperationException</span><span class="token punctuation">(</span>
<span class="token string">"Connection string 'IdentityDbContextConnection' not found."</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddDbContext</span><span class="token generic class-name"><span class="token punctuation"><</span>IdentityDbContext<span class="token punctuation">></span></span></span><span class="token punctuation">(</span>options <span class="token operator">=></span> options<span class="token punctuation">.</span><span class="token function">UseNpgsql</span><span class="token punctuation">(</span>connectionString<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddDefaultIdentity</span><span class="token generic class-name"><span class="token punctuation"><</span>AppUser<span class="token punctuation">></span></span></span><span class="token punctuation">(</span>options <span class="token operator">=></span> options<span class="token punctuation">.</span>SignIn<span class="token punctuation">.</span>RequireConfirmedAccount <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddEntityFrameworkStores</span><span class="token generic class-name"><span class="token punctuation"><</span>IdentityDbContext<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token function">AddControllersWithViews</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// This method gets called by the runtime. Use this method to configure the HTTP request pipeline</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Configure</span><span class="token punctuation">(</span>
<span class="token class-name">IApplicationBuilder</span> app<span class="token punctuation">,</span>
<span class="token class-name">IWebHostEnvironment</span> env
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment">// Configure the HTTP request pipeline.</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>env<span class="token punctuation">.</span><span class="token function">IsDevelopment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
app<span class="token punctuation">.</span><span class="token function">UseExceptionHandler</span><span class="token punctuation">(</span><span class="token string">"/Home/Error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.</span>
app<span class="token punctuation">.</span><span class="token function">UseHsts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
app<span class="token punctuation">.</span><span class="token function">UseHttpsRedirection</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseStaticFiles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseRouting</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseAuthorization</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseEndpoints</span><span class="token punctuation">(</span>
endpoints <span class="token operator">=></span>
<span class="token punctuation">{</span>
endpoints<span class="token punctuation">.</span><span class="token function">MapControllerRoute</span><span class="token punctuation">(</span>
<span class="token named-parameter punctuation">name</span><span class="token punctuation">:</span> <span class="token string">"default"</span><span class="token punctuation">,</span>
<span class="token named-parameter punctuation">pattern</span><span class="token punctuation">:</span> <span class="token string">"{controller=Home}/{action=Index}/{id?}"</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>The next step would be to create the first migration and seed the database:</p>
<pre class="language-shell"><code class="language-shell">dotnet ef migrations <span class="token function">add</span> CreateIdentitySchema</code></pre>
<p>Straight forward, one might think... but no:</p>
<blockquote>
<p>More than one DbContext was found. Specify which one to use. Use the '-Context' parameter for PowerShell commands and the '--context' parameter for dotnet commands.</p>
</blockquote>
<p>So, let's get a list of the contexts available using <code>dotnet ef dbcontext list</code>:</p>
<pre class="language-shell"><code class="language-shell">dotnet ef dbcontext list
Build started<span class="token punctuation">..</span>.
Build succeeded.
Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext
WebAppWithIdentity.Areas.Identity.Data.IdentityDbContext</code></pre>
<p>Ok, let's try to use a full qualified name then:</p>
<pre class="language-shell"><code class="language-shell">dotnet ef database update <span class="token parameter variable">--context</span> WebAppWithIdentity.Areas.Identity.Data.IdentityDbContext</code></pre>
<blockquote>
<p>Build started...
Build succeeded.
Unable to create an object of type 'IdentityDbContext'. For the different patterns supported at design time, see <a href="https://go.microsoft.com/fwlink/?linkid=851728">https://go.microsoft.com/fwlink/?linkid=851728</a></p>
</blockquote>
<p>That's not funny, tbh.</p>
<p>Well, let's go back to scaffolding and try this one (still with <code>Startup.cs</code> in use):</p>
<pre class="language-shell"><code class="language-shell">dotnet aspnet-codegenerator identity <span class="token parameter variable">-dc</span> SecurityDbContext <span class="token parameter variable">-u</span> AppUser <span class="token parameter variable">-f</span> <span class="token parameter variable">-gl</span> <span class="token parameter variable">-dbProvider</span> sqlite</code></pre>
<p>Result:</p>
<pre class="language-shell"><code class="language-shell">Building project <span class="token punctuation">..</span>.
Finding the generator <span class="token string">'identity'</span><span class="token punctuation">..</span>.
Running the generator <span class="token string">'identity'</span><span class="token punctuation">..</span>.
RunTime 00:00:17.25</code></pre>
<p>Umm... no let's list the contexts again:</p>
<pre class="language-shell"><code class="language-shell">dotnet ef dbcontext list
Build started<span class="token punctuation">..</span>.
Build succeeded.
WebAppWithIdentity.Areas.Identity.Data.SecurityDbContext</code></pre>
<p>Well, that's interesting.</p>
<p>Now, let's try to run the migrations:</p>
<pre class="language-shell"><code class="language-shell">dotnet ef migrations <span class="token function">add</span> CreateIdentitySchema
Build started<span class="token punctuation">..</span>.
Build succeeded.
Done. To undo this action, use <span class="token string">'ef migrations remove'</span></code></pre>
<p>Wow!</p>
<p>And the final step: apply the changes to the actual database:</p>
<pre class="language-shell"><code class="language-shell">dotnet ef database update
Build started<span class="token punctuation">..</span>.
Build succeeded.
<span class="token comment"># shortened the output a bit here</span>
info: Microsoft.EntityFrameworkCore.Database.Command<span class="token punctuation">[</span><span class="token number">20101</span><span class="token punctuation">]</span>
Executed DbCommand <span class="token punctuation">(</span>14ms<span class="token punctuation">)</span> <span class="token punctuation">[</span>Parameters<span class="token operator">=</span><span class="token punctuation">[</span><span class="token punctuation">]</span>, <span class="token assign-left variable">CommandType</span><span class="token operator">=</span><span class="token string">'Text'</span>, <span class="token assign-left variable">CommandTimeout</span><span class="token operator">=</span><span class="token string">'20'</span><span class="token punctuation">]</span>
CREATE UNIQUE INDEX <span class="token string">"UserNameIndex"</span> ON <span class="token string">"AspNetUsers"</span> <span class="token punctuation">(</span><span class="token string">"NormalizedUserName"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
info: Microsoft.EntityFrameworkCore.Database.Command<span class="token punctuation">[</span><span class="token number">20101</span><span class="token punctuation">]</span>
Executed DbCommand <span class="token punctuation">(</span>14ms<span class="token punctuation">)</span> <span class="token punctuation">[</span>Parameters<span class="token operator">=</span><span class="token punctuation">[</span><span class="token punctuation">]</span>, <span class="token assign-left variable">CommandType</span><span class="token operator">=</span><span class="token string">'Text'</span>, <span class="token assign-left variable">CommandTimeout</span><span class="token operator">=</span><span class="token string">'20'</span><span class="token punctuation">]</span>
INSERT INTO <span class="token string">"__EFMigrationsHistory"</span> <span class="token punctuation">(</span><span class="token string">"MigrationId"</span>, <span class="token string">"ProductVersion"</span><span class="token punctuation">)</span>
VALUES <span class="token punctuation">(</span><span class="token string">'20230318144833_CreateIdentitySchema'</span>, <span class="token string">'7.0.4'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Done.</code></pre>
<p>Ok, lessons learned:</p>
<p><strong>Never name your DbContent <code>IdentityDbContext</code></strong></p>
IdentityGeneratorTemplateMode2 does not contain a definition for "UseSQLite"2023-03-18T09:00:00Zhttps://alexanderzeitler.com/articles/identitygeneratortemplatemode2-does-not-contain-a-definition-for-usesqlite/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="IdentityGeneratorTemplateMode2 does not contain a definition for 'UseSQLite'" />
<meta name="twitter:description" content="IdentityGeneratorTemplateMode2 does not contain a definition for 'UseSQLite'" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/identitygeneratortemplatemode2-does-not-contain-a-definition-for-usesqlite/jackson-simmer-Vqg809B-SrE-unsplash.jpg" />
<p>The other day I tried to scaffold ASP.NET Identity for Sqlite (actually I wanted to scaffold it for Postgres but that's another story) from the CLI:</p>
<pre class="language-bash"><code class="language-bash">dotnet aspnet-codegenerator identity <span class="token parameter variable">-dc</span> IdentityDbContext <span class="token parameter variable">-u</span> AppUser <span class="token parameter variable">-f</span> <span class="token parameter variable">-gl</span> <span class="token parameter variable">-dbProvider</span> sqlite</code></pre>
<p>Straight forward, but I got this error:</p>
<blockquote>
<p>IdentityGeneratorTemplateMode2 does not contain a definition for 'UseSQLite'.</p>
</blockquote>
<p>The project I was trying to add Identity to, has been a spike where I was trying out some stuff using <a href="https://jasperfx.github.io/alba/"><code>Alba</code></a>.</p>
<p>One thing I had changed in the project compared to others where I was scaffolding Identity successfully, was that the project contained a <code>Startup.cs</code> file.</p>
<p>As I was stuck I just tried guessing and deleted the <code>Startup.cs</code> file and ran the scaffolding again.</p>
<p>And boom 💥, it did work successfully again.</p>
<p>Right now I don't know why the file caused this issue but I <a href="https://github.com/dotnet/Scaffolding/issues/2257#issuecomment-1473977842">provided my learning</a> to an existing GitHub issue so Microsoft can figure it out.</p>
<h3>Update</h3>
<p>Make sure to read the <a href="https://alexanderzeitler.com/articles/entity-framework-core-why-you-should-never-name-your-db-context-identitydbcontext/">follow up</a>.</p>
<small>
<small>
Twitter preview Photo by <a href="https://unsplash.com/@simmerdownjpg?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Jackson Simmer</a> on <a href="https://unsplash.com/photos/Vqg809B-SrE?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small>
</small>
Can't delete a note in PDF using Preview on macOS2023-03-11T19:00:00Zhttps://alexanderzeitler.com/articles/delete-note-in-pdf-preview-macos/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Can't delete a note in PDF using Preview on macOS" />
<meta name="twitter:description" content="Can't delete a note in PDF using Preview on macOS" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/delete-note-in-pdf-preview-macos/cant-delete-note-pdf-macos-preview.png" />
<p>While reviewing some PDF articles I took notes that I wanted to delete later on using macOS Preview app. But it didn't work as expected.</p>
<p>Here's the crime scene:</p>
<p><img src="https://alexanderzeitler.com/articles/delete-note-in-pdf-preview-macos/cant-delete-note-pdf-macos-preview.png" alt="" /></p>
<p>The most obvious things to try are hitting the backspace key (also together with ⇧ and/or ⌘). But this didn't work.</p>
<p>Lucky me, there's a workaround.</p>
<p>Open the "Inspector" using <code>⌘ + i</code> shortcut:</p>
<p><img src="https://alexanderzeitler.com/articles/delete-note-in-pdf-preview-macos/inspector.png" alt="" /></p>
<p>There you can select a note and once it's highlighted, it can be removed using the backspace key:</p>
<p><img src="https://alexanderzeitler.com/articles/delete-note-in-pdf-preview-macos/note-deleted.png" alt="" /></p>
ASP.NET Core developer certificate: ERR_SSL_VERSION_OR_CIPHER_MISMATCH on macOS2023-03-08T21:00:00Zhttps://alexanderzeitler.com/articles/asp-net-core-developer-certificate-err-ssl-version-or-cipher-mismatch-on-macos/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="ASP.NET Core developer certificate: ERR_SSL_VERSION_OR_CIPHER_MISMATCH on macOS" />
<meta name="twitter:description" content="ASP.NET Core developer certificate: ERR_SSL_VERSION_OR_CIPHER_MISMATCH on macOS" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/the-asp-net-core-developer-certificate-is-in-an-invalid-state-macos/kenny-eliason--Cmz06-0btw-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@neonbrand?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Kenny Eliason</a> on <a href="https://unsplash.com/s/photos/wrong?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/asp-net-core-developer-certificate-err-ssl-version-or-cipher-mismatch-on-macos/kenny-eliason--Cmz06-0btw-unsplash.jpg" alt="" /></p>
<p>I've been facing an issue with the ASP.NET developer certificates for ASP.NET Core 7 apps using HTTPS on macOS:</p>
<blockquote>
<p>ERR_SSL_VERSION_OR_CIPHER_MISMATCH</p>
</blockquote>
<p>Whatever I tried (<code>dotnet dev-certs https</code> [ <code>--clean</code> / <code>--trust</code> ]), I couldn't get it to work.</p>
<p>I also removed the certs manually from macOS Keychain.</p>
<p>Then I found <a href="https://github.com/dotnet/aspnetcore/issues/18236">this issue</a> on GitHub which suggested adding this <code>PropertyGroup</code> to the <code>csproj</code> file:</p>
<pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>PropertyGroup</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>UseAppHost</span><span class="token punctuation">></span></span>false<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>UseAppHost</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>PropertyGroup</span><span class="token punctuation">></span></span></code></pre>
<p>In fact it fixed the error.</p>
<p>Now I could use some help from you: Why does this setting fix it?</p>
Quickly navigate between Finder folders using the keyboard on macOS2023-03-05T08:00:00Zhttps://alexanderzeitler.com/articles/quick-navigate-to-folder-macos-finder-keyboard/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Quickly navigate between Finder folders using the keyboard on macOS" />
<meta name="twitter:description" content="Quickly navigate between Finder folders using the keyboard on macOS" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/quick-navigate-to-folder-macos-finder-keyboard/glenn-carstens-peters-npxXWgQ33ZQ-unsplash.jpg)" />
<p><small><small>
Photo by <a href="https://unsplash.com/@glenncarstenspeters?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Glenn Carstens-Peters</a> on <a href="https://unsplash.com/photos/npxXWgQ33ZQ?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/quick-navigate-to-folder-macos-finder-keyboard/glenn-carstens-peters-npxXWgQ33ZQ-unsplash.jpg" alt="" /></p>
<p>Recently I learned <a href="https://twitter.com/gittower/status/1626589872445325320">how to navigate to a folder quickly</a> when opening or saving files on macOS using <code>⇧ + ⌘ + G</code> on the keyboard.</p>
<p>This shortcut also works when having folders open in Finder:</p>
<p><img src="https://alexanderzeitler.com/articles/quick-navigate-to-folder-macos-finder-keyboard/quick-navigate-between-folders-shift-command-g-in-macos-finder-min.png" alt="Quick navigation between folders in macOS Finder" /></p>
ASP.NET: The default developer certificate could not be found2023-03-04T14:00:00Zhttps://alexanderzeitler.com/articles/aspnet-the-default-developer-certifcate-could-not-be-found-or-is-out-of-date/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="ASP.NET: The default developer certificate could not be found" />
<meta name="twitter:description" content="ASP.NET: The default developer certificate could not be found" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/aspnet-developer-certificate-ssl-error-protocol-version-alert/georg-bommeli-ybtUqjybcjE-unsplash.jpg" />
<p><small><small>
Photo by <a href="https://unsplash.com/@calina?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Georg Bommeli</a> on <a href="https://unsplash.com/photos/ybtUqjybcjE?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/aspnet-the-default-developer-certifcate-could-not-be-found-or-is-out-of-date/georg-bommeli-ybtUqjybcjE-unsplash.jpg" alt="" /></p>
<p>After playing around with ASP.NET developer certificates on macOS I couldn't get it back to work. Whenever the app started, I got this exception on startup:</p>
<blockquote>
<p>Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date.</p>
</blockquote>
<p>Whatever I tried (<code>dotnet dev-certs https</code> [ <code>--clean</code> / <code>--trust</code> ]), I couldn't get it to work.</p>
<p>I also removed the certs manually from macOS Keychain.</p>
<p>After a while I noticed, that in <code>~/.aspnet/dev-certs/https</code> even after a <code>dotnet dev-certs https --clean</code> there has still been one <code>.pfx</code> left, owned by <code>root</code>.</p>
<p>After deleting this one, ASP.NET picked up a fresh generated developer certifcate correctly again.</p>
Fixing my .NET 6 SDK installation after installing .NET SDK 7.0 on Xubuntu2023-01-27T20:00:00Zhttps://alexanderzeitler.com/articles/fixing-my-bricked-dotnet-sdk-6-installation-on-xubuntu-ubuntu-after-dotnet-7-sdk-installation/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Fixing my .NET 6 SDK installation after installing .NET SDK 7.0 on Xubuntu" />
<meta name="twitter:description" content="Fixing my .NET 6 SDK installation after installing .NET SDK 7.0 on Xubuntu" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/fixing-my-bricked-dotnet-sdk-6-installation-on-xubuntu-ubuntu-after-dotnet-7-sdk-installation/This_is_Fine_Gopher.jpg" />
<p><small><small>Artwork by <a href="https://twitter.com/ashleymcnamara">Ashley Willis</a> on <a href="https://github.com/ashleymcnamara/gophers/">GitHub</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/fixing-my-bricked-dotnet-sdk-6-installation-on-xubuntu-ubuntu-after-dotnet-7-sdk-installation/This_is_Fine_Gopher.jpg" alt="" /></p>
<p>I'm running on Xubuntu 22.04 day in day out working with .NET most of the time, so downtime is not an option.</p>
<p>.NET 6 SDK was installed using the <code>dotnet6</code> <a href="https://github.com/dotnet/core/issues/7699">package from Ubuntu</a>.</p>
<p>Shortly after Microsoft released .NET 7, I wanted to upgrade, but it did not work because of this error:</p>
<pre class="language-bash"><code class="language-bash">Package <span class="token string">'dotnet-sdk-7.0'</span> has no installation candidate</code></pre>
<p>So, no .NET 7 for me so far.</p>
<p>Last weekend has been the time when I finally wanted to install .NET 7 SDK side-by-side with the existing .NET 7 SDK because I wanted to test out some stuff:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">This 👇<a href="https://t.co/WdanSAlhvW">https://t.co/WdanSAlhvW</a><br /><br />And side projects ofc <a href="https://t.co/xrrLC8tV35">https://t.co/xrrLC8tV35</a> <a href="https://t.co/rzMkeyIHYR">pic.twitter.com/rzMkeyIHYR</a></p>— Alexander Zeitler (@lxztlr) <a href="https://twitter.com/lxztlr/status/1619121970796457985?ref_src=twsrc%5Etfw">January 27, 2023</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>As I knew there would be some trouble I pulled out the <a href="https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu">Microsoft installation Guide for .NET 7 on Ubuntu</a>. I also had an eye on the <a href="https://learn.microsoft.com/en-us/dotnet/core/install/linux-package-mixup">troubleshooting guide</a> for mixed installations like my scenario was.</p>
<p>So my first attempt has been this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">wget</span> https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb <span class="token parameter variable">-O</span> packages-microsoft-prod.deb
<span class="token function">sudo</span> dpkg <span class="token parameter variable">-i</span> packages-microsoft-prod.deb
<span class="token function">rm</span> packages-microsoft-prod.deb</code></pre>
<p>And it failed (obviously, because this would not be worth a blog post):</p>
<pre class="language-bash"><code class="language-bash">Reading package lists<span class="token punctuation">..</span>. Done
Building dependency tree<span class="token punctuation">..</span>. Done
Reading state information<span class="token punctuation">..</span>. Done
Package dotnet-sdk-7.0 is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another <span class="token builtin class-name">source</span>
E: Package <span class="token string">'dotnet-sdk-7.0'</span> has no installation candidate</code></pre>
<p>Back to the troubleshooting guide, I prioritized the Microsoft package feed for the <code>dotnet-sdk-7.0</code> package <a href="https://learn.microsoft.com/en-us/dotnet/core/install/linux-package-mixup#solutions">as described in the guide</a> by editing / adding <code>/etc/apt/preferences</code>:</p>
<pre class="language-bash"><code class="language-bash">Package: dotnet-sdk-7.0
Pin: origin <span class="token string">"packages.microsoft.com"</span>
Pin-Priority: <span class="token number">999</span></code></pre>
<p>Next <code>sudo apt update && sudo apt install dotnet-sdk-7.0</code>: no errors.</p>
<p>Let's try <code>dotnet --list-sdks</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token number">6.0</span>.405 <span class="token punctuation">[</span>/usr/share/dotnet/sdk<span class="token punctuation">]</span>
<span class="token number">7.0</span>.102 <span class="token punctuation">[</span>/usr/share/dotnet/sdk<span class="token punctuation">]</span></code></pre>
<p>Restrained joy. Should that have been it?</p>
<p>Of course - not.</p>
<p>Back to my major project, I noticed that .NET 6 SDK (via <code>dotnet run --framework net6.0</code>) stopped working:</p>
<pre class="language-bash"><code class="language-bash">dotnet run <span class="token parameter variable">--framework</span> net6.0
Building<span class="token punctuation">..</span>.
You must <span class="token function">install</span> or update .NET to run this application.
App: /home/alexzeitler/src/myproject/bin/Debug/net6.0/myproject
Architecture: x64
Framework: <span class="token string">'Microsoft.NETCore.App'</span>, version <span class="token string">'6.0.0'</span> <span class="token punctuation">(</span>x64<span class="token punctuation">)</span>
.NET location: /usr/share/dotnet
The following frameworks were found:
<span class="token number">7.0</span>.2 at <span class="token punctuation">[</span>/usr/share/dotnet/shared/Microsoft.NETCore.App<span class="token punctuation">]</span>
Learn about framework resolution:
https://aka.ms/dotnet/app-launch-failed
To <span class="token function">install</span> missing framework, download:
https://aka.ms/dotnet-core-applaunch?framework<span class="token operator">=</span>Microsoft.NETCore.App<span class="token operator">&</span><span class="token assign-left variable">framework_version</span><span class="token operator">=</span><span class="token number">6.0</span>.0<span class="token operator">&</span><span class="token assign-left variable">arch</span><span class="token operator">=</span>x64<span class="token operator">&</span><span class="token assign-left variable">rid</span><span class="token operator">=</span>ubuntu.22.04-x64</code></pre>
<p>Maybe add a <code>global.json</code> inside project folder?</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"sdk"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"6.0.405"</span><span class="token punctuation">,</span>
<span class="token property">"rollForward"</span><span class="token operator">:</span> <span class="token string">"latestMinor"</span><span class="token punctuation">,</span>
<span class="token property">"allowPrerelease"</span><span class="token operator">:</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Nobody likes your <code>global.json</code>, Alex.</p>
<p>"Maybe I should just move on and work with .NET 7 locally...".</p>
<p>Didn't want that, but I didn't want to invest more time and I also wanted to keep .NET 7 SDK installed.</p>
<p>So, I updated the <code>csproj</code> file like this to target .NET 6 and .NET 7:</p>
<pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TargetFrameworks</span><span class="token punctuation">></span></span>net6.0;net7.0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>TargetFrameworks</span><span class="token punctuation">></span></span></code></pre>
<p>Now I could use build and run my project locally using .NET 7 and tests did also work.</p>
<p>That's all nice but how about some broken CI builds?</p>
<p>Of course this didn't work as expected because the project has multiple targets now, and you need to specify a framework version in your Docker build.</p>
<p>"Well, that's easy".</p>
<p>No, it's not.</p>
<p><code>dotnet restore</code> doesn't understand <code>--framework</code> flag.</p>
<p>But: it does understand a MSBuild parameter called <code>TargetFramework</code>.</p>
<pre class="language-text"><code class="language-text">FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore -p:TargetFramework=net6.0</code></pre>
<p>So, this should also work for <code>dotnet publish</code> one might guess...</p>
<pre class="language-text"><code class="language-text">FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore -p:TargetFramework=net6.0
# Build and publish a release
RUN dotnet publish --framework net6.0 -p:TargetFramework=net6.0 -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "MyApp.dll"]</code></pre>
<p>Hard "no", again.</p>
<p>And it's not interested in your <code>--framework</code> flag also, Alex.</p>
<p>For whatever reason, this command did work outside the Docker build.</p>
<p>Meanwhile, I got a suggestion to try the .NET Core plugin for the <code>asdf</code> version manager and do a manual install (did I mention we really should have something like <code>nvm</code> for Node?).</p>
<p>I was tempted to walk this path, but something dragged me to take a look at the issues for the plugin first.</p>
<p>And there it was: <a href="https://github.com/emersonsoares/asdf-dotnet-core/issues/21">Rider can't detect dotnet sdk</a>.</p>
<p>And this ongoing issue dug <a href="https://github.com/emersonsoares/asdf-dotnet-core/issues/21">another rabbit hole</a>.</p>
<p>Ok, time to chop some wood.</p>
<p>It's been late in the evening and I did not want to start the next day with a broken installation, so I decided to wipe .NET SDK completely and start with a fresh install.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> remove dotnet-sdk-6.0 dotnet-sdk-7.0</code></pre>
<p>Now <code>dotnet --version</code> shouldn't work, but...</p>
<pre class="language-bash"><code class="language-bash"><span class="token number">6.0</span>.13</code></pre>
<p>Wait, what?</p>
<p>Turns out, there's still something left from the Ubuntu .NET 6 SDK install.
So comment out the settings in <code>/etc/apt/preferences</code>.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> remove dotnet6</code></pre>
<p>Finally, gone.</p>
<p>Next: reinstall .NET 6 SDK.</p>
<p>Undo the comment out of the settings in <code>/etc/apt/preferences</code>:</p>
<pre class="language-bash"><code class="language-bash">Package: dotnet-* aspnetcore-* netstandard-*
Pin: origin <span class="token string">"packages.microsoft.com"</span>
Pin-Priority: <span class="token number">999</span></code></pre>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> clean
<span class="token function">sudo</span> <span class="token function">apt</span> update
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> dotnet-sdk-6.0</code></pre>
<p>No errors.</p>
<pre class="language-bash"><code class="language-bash">dotnet <span class="token parameter variable">--version</span>
A fatal error occurred. The folder <span class="token punctuation">[</span>/usr/share/dotnet/host/fxr<span class="token punctuation">]</span> does not exist</code></pre>
<p>Great.</p>
<p>But <code>apt install</code> did tell me, the dependencies for <code>dotnet-sdk-6.0</code> have been installed...</p>
<p>Turns out, they have been installed from Ubuntu packages and <code>dotnet-sdk-6.0</code> has been installed from Microsoft packages.</p>
<p>So, remove everything again, but also the dependencies:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> remove dotnet-sdk-6.0
<span class="token function">sudo</span> <span class="token function">apt</span> autoremove <span class="token comment">#this will remove the unused dependencies </span></code></pre>
<p>Update the <code>/etc/apt/preferences</code> again. This time include all dependencies:</p>
<pre class="language-bash"><code class="language-bash">Package: dotnet-* aspnetcore-* netstandard-*
Pin: origin <span class="token string">"packages.microsoft.com"</span>
Pin-Priority: <span class="token number">999</span></code></pre>
<p>Next try:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> update
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> dotnet-sdk-6.0</code></pre>
<pre class="language-bash"><code class="language-bash">dotnet <span class="token parameter variable">--version</span>
<span class="token number">6.0</span>.405</code></pre>
<p>Good. Next: Run the app:</p>
<pre class="language-bash"><code class="language-bash">dotnet run
<span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Now listening on: https://0.0.0.0:5001
<span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Now listening on: http://0.0.0.0:5000
<span class="token punctuation">[</span>08:32:51 DBG<span class="token punctuation">]</span> Loaded hosting startup assembly MyProject
<span class="token punctuation">[</span>08:32:51 DBG<span class="token punctuation">]</span> Loaded hosting startup assembly Microsoft.AspNetCore.Watch.BrowserRefresh
<span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Application started. Press Ctrl+C to shut down.
<span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Hosting environment: Development
<span class="token punctuation">[</span>08:32:51 DBG<span class="token punctuation">]</span> Hosting started</code></pre>
<p>Ok, let's try <code>dotnet watch run</code>.</p>
<pre class="language-bash"><code class="language-bash">dotnet <span class="token function">watch</span> run
You must <span class="token function">install</span> .NET to run this application.
App: /home/alexzeitler/.dotnet/tools/dotnet-outdated
Architecture: x64
App <span class="token function">host</span> version: <span class="token number">6.0</span>.13
.NET location: Not found
Learn about runtime installation:
https://aka.ms/dotnet/app-launch-failed
Download the .NET runtime:
https://aka.ms/dotnet-core-applaunch?missing_runtime<span class="token operator">=</span>true<span class="token operator">&</span><span class="token assign-left variable">arch</span><span class="token operator">=</span>x64<span class="token operator">&</span><span class="token assign-left variable">rid</span><span class="token operator">=</span>ubuntu.22.04-x64<span class="token operator">&</span><span class="token assign-left variable">apphost_version</span><span class="token operator">=</span><span class="token number">6.0</span>.13</code></pre>
<p>Still something broken - but what?</p>
<p>Looks like it's not just me having issues: <a href="https://github.com/dotnet/runtime/issues/79237">.NET runtime not found on Ubuntu</a></p>
<p>Ok, let's follow <a href="https://github.com/dotnet/runtime/issues/79237#issuecomment-1338168740">the advice</a> and set <code>/etc/dotnet/install_location</code> to <code>/usr/share/dotnet</code>.</p>
<p>Then fix the missing <code>DOTNET_ROOT</code> environment variable and add (re-add?) that path to the <code>PATH</code> environment variable in <code>~/.zshrc</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">DOTNET_ROOT</span><span class="token operator">=</span>/usr/share/dotnet
<span class="token builtin class-name">export</span> <span class="token assign-left variable"><span class="token environment constant">PATH</span></span><span class="token operator">=</span><span class="token environment constant">$PATH</span><span class="token builtin class-name">:</span><span class="token variable">$DOTNET_ROOT</span></code></pre>
<p>Source the updated <code>.zshrc</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">.</span> ~/.zshrc</code></pre>
<p>Try a <code>dotnet</code> tool:</p>
<pre class="language-bash"><code class="language-bash">dotnet-outdated
Discovering projects<span class="token punctuation">..</span>.The directory <span class="token string">'/home/alex'</span> does not contain any solutions or projects.</code></pre>
<p>Fine. Try to <code>dotnet watch run</code> the project again:</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Now listening on: https://0.0.0.0:5001
<span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Now listening on: http://0.0.0.0:5000
<span class="token punctuation">[</span>08:32:51 DBG<span class="token punctuation">]</span> Loaded hosting startup assembly MyProject
<span class="token punctuation">[</span>08:32:51 DBG<span class="token punctuation">]</span> Loaded hosting startup assembly Microsoft.AspNetCore.Watch.BrowserRefresh
<span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Application started. Press Ctrl+C to shut down.
<span class="token punctuation">[</span>08:32:51 INF<span class="token punctuation">]</span> Hosting environment: Development
<span class="token punctuation">[</span>08:32:51 DBG<span class="token punctuation">]</span> Hosting started</code></pre>
<p>Ok. I can get my work done tomorrow.</p>
<p>But what about .NET 7 SDK?</p>
<p>"It's just 1:30am so why not give it a try again?"</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> update
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> dotnet-sdk-7.0</code></pre>
<p>No errors.</p>
<pre class="language-bash"><code class="language-bash">dotnet --list-sdks
<span class="token number">6.0</span>.405 <span class="token punctuation">[</span>/usr/share/dotnet/sdk<span class="token punctuation">]</span>
<span class="token number">7.0</span>.102 <span class="token punctuation">[</span>/usr/share/dotnet/sdk<span class="token punctuation">]</span></code></pre>
<p>Open the .NET 6 Project in Rider with only .NET 6 targeted again:</p>
<pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TargetFramework</span><span class="token punctuation">></span></span>net6.0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>TargetFramework</span><span class="token punctuation">></span></span></code></pre>
<p>Loads.</p>
<p>Try to build.</p>
<p>Works.</p>
<p>Try to run the tests.</p>
<p>Works.</p>
<p>Open a .NET 7 project.</p>
<p>Try to build.</p>
<p>Works.</p>
<p>Try to run the tests.</p>
<p>Works.</p>
<p>YES!</p>
ASP.NET Razor Pages Fragments / Single View approach2023-01-27T20:00:00Zhttps://alexanderzeitler.com/articles/aspnet-razor-pages-fragment-single-view-approach/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="ASP.NET Razor Pages Fragments / Single View approach" />
<meta name="twitter:description" content="ASP.NET Razor Pages Fragments / Single View approach" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/aspnet-razor-pages-fragment-single-view-approach/olga-deeva-0OMqNL2khKU-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/@loniel?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Olga Deeva</a> on <a href="https://unsplash.com/photos/0OMqNL2khKU?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/aspnet-razor-pages-fragment-single-view-approach/olga-deeva-0OMqNL2khKU-unsplash.jpg" alt="" /></p>
<p>I got some feedback for my <a href="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach">previous post</a>: Will the single view approach work with ASP.NET Razor Pages instead of MVC Views and Controllers?</p>
<p>Well, I'm a total Razor Pages noob, but this didn't stop me to give it a try.</p>
<p>Long story short: it works, but I might be doing it totally wrong.</p>
<p>Here's the source:</p>
<p>Biggest issue upfront: how do we get multiple GET handlers in our Page Model?</p>
<p>Jerrie Pelser has <a href="https://www.jerriepelser.com/blog/razor-pages-muliple-handlers/">a post</a> that solved it for me.</p>
<p>Our Page now has this route template:</p>
<pre class="language-html"><code class="language-html">@page "{handler?}"</code></pre>
<p>This makes stuff dynamic, and we can end up having multiple GET requests within the same Code for the page.</p>
<p>Thus, our page looks very similar to our MVC View except for the Model which is now a child property if the Page Model:</p>
<pre class="language-html"><code class="language-html">@page "{handler?}"
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
@{
void RenderDetail(ChildModel child)
{
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bg-gray-200<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<a href="@Url.PageLink("Index", "Details", new {Id = child.Id})">@child.Id<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
}
void RenderFull(ParentModel parent)
{
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bg-blue-500 p-4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
@{
@foreach (var parentChild in parent.Childs)
{
RenderDetail(parentChild);
}
}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
}
}
@{
switch (Model.PageModel.FragmentId)
{
case "Full":
RenderFull(Model.PageModel as ParentModel);
break;
case "Detail":
RenderDetail(Model.PageModel as ChildModel);
break;
default:
throw new ArgumentOutOfRangeException();
}
}</code></pre>
<p>The page model looks like this:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNetCore<span class="token punctuation">.</span>Mvc</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNetCore<span class="token punctuation">.</span>Mvc<span class="token punctuation">.</span>RazorPages</span><span class="token punctuation">;</span>
<span class="token keyword">namespace</span> <span class="token namespace">RazorPagesFragments<span class="token punctuation">.</span>Pages</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">IndexModel</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">PageModel</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token class-name">ILogger<span class="token punctuation"><</span>IndexModel<span class="token punctuation">></span></span> _logger<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token return-type class-name">FragmentModel</span> PageModel <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token operator">!</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token function">IndexModel</span><span class="token punctuation">(</span>
<span class="token class-name">ILogger<span class="token punctuation"><</span>IndexModel<span class="token punctuation">></span></span> logger
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
_logger <span class="token operator">=</span> logger<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">OnGet</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
PageModel <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ParentModel</span><span class="token punctuation">(</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">List<span class="token punctuation"><</span>ChildModel<span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">new</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token keyword">new</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">OnGetDetails</span><span class="token punctuation">(</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">FromQuery</span></span><span class="token punctuation">]</span> <span class="token class-name"><span class="token keyword">int</span></span> id
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
PageModel <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ChildModel</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FragmentModel</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">FragmentModel</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">string</span></span> fragmentId
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
FragmentId <span class="token operator">=</span> fragmentId<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> FragmentId <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ChildModel</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">FragmentModel</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token function">ChildModel</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">int</span></span> id
<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span><span class="token string">"Detail"</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
Id <span class="token operator">=</span> id<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ParentModel</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">FragmentModel</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name">List<span class="token punctuation"><</span>ChildModel<span class="token punctuation">></span></span> Childs <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token function">ParentModel</span><span class="token punctuation">(</span>
<span class="token class-name">List<span class="token punctuation"><</span>ChildModel<span class="token punctuation">></span></span> childs
<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span><span class="token string">"Full"</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
Childs <span class="token operator">=</span> childs<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>And that's it.</p>
<p>If I have missed something due to my lack of Razor Pages knowledge, feel fry to drop me a line how to improve it.</p>
<p>The full sample can be found <a href="https://github.com/AlexZeitler/razor-pages-fragments-sample">on GitHub</a>.</p>
A ASP.NET Razor Fragments / Single View approach - not only for HTMX2023-01-18T10:00:00Zhttps://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="A ASP.NET Core Razor Fragments / Single View approach for HTMX" />
<meta name="twitter:description" content="A ASP.NET Core Razor Fragments / Single View approach for HTMX" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/clint-adair-BW0vK-FA3eg-unsplash.jpg" />
<meta property="og:title" content="A ASP.NET Core Razor Fragments / Single View approach for HTMX" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/clint-adair-BW0vK-FA3eg-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/ja/@clintadair?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Clint Adair</a> on <a href="https://unsplash.com/photos/BW0vK-FA3eg?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
</small></small></p>
<p><img src="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/clint-adair-BW0vK-FA3eg-unsplash.jpg" alt="" /></p>
<p>A few months ago, the HTMX team published <a href="https://htmx.org/essays/template-fragments/">an essay on so-called template fragments</a> to adhere to the Locality of Behavior (and in my opinion also cohesion) principle in server side rendered views.</p>
<p>If you're using a template engine it is likely you end up with several views like typical parent / child/detail views:</p>
<p><img src="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/parent-child.svg" alt="" /></p>
<p>Until now, it has been common that this results in multiple view/component files. However, this comes with a disadvantage:</p>
<p>While separation of concern is improved now, in the same way cohesion and locality of behavior deteriorates:</p>
<p><strong>Locality of Behavior</strong></p>
<blockquote>
<p>The behaviour of a unit of code should be as obvious as possible by looking only at that unit of code</p>
</blockquote>
<p><strong>Cohesion</strong></p>
<blockquote>
<p>Cohesion refers to the degree to which the elements inside a module belong together.</p>
</blockquote>
<p>The proposed solution is to have a single template file but being able to address fragments of it by a key. By providing the key by e.g. a controller method calling the view engine, it will just render the fragment.</p>
<p>This is the proposed sample as an HTML file providing template fragment support:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">hx-target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>this<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
#fragment archive-ui
#if contact.archived
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">hx-patch</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/contacts/${contact.id}/unarchive<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Unarchive<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
#else
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">hx-delete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/contacts/${contact.id}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Archive<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
#end
#end
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>Contact<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>${contact.email}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>The fragment id here is <code>archive-ui</code>. So you can either render the full view or just two buttons.</p>
<p>When reading this essay after it has been published, it appealed to me a lot, but I didn't think it could be supported by ASP.NET Core Razor without extending the View engine.</p>
<p>So I went over to GitHub and created an <a href="https://github.com/dotnet/aspnetcore/issues/43713">issue</a>, requesting for that enhancement. Although it has been <a href="https://github.com/dotnet/aspnetcore/blob/main/docs/TriageProcess.md">triaged</a> and moved to the Backlog this won't be available soon (read: at least not in .NET 8).</p>
<p>After creating the issue, I was repeatedly annoyed by the lack of support, but did not pursue the issue further - until yesterday.</p>
<p>The topic came up again in the <a href="https://discord.com/channels/725789699527933952/963040198390861875">dotnet-htmx</a> channel on the HTMX discord server.</p>
<p>Based on the discussion, a rough idea formed in my head how this could be solved without extending Razor at all, getting full IDE support and all the like.</p>
<p>Here's what I came up with - let's just talk code:</p>
<p>First, we define some models:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FragmentModel</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">FragmentModel</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">string</span></span> fragmentId
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
FragmentId <span class="token operator">=</span> fragmentId<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> FragmentId <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ChildModel</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">FragmentModel</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token function">ChildModel</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">int</span></span> id
<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span><span class="token string">"Detail"</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
Id <span class="token operator">=</span> id<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ParentModel</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">FragmentModel</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name">List<span class="token punctuation"><</span>ChildModel<span class="token punctuation">></span></span> Childs <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token function">ParentModel</span><span class="token punctuation">(</span>
<span class="token class-name">List<span class="token punctuation"><</span>ChildModel<span class="token punctuation">></span></span> childs
<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span><span class="token string">"Full"</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
Childs <span class="token operator">=</span> childs<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Then we define a <strong>single</strong> View file:</p>
<pre class="language-csharp"><code class="language-csharp">@model RazorFragments<span class="token punctuation">.</span>Models<span class="token punctuation">.</span>FragmentModel
@<span class="token punctuation">{</span>
Layout <span class="token operator">=</span> <span class="token string">"_Layout"</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
@<span class="token punctuation">{</span>
<span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">RenderDetail</span><span class="token punctuation">(</span>
<span class="token class-name">ChildModel</span> child<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"bg-gray-200"</span><span class="token operator">></span>
@Html<span class="token punctuation">.</span><span class="token function">ActionLink</span><span class="token punctuation">(</span>child<span class="token punctuation">.</span>Id<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"Detail"</span><span class="token punctuation">,</span> <span class="token keyword">new</span>
<span class="token punctuation">{</span>
id <span class="token operator">=</span> child<span class="token punctuation">.</span>Id
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token punctuation">}</span>
<span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">RenderFull</span><span class="token punctuation">(</span>
<span class="token class-name">ParentModel</span> parent<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"bg-blue-500 p-4"</span><span class="token operator">></span>
@<span class="token punctuation">{</span>
@<span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">var</span></span> parentChild <span class="token keyword">in</span> parent<span class="token punctuation">.</span>Childs<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token function">RenderDetail</span><span class="token punctuation">(</span>parentChild<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
@<span class="token punctuation">{</span>
<span class="token keyword">switch</span> <span class="token punctuation">(</span>Model<span class="token punctuation">.</span>FragmentId<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">case</span> <span class="token string">"Full"</span><span class="token punctuation">:</span>
<span class="token function">RenderFull</span><span class="token punctuation">(</span>Model <span class="token keyword">as</span> <span class="token class-name">ParentModel</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">case</span> <span class="token string">"Detail"</span><span class="token punctuation">:</span>
<span class="token function">RenderDetail</span><span class="token punctuation">(</span>Model <span class="token keyword">as</span> <span class="token class-name">ChildModel</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>And everything gets tied together in our controller:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">RazorFragmentController</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">Controller</span></span>
<span class="token punctuation">{</span>
<span class="token comment">// GET</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IActionResult</span> <span class="token function">Index</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">View</span><span class="token punctuation">(</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">ParentModel</span><span class="token punctuation">(</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">List<span class="token punctuation"><</span>ChildModel<span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">new</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token keyword">new</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Route</span><span class="token attribute-arguments"><span class="token punctuation">(</span><span class="token string">"/detail/{id}"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IActionResult</span> <span class="token function">Detail</span><span class="token punctuation">(</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">FromRoute</span></span><span class="token punctuation">]</span> <span class="token class-name"><span class="token keyword">int</span></span> id
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">View</span><span class="token punctuation">(</span><span class="token string">"Index"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ChildModel</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This is just a first draft stitched together in the middle of the night so I'm curious about your thoughts, feedback and suggestions for improvement.</p>
<p>The full sample can be found <a href="https://github.com/AlexZeitler/htmx-aspnet-razor-fragments">on GitHub</a>.</p>
<h2><strong>Update (2023/01/26)</strong></h2>
<p>I created <a href="https://github.com/AlexZeitler/razor-sections-layout-with-fragments">another sample</a> which is closer to the mockup in from the intro:</p>
<p><strong>List view</strong>
<img src="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/screenshot_list.png" alt="List view" /></p>
<p><strong>Details view</strong>
<img src="https://alexanderzeitler.com/articles/htmx-razor-fragment-single-view-approach/screenshot_details.png" alt="Details view" /></p>
Serializing .NET objects for use with Alpine.js x-data attribute2023-01-15T20:00:00Zhttps://alexanderzeitler.com/articles/serializing-dotnet-csharp-object-for-use-with-alpine-x-data-attribute/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Serializing .NET objects for use with Alpine.js x-data attribute" />
<meta name="twitter:description" content="Serializing .NET objects for use with Alpine.js x-data attribute" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/serializing-dotnet-csharp-object-for-use-with-alpine-x-data-attribute/clint-patterson-CgIFBwOkApI-unsplash.jpg" />
<meta property="og:title" content="Serializing .NET objects for use with Alpine.js x-data attribute" />
<meta property="og:url" content="https://alexanderzeitler.com/articles/serializing-dotnet-csharp-object-for-use-with-alpine-x-data-attribute/" />
<meta property="og:image" content="https://alexanderzeitler.com/articles/serializing-dotnet-csharp-object-for-use-with-alpine-x-data-attribute/clint-patterson-CgIFBwOkApI-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/ja/@cbpsc1?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Clint Patterson</a> on <a href="https://unsplash.com/photos/CgIFBwOkApI?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></small></small></p>
<p><img src="https://alexanderzeitler.com/articles/serializing-dotnet-csharp-object-for-use-with-alpine-x-data-attribute/clint-patterson-CgIFBwOkApI-unsplash.jpg" alt="Serialization in progress..." /></p>
<p>Alpine.js allows you set javascript objects in the scope of a HTML tag using the <code>x-data</code> attribute:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ open: false }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">@click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open = ! open<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Toggle Content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-show</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
Content...
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The data is a plain javascript object whose properties are available to all child elements of the outer <code>div</code>.</p>
<p>Clicking the button will show/hide the content of the inner <code>div</code>.</p>
<p>In a typical SPA like setup you might use <code>fetch</code> together with Alpine's <code>json</code> function to include server side data:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span>
<span class="token attr-name">x-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ posts: [] }<span class="token punctuation">"</span></span>
<span class="token attr-name">x-init</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>posts = await (await fetch('/posts')).json()<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>But what to do if you want to render the Razor view and let it include the server side object directly into Alpine's <code>x-data</code> attribute? The closest you can get using out of the box serialization is this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>@Html.Raw(Json.Serialize(new { Open = false}))<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>This will render the expected output, and you can toggle the content using the button.</p>
<p>But as you may have noticed, the <code>x-data</code> attribute in the last sample is using single quotes. When using double quotes, the code will break at runtime. Your single quotes might get replaced by double quotes due to IDE settings on when saving the <code>cshtml</code> file.</p>
<p>A better solution would be to use single quotes for the javascript object string and date properties.</p>
<p>One approach could be to replace the instance of <code>IJsonHelper</code> which is being called by <code>Json.Serialize</code> inside the view.</p>
<p>Yet, the implementation is using <code>System.Text.Json</code> which neither allows to use non-quoted property names nor use a different quote char.</p>
<p>So, back to <code>Newtonsoft.Json</code> - here's a possible solution (we could also create a new implementation of <code>IJSonHelper</code>, of course):</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">JavaScriptConverter</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name">IHtmlContent</span> <span class="token function">SerializeObject</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">object</span></span> <span class="token keyword">value</span>
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">using</span> <span class="token class-name"><span class="token keyword">var</span></span> stringWriter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">StringWriter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token class-name"><span class="token keyword">var</span></span> jsonWriter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">JsonTextWriter</span><span class="token punctuation">(</span>stringWriter<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> serializer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">JsonSerializer</span>
<span class="token punctuation">{</span>
ContractResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">CamelCasePropertyNamesContractResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
jsonWriter<span class="token punctuation">.</span>QuoteName <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
jsonWriter<span class="token punctuation">.</span>QuoteChar <span class="token operator">=</span> <span class="token char">'\''</span><span class="token punctuation">;</span>
serializer<span class="token punctuation">.</span><span class="token function">Serialize</span><span class="token punctuation">(</span>jsonWriter<span class="token punctuation">,</span> <span class="token keyword">value</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">HtmlString</span><span class="token punctuation">(</span>stringWriter<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Using the <code>SerializeObject</code> method, we solve three tasks:</p>
<p>.NET objects get serialized</p>
<ul>
<li>using camelCase</li>
<li>without quotes in property names, hence creating a Javascript object</li>
<li>using single quotes for string and date properties</li>
</ul>
<p>Usage inside the view changed to following:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@JavaScriptSerializer.SerializeObject(new { Open = false})<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>This is less error-prone but still feels a bit cumbersome.</p>
<p>So let's wrap the <code>JavaScriptSerializer</code> call using a Tag Helper:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">HtmlTargetElement</span><span class="token attribute-arguments"><span class="token punctuation">(</span><span class="token string">"*"</span><span class="token punctuation">,</span> Attributes <span class="token operator">=</span> <span class="token string">"alpine-data"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AlpineTagHelper</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">TagHelper</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">override</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Process</span><span class="token punctuation">(</span>
<span class="token class-name">TagHelperContext</span> context<span class="token punctuation">,</span>
<span class="token class-name">TagHelperOutput</span> output
<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
output<span class="token punctuation">.</span>Attributes<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token string">"x-data"</span><span class="token punctuation">,</span> JavaScriptConverter<span class="token punctuation">.</span><span class="token function">SerializeObject</span><span class="token punctuation">(</span>Data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">Process</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> output<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">HtmlAttributeName</span><span class="token attribute-arguments"><span class="token punctuation">(</span><span class="token string">"alpine-data"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">object</span></span> Data <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token operator">!</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now lets see how we can use this one.</p>
<p>First, register the Tag Helper in <code>_ViewImports.cshtml</code>:</p>
<pre class="language-text"><code class="language-text">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Alpine.TagHelpers</code></pre>
<p>And just use the Tag Helper:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">alpine-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>new { Open = false }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name"><span class="token namespace">x-on:</span>click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open = !open<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Show<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-show</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open<span class="token punctuation">"</span></span> <span class="token attr-name">x-cloak</span><span class="token punctuation">></span></span>Some details...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The rendered result:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{open:false}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name"><span class="token namespace">x-on:</span>click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open = !open<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Show<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">x-show</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Some details...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The Tag Helper is available as a <a href="https://www.nuget.org/packages/Alpine.TagHelpers">NuGet package</a>:</p>
<pre class="language-bash"><code class="language-bash">dotnet <span class="token function">add</span> package Alpine.TagHelpers</code></pre>
Dynamic subdomain per tenant using nginx and Docker2023-01-11T08:00:00Zhttps://alexanderzeitler.com/articles/dynamic-subdomains-per-tenant-using-nginx-and-docker/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Dynamic subdomain per tenant using nginx and Docker" />
<meta name="twitter:description" content="Dynamic subdomain per tenant using nginx and Docker" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/dynamic-subdomains-per-tenant-using-nginx-and-docker/jose-martin-ramirez-carrasco-yhNVwsKTSaI-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/@martinirc?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">José Martín Ramírez Carrasco</a> on <a href="https://unsplash.com/photos/yhNVwsKTSaI?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></small></small></p>
<p><img src="https://alexanderzeitler.com/articles/dynamic-subdomains-per-tenant-using-nginx-and-docker/jose-martin-ramirez-carrasco-yhNVwsKTSaI-unsplash.jpg" alt="" /></p>
<p>Even if you don't use B2B SaaS, you might have seen urls like <a href="https://theleaddeveloper.slack.com/">https://theleaddeveloper.slack.com</a>, where you can have your own subdomain on slack.com for your Slack channel.</p>
<p>While playing around with some nginx settings, I noticed that this could actually be used for dynamic subdomains per tenant.</p>
<p>Long story short, here's the solution (which can certainly be optimized):</p>
<pre class="language-nginx"><code class="language-nginx"><span class="token comment"># sample shows only relevant part of the config, the rest is omitted for the sake of brevity</span>
<span class="token directive"><span class="token keyword">server</span></span> <span class="token punctuation">{</span>
<span class="token directive"><span class="token keyword">listen</span> <span class="token number">443</span> ssl</span><span class="token punctuation">;</span>
<span class="token directive"><span class="token keyword">server_name</span> *.tempuri.org</span><span class="token punctuation">;</span>
<span class="token directive"><span class="token keyword">location</span> /</span> <span class="token punctuation">{</span>
<span class="token directive"><span class="token keyword">set</span> <span class="token variable">$tenant</span> <span class="token string">""</span></span><span class="token punctuation">;</span>
<span class="token directive"><span class="token keyword">if</span> (<span class="token variable">$host</span> ~* <span class="token string">"^(.+)\.tempuri.org$"</span>)</span> <span class="token punctuation">{</span>
<span class="token directive"><span class="token keyword">set</span> <span class="token variable">$tenant</span> <span class="token variable">$1</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token directive"><span class="token keyword">resolver</span> 127.0.0.11</span><span class="token punctuation">;</span>
<span class="token directive"><span class="token keyword">proxy_pass</span> http://app/tenant?slug=<span class="token variable">$tenant</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>So what's going on there?</p>
<p>Given our domain is <code>tempuri.org</code>, we want tenants to be available via <code>tenantname.tempuri.org</code>, hence our nginx server is listening on <code>*.tempuri.org</code>.</p>
<p>Next we're using a little regular expression to pick up the name of the subdomain (which equals our tenant name/slug) the user entered.</p>
<p>After that we set the DNS resolver to <code>127.0.0.11</code> (yes: eleven) which is the internal Docker DNS resolver. That's required to make sure the host name <code>app</code> used for the <code>proxy_pass</code> directive can be resolved. <code>app</code> is the name of the container hosting the actual application which e.g. shows a branded login screen or third party identity providers per tenant.</p>
Resizung a Linux (Ubuntu/Xubuntu/Kubuntu) application window exactly2022-06-20T14:00:00Zhttps://alexanderzeitler.com/articles/resizing-a-linux-ubuntu-application-window-exactly/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Resizung a Linux (Ubuntu/Xubuntu/Kubuntu) application window exactly" />
<meta name="twitter:description" content="Resizung a Linux (Ubuntu/Xubuntu/Kubuntu) application window exactly" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/resizing-a-linux-ubuntu-application-window-exactly/adeolu-eletu-ohh8ROaQSJg-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/@adeolueletu?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Adeolu Eletu</a> on <a href="https://unsplash.com/s/photos/window?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></small></small></p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-linux-ubuntu-application-window-exactly/adeolu-eletu-ohh8ROaQSJg-unsplash.jpg" alt="" /></p>
<p>Sometimes you need a screenshot of an application window with specific dimensions.</p>
<p>On Linux, you can use the <code>xdotool</code> commandline tool, which is a fake keyboard/mouse input and window management tool for x11.</p>
<p>Using <code>xdotool</code>, resizing a Firefox window to 1024 x 768 pixels is as easy as this command:</p>
<pre class="language-shell"><code class="language-shell">xdotool search <span class="token string">"Mozilla Firefox"</span> windowsize <span class="token number">1024</span> <span class="token number">768</span></code></pre>
<p>However, knowing the right window name might fail you, there's a even better solution:</p>
<pre class="language-shell"><code class="language-shell">xdotool selectwindow windowsize <span class="token number">1024</span> <span class="token number">768</span></code></pre>
<p>Now you can just run the command, click a window - and 💥 - the selected window is resized.</p>
<p>⚠️ <a href="https://github.com/jordansissel/xdotool#xdotool---x11-automation-tool">Note</a>: If you are using Wayland, please be aware this software will not work correctly. ⚠️</p>
Deconstructing a C# record with properties2022-06-06T22:00:00Zhttps://alexanderzeitler.com/articles/deconstructing-a-csharp-record-with-properties/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Deconstructing a C# record with properties" />
<meta name="twitter:description" content="Deconstructing a C# record with properties" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/deconstructing-a-csharp-record-with-properties/florian-klauer--K6JMRMj4x4-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/@florianklauer?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Florian Klauer</a> on <a href="https://unsplash.com/s/photos/deconstruct?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></small>
</small></p>
<p><img src="https://alexanderzeitler.com/articles/deconstructing-a-csharp-record-with-properties/florian-klauer--K6JMRMj4x4-unsplash.jpg" alt="" /></p>
<p>Today I tried to deconstruct a C# record with properties and I failed.</p>
<p>First, how do you deconstruct a record with positional parameters?</p>
<p>It's as easy like this:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Person</span><span class="token punctuation">(</span>
<span class="token class-name"><span class="token keyword">string</span></span> FirstName<span class="token punctuation">,</span>
<span class="token class-name"><span class="token keyword">string</span></span> LastName
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> janeDoe <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Person</span><span class="token punctuation">(</span><span class="token string">"Jane"</span><span class="token punctuation">,</span> <span class="token string">"Doe"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> <span class="token punctuation">(</span>lastName<span class="token punctuation">,</span> firstName<span class="token punctuation">)</span> <span class="token operator">=</span> janeDoe<span class="token punctuation">;</span></code></pre>
<p>That's it.</p>
<p>My naïve approach (the record is simplyfied for brevity) to deconstruct a record with properties was like this:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> Firstname <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">init</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> Lastname <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">init</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Next, I created an instance:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token class-name"><span class="token keyword">var</span></span> janeDoe <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Firstname <span class="token operator">=</span> <span class="token string">"Jane"</span><span class="token punctuation">,</span> Lastname <span class="token operator">=</span> <span class="token string">"Doe"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Now I tried to deconstruct it like I would do with the first sample:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">var</span> <span class="token punctuation">(</span>lastName<span class="token punctuation">,</span> firstName<span class="token punctuation">)</span> <span class="token operator">=</span> janeDoe<span class="token punctuation">;</span></code></pre>
<p>And I got this compiler error:</p>
<pre class="language-bash"><code class="language-bash">$ No <span class="token string">'Deconstruct'</span> method with <span class="token number">2</span> out parameters found <span class="token keyword">for</span> <span class="token builtin class-name">type</span> <span class="token string">'Person'</span></code></pre>
<p>After reading this error message several times, I got it: the compiler is expecting a method name <code>Deconstruct</code> like this on my <code>Person</code> record type:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> Firstname <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">init</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> Lastname <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">init</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Deconstruct</span><span class="token punctuation">(</span>
<span class="token keyword">out</span> <span class="token class-name"><span class="token keyword">string</span></span> firstName<span class="token punctuation">,</span>
<span class="token keyword">out</span> <span class="token class-name"><span class="token keyword">string</span></span> lastName
<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>firstName<span class="token punctuation">,</span> lastName<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token punctuation">(</span>Firstname<span class="token punctuation">,</span> Lastname<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Deconstruction now works as expected:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">var</span> <span class="token punctuation">(</span>lastName<span class="token punctuation">,</span> firstName<span class="token punctuation">)</span> <span class="token operator">=</span> janeDoe<span class="token punctuation">;</span></code></pre>
<p>The reason for this issue is quite simple: The C# compiler auto-generates the <code>Deconstruct</code> method when you have a record type with positional parameters but not for record types with properties.</p>
Writing is thinking - and more2022-05-30T02:00:00Zhttps://alexanderzeitler.com/articles/writing-is-thinking-and-more/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Writing is thinking - and more" />
<meta name="twitter:description" content="Writing is thinking - and more" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/writing-is-thinking-and-more/yannick-pulver-hopX_jpVtRM-unsplash.jpg" />
<p><small><small>Photo by <a href="https://unsplash.com/@yanu?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Yannick Pulver</a> on <a href="https://unsplash.com/s/photos/writing?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></small></small>
<img src="https://alexanderzeitler.com/articles/writing-is-thinking-and-more/yannick-pulver-hopX_jpVtRM-unsplash.jpg" alt="" /></p>
<p>Recently I came across this tweet:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Writing is not a result of thinking. <br /><br />Writing is thinking.</p>— Tiago Forte (@fortelabs) <a href="https://twitter.com/fortelabs/status/1530901044200448000?ref_src=twsrc%5Etfw">May 29, 2022</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>This resonates very much with my experience and I want to share it here.</p>
<p>This weekend I did nothing but read and write, with writing predominating.
I wrote concepts, ideas and strategies for <a href="https://twitter.com/klientoapp">@klientoapp</a>.</p>
<p>Writing, in my opinion, is even more important when you are a founder on your own. It gives you the opportunity to talk to yourself and reflect on thoughts without having to keep them in your head. This relieves the brain and reduces the cognitive load and avoids multitasking with thoughts.</p>
<p>This, in turn, creates capacity for more thoughts and gets you into flow faster because you can immediately "dump" the brain again with each new thought since you already have all the tools at your disposal (pen and paper or a note-taking app).</p>
<p>When working and developing alone, it's easy to fall into the trap of immediately starting to write code. This leads neither to structured code nor to well thought-out features. You quickly produce a ball of mud and have no clear structure. Indicators are unclear commit messages, no releasable artifacts, and so on.</p>
<p>You work away and never come to a defined end, because without a concept you often overlook and forget important things.</p>
<p>Writing also helps with prioritization. If you write down the features, you know on the one hand the scope and on the other hand you recognize whether this is a USP, commodity or nice to have. You can prioritize accordingly.</p>
<p>When you write, thoughts arise that are not only related to the features. You think more strategically, e.g. also in the direction of investors, hiring etc.</p>
<p>Writing is also helpful when you take a break from a task, e.g. writing code, without losing the context, because you can always pick up the task again when reading what you have written.</p>
<p>I've also gotten into the habit of creating a "TIL" document for each day. This is where notes go in about things I've learned. This can be personal, about the domain or code related on a project. If there are learnings that I can use in other contexts, I store them outside the project and only reference them in the TIL document.</p>
<p>As you may have noticed, I'm <a href="https://www.zotero.org/">Zotero</a> and <a href="https://www.zettlr.com/">Zettlr</a> for this.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">I am also in the process of using the Zettelkasten method.<br />I use <a href="https://twitter.com/zettlr?ref_src=twsrc%5Etfw">@zettlr</a> as it also provides a graphic of the "Zettel" (notes).<br />The recommended book is a great read on this topic. <a href="https://t.co/g2wd24x4JT">https://t.co/g2wd24x4JT</a></p>— Alexander Zeitler (@lxztlr) <a href="https://twitter.com/lxztlr/status/1524282453661212672?ref_src=twsrc%5Etfw">May 11, 2022</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>In Zotero I put everything I want to reference, while in Zettlr I write down information in my own words to understand the thought behind it later. I don't make copies of external information in Zettlr - such things are stored in Zotero.</p>
<p>All notes and highlights I make in books and on my kindle I also put in Zotero. That way I avoid having to deal with different media and formats in the long run. Notes and highlights I made in a book or kindle I can delete afterwards, since everything is in Zotero. This also means I'm not dependent on a platform like kindle and when I search I only have to search in one place: for things I've read, listened to or watched it's Zotero, for my own thoughts it's Zettlr. So again, I avoid unnecessary strain on the brain.</p>
<p>Another habit it got into is summarizing inquiries from prospects in my own words after the initial conversation (this can be quite detailed). At the end, I have a list of questions that I can discuss with the prospect and that they can distribute at their company as well. If I receive feedback on the questions, I expand my document. The questions remain, the answers are then also with the questions and so I can reference questions and answers again at the appropriate place. Thus, I have a living documentation of the inquiry process. Of course, this can also be maintained after I got the job...</p>
<p>How do you organize your thoughts and learnings?</p>
Refactoring-safe nested validation with express-validator and ts-simple-nameof2021-08-10T01:00:00Zhttps://alexanderzeitler.com/articles/type-safe-nested-validation-with-express-validator-and-ts-simple-nameof/<p><a href="https://www.npmjs.com/package/express-validator"><code>express-validator</code></a> is a powerful validation and sanitation library.</p>
<p>However, it relies on string property names for the validation to work.</p>
<p>This is not type and refactoring safe, hence error prone.</p>
<p>Let's see if we can fix this...</p>
<p>First, a litte example of how <code>express-validator</code> works (from the official example):</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> body<span class="token punctuation">,</span> validationResult <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"express-validator"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token keyword">require</span><span class="token punctuation">(</span><span class="token string">"express"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>express<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">"/user"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
User<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
username<span class="token operator">:</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>username<span class="token punctuation">,</span>
password<span class="token operator">:</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>password<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span> <span class="token operator">=></span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span>
<span class="token string">"/user"</span><span class="token punctuation">,</span>
<span class="token comment">// username must be an email</span>
<span class="token function">body</span><span class="token punctuation">(</span><span class="token string">"username"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isEmail</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token comment">// password must be at least 5 chars long</span>
<span class="token function">body</span><span class="token punctuation">(</span><span class="token string">"password"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isLength</span><span class="token punctuation">(</span><span class="token punctuation">{</span> min<span class="token operator">:</span> <span class="token number">5</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// Finds the validation errors in this request and wraps them in an object with handy functions</span>
<span class="token keyword">const</span> errors <span class="token operator">=</span> <span class="token function">validationResult</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>errors<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span> errors<span class="token operator">:</span> errors<span class="token punctuation">.</span><span class="token function">array</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
User<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
username<span class="token operator">:</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>username<span class="token punctuation">,</span>
password<span class="token operator">:</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>password<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span> <span class="token operator">=></span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>As you can see, <code>body('username').isEmail()</code> is using the <code>'username'</code> string to define a validation rule.</p>
<p>This will become an issue if the <code>username</code> property will be changed - if you don't have tests, your validation will be broken now.</p>
<p>But of course, there's a package for that: <a href="https://www.npmjs.com/package/ts-simple-nameof"><code>ts-simple-nameof</code></a>:</p>
<blockquote>
<p>Parses a class name or a dot-separated property name from a lambda expression and provides some level of type safety using type parameters.</p>
</blockquote>
<p>What does this mean exactly?</p>
<p>Here's a simple example:</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">Comment</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
user<span class="token operator">:</span> <span class="token punctuation">{</span>
posts<span class="token operator">:</span> Post<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// Returns "posts".</span>
<span class="token generic-function"><span class="token function">nameof</span><span class="token generic class-name"><span class="token operator"><</span>Comment<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">=></span> c<span class="token punctuation">.</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// returns "user"</span>
<span class="token comment">// Returns "user.posts".</span>
<span class="token generic-function"><span class="token function">nameof</span><span class="token generic class-name"><span class="token operator"><</span>Comment<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">=></span> c<span class="token punctuation">.</span>user<span class="token punctuation">.</span>posts<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>So, <code>ts-simple-nameof</code> returns the name of a property of a TypeScript <code>type</code>, <code>class</code> or <code>interface</code>.</p>
<p>Having <code>ts-simple-nameof</code> at hand, we now can solve our <code>express-validator</code> refactoring issue like this:</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">MyCommand</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
some<span class="token operator">:</span> <span class="token punctuation">{</span>
nested<span class="token operator">:</span> <span class="token punctuation">{</span>
property<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token function">body</span><span class="token punctuation">(</span><span class="token generic-function"><span class="token function">nameof</span><span class="token generic class-name"><span class="token operator"><</span>MyCommand<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">=></span> c<span class="token punctuation">.</span>some<span class="token punctuation">.</span>nested<span class="token punctuation">.</span>property<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">exists</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will ensure, that a arbitrerily deeply nested property can be passed to the <code>body</code> validation function in a refactoring safe way - neat!</p>
<p>A sample repo can be found <a href="https://github.com/AlexZeitler/spike-express-validatorjs-nested-nameof">here</a>. It also shows how you can test <code>express-validator</code> validations.</p>
Creating an AWS Cognito user pool with OAuth flows using AWS CDK2020-10-05T01:00:00Zhttps://alexanderzeitler.com/articles/create-aws-cognito-userpool-with-oauth-flows-using-cdk/<p>I tried to setup an <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html">AWS Cognito user pool</a> supporting <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html">OAuth 2.0 client credential flow</a> using <a href="https://aws.amazon.com/cdk/">AWS CDK</a>.</p>
<p>As of version 1.66.0. CDK allows you to create a Cognito User Pool very straight forward:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> idp-stack <span class="token operator">&&</span> <span class="token builtin class-name">cd</span> idp-stack
cdk init idp-stack <span class="token parameter variable">--language</span> typescript
<span class="token function">npm</span> <span class="token function">install</span> @aws-cdk/aws-cognito</code></pre>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> OAuthScope<span class="token punctuation">,</span> UserPool <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@aws-cdk/aws-cognito"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> pool <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UserPool</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token string">"dev-userpool"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
userPoolName<span class="token operator">:</span> <span class="token string">"dev-userpool"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Next you would assume, you can add a client with client credential flow enabled (as explained in the links above). So here it is:</p>
<pre class="language-typescript"><code class="language-typescript">pool<span class="token punctuation">.</span><span class="token function">addClient</span><span class="token punctuation">(</span><span class="token string">"console-client"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
generateSecret<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
oAuth<span class="token operator">:</span> <span class="token punctuation">{</span>
flows<span class="token operator">:</span> <span class="token punctuation">{</span>
clientCredentials<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
scopes<span class="token operator">:</span> <span class="token punctuation">[</span>OAuthScope<span class="token punctuation">.</span><span class="token function">custom</span><span class="token punctuation">(</span><span class="token string">"https://resource-server//get-todos"</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In order to be able to add a custom scope like <code>https://resource-server//get-todos</code>, first you need to create a resource server. But it is not connected to the user pool in terms of a function to call on the pool instance (which makes sense if you think about it for a while).</p>
<p>So here we go:</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">new</span> <span class="token class-name">CfnUserPoolResourceServer</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token string">"dev-userpool-resource-server"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
identifier<span class="token operator">:</span> <span class="token string">"https://resource-server/"</span><span class="token punctuation">,</span>
name<span class="token operator">:</span> <span class="token string">"dev-userpool-resource-server"</span><span class="token punctuation">,</span>
userPoolId<span class="token operator">:</span> pool<span class="token punctuation">.</span>userPoolId<span class="token punctuation">,</span>
scopes<span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
scopeDescription<span class="token operator">:</span> <span class="token string">"Get todo items"</span><span class="token punctuation">,</span>
scopeName<span class="token operator">:</span> <span class="token string">"get-todos"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Now we can use the <code>get-todos</code> scope in our client (take care of the correct convention to specify the scope here):</p>
<pre class="language-typescript"><code class="language-typescript">pool<span class="token punctuation">.</span><span class="token function">addClient</span><span class="token punctuation">(</span><span class="token string">"console-client"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
generateSecret<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
oAuth<span class="token operator">:</span> <span class="token punctuation">{</span>
flows<span class="token operator">:</span> <span class="token punctuation">{</span>
clientCredentials<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
scopes<span class="token operator">:</span> <span class="token punctuation">[</span>OAuthScope<span class="token punctuation">.</span><span class="token function">custom</span><span class="token punctuation">(</span><span class="token string">"https://resource-server//get-todos"</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Make sure to take care of the convention for scopes: <code><resourceserver-identifier>//<scope-name></code> (notice the double slash).</p>
<p>Additionally we'll specify a domain for our user pool:</p>
<pre class="language-typescript"><code class="language-typescript">pool<span class="token punctuation">.</span><span class="token function">addDomain</span><span class="token punctuation">(</span><span class="token string">"CognitoDomain"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
cognitoDomain<span class="token operator">:</span> <span class="token punctuation">{</span>
domainPrefix<span class="token operator">:</span> <span class="token string">"dev-userpool"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Lets try <code>cdk deploy</code> and everything should be fine:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> run build <span class="token operator">&&</span> <span class="token function">npm</span> run cdk deploy</code></pre>
<p>This is the result - and we're done ✅</p>
<pre class="language-bash"><code class="language-bash">
The stack IdpStack already includes a CDKMetadata resource
IdpStack: deploying<span class="token punctuation">..</span>.
IdpStack: creating CloudFormation changeset<span class="token punctuation">..</span>.
<span class="token punctuation">[</span>██████████████████████████████████████████████████████████<span class="token punctuation">]</span> <span class="token punctuation">(</span><span class="token number">6</span>/6<span class="token punctuation">)</span>
✅ IdpStack</code></pre>
<p>The user pool:<br />
<img src="https://alexanderzeitler.com/articles/create-aws-cognito-userpool-with-oauth-flows-using-cdk/user-pool-overview.jpg" alt="" /></p>
<p>The client:
<img src="https://alexanderzeitler.com/articles/create-aws-cognito-userpool-with-oauth-flows-using-cdk/user-pool-client.jpg" alt="" /></p>
<p>The resource server:
<img src="https://alexanderzeitler.com/articles/create-aws-cognito-userpool-with-oauth-flows-using-cdk/user-pool-resource-server.jpg" alt="" /></p>
<p>The full code example can be found <a href="https://github.com/AlexZeitler/cdk-cognito-oauth-flow-example">here</a>.</p>
Minimal IAM permissions for AWS CDK deployments2020-10-01T02:00:00Zhttps://alexanderzeitler.com/articles/minimal-iam-permission-for-aws-cdk-deployment/<p>AWS CDK is leveraging AWS CloudFormation to deploy Stacks in AWS.</p>
<p>In addition, AWS CDK may require some data which is being stored in a S3 Bucket named <code>cdktoolkit-stagingbucket-*</code>.</p>
<p>This is the IAM policy IAM assigning to a AWS IAM group which should be able to deploy resources via AWS CDK. Of course, depending on the resources you want to deploy, you need further IAM permissions.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Version"</span><span class="token operator">:</span> <span class="token string">"2012-10-17"</span><span class="token punctuation">,</span>
<span class="token property">"Statement"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"Action"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"cloudformation:*"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"Resource"</span><span class="token operator">:</span> <span class="token string">"*"</span><span class="token punctuation">,</span>
<span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"Action"</span><span class="token operator">:</span> <span class="token string">"s3:*"</span><span class="token punctuation">,</span>
<span class="token property">"Resource"</span><span class="token operator">:</span> <span class="token string">"arn:aws:s3:::cdktoolkit-stagingbucket-*"</span><span class="token punctuation">,</span>
<span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>The policy gives full access to CloudFormation and all S3 Buckets named <code>cdktoolkit-stagingbucket-*</code>.</p>
<p>Another option is to additionally grant full access for all resources and their actions if the action has been triggered by CloudFormation (or CDK):</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Version"</span><span class="token operator">:</span> <span class="token string">"2012-10-17"</span><span class="token punctuation">,</span>
<span class="token property">"Statement"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"Action"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"cloudformation:*"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"Resource"</span><span class="token operator">:</span> <span class="token string">"*"</span><span class="token punctuation">,</span>
<span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"Condition"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"ForAnyValue:StringEquals"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"aws:CalledVia"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"cloudformation.amazonaws.com"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"Action"</span><span class="token operator">:</span> <span class="token string">"*"</span><span class="token punctuation">,</span>
<span class="token property">"Resource"</span><span class="token operator">:</span> <span class="token string">"*"</span><span class="token punctuation">,</span>
<span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"Action"</span><span class="token operator">:</span> <span class="token string">"s3:*"</span><span class="token punctuation">,</span>
<span class="token property">"Resource"</span><span class="token operator">:</span> <span class="token string">"arn:aws:s3:::cdktoolkit-stagingbucket-*"</span><span class="token punctuation">,</span>
<span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>Handle with care...</p>
<h3>Update for CDK version 2</h3>
<p>Another permission is required for CDK 2:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Version"</span><span class="token operator">:</span> <span class="token string">"2012-10-17"</span><span class="token punctuation">,</span>
<span class="token property">"Statement"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span><span class="token punctuation">,</span>
<span class="token property">"Action"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"sts:AssumeRole"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"Resource"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"arn:aws:iam::*:role/cdk-*"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>Thanks to <a href="https://linkedin.com/in/jamescrowley">James Crowley</a> for pointing this out.</p>
Accessing local domains in local dev environment from Android emulator2020-09-18T21:00:00Zhttps://alexanderzeitler.com/articles/accessing-local-domains-on-local-dev-environment-from-android-emulator-using-serverless-pihole-in-docker/<p>Let's consider you're using local domains in development and want access them from Android virtual devices using the Android emulator? Here's how to solve this on macOS using "serverless" pihole.</p>
<p>Given our local IP is <code>192.168.0.87</code> and we have defined a domain <code>ui</code> in <code>/etc/hosts</code>.</p>
<pre class="language-text"><code class="language-text">##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
192.168.0.87 ui
127.0.0.1 localhost
::1 localhost</code></pre>
<p>If you want to access <code>http://ui</code> from an virtual Android device running in the Android emulator, you get this error in Chrome:</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-local-domains-on-local-dev-environment-from-android-emulator-using-serverless-pihole-in-docker/chrome_ip_not_found.png" alt="" /></p>
<p>To make it work, we need a DNS server which knows how to resolve <code>http://ui</code> to <code>192.168.0.87</code>.</p>
<p>An easy way is to run the popular <a href="https://pi-hole.net/">pihole</a> in a Docker container (hence "serverless" in my post title 😉).</p>
<p>Just grab the sample <code>docker-compose.yml</code> from the <a href="https://hub.docker.com/r/pihole/pihole/">Docker registry</a>:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">"3"</span>
<span class="token comment"># More info at https://github.com/pi-hole/docker-pi-hole/ and https://docs.pi-hole.net/</span>
<span class="token key atrule">services</span><span class="token punctuation">:</span>
<span class="token key atrule">pihole</span><span class="token punctuation">:</span>
<span class="token key atrule">container_name</span><span class="token punctuation">:</span> pihole
<span class="token key atrule">image</span><span class="token punctuation">:</span> pihole/pihole<span class="token punctuation">:</span>latest
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">"53:53/tcp"</span>
<span class="token punctuation">-</span> <span class="token string">"53:53/udp"</span>
<span class="token punctuation">-</span> <span class="token string">"67:67/udp"</span>
<span class="token punctuation">-</span> <span class="token string">"80:80/tcp"</span>
<span class="token punctuation">-</span> <span class="token string">"443:443/tcp"</span>
<span class="token key atrule">environment</span><span class="token punctuation">:</span>
<span class="token key atrule">TZ</span><span class="token punctuation">:</span> <span class="token string">'America/Chicago'</span>
<span class="token comment"># WEBPASSWORD: 'set a secure password here or it will be random'</span>
<span class="token comment"># Volumes store your data between container upgrades</span>
<span class="token key atrule">volumes</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">'./etc-pihole/:/etc/pihole/'</span>
<span class="token punctuation">-</span> <span class="token string">'./etc-dnsmasq.d/:/etc/dnsmasq.d/'</span>
<span class="token comment"># Recommended but not required (DHCP needs NET_ADMIN)</span>
<span class="token comment"># https://github.com/pi-hole/docker-pi-hole#note-on-capabilities</span>
<span class="token key atrule">cap_add</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> NET_ADMIN
<span class="token key atrule">restart</span><span class="token punctuation">:</span> unless<span class="token punctuation">-</span>stopped</code></pre>
<p>Given <code>http://ui</code> runs on port <code>80</code>, we need to change the port mapping for port <code>80</code> in the <code>docker-compose.yml</code> to another port, e.g. <code>8080</code>:</p>
<pre class="language-yml"><code class="language-yml"><span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">"53:53/tcp"</span>
<span class="token punctuation">-</span> <span class="token string">"53:53/udp"</span>
<span class="token punctuation">-</span> <span class="token string">"67:67/udp"</span>
<span class="token punctuation">-</span> <span class="token string">"8080:80/tcp"</span>
<span class="token punctuation">-</span> <span class="token string">"443:443/tcp"</span></code></pre>
<p>Next, we run <code>docker-compose up -d</code>.</p>
<p>Then we can browse to <code>http://localhost:8080/admin</code>.</p>
<p>The Browser should show something like this:</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-local-domains-on-local-dev-environment-from-android-emulator-using-serverless-pihole-in-docker/pihole_anon.png" alt="" /></p>
<p>After this, we can click "Login" from the sidebar and login using the password (how to obtain the password is described on the Docker registry page linked above).</p>
<p>Next we can select "Local DNS records" from the sidebar:</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-local-domains-on-local-dev-environment-from-android-emulator-using-serverless-pihole-in-docker/pihole_menu.png" alt="" /></p>
<p>Then we add a new DNS entry for <code>ui</code> with IP address <code>192.168.0.87</code>.</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-local-domains-on-local-dev-environment-from-android-emulator-using-serverless-pihole-in-docker/pihole_add_dns_entry.png" alt="" /></p>
<p>Now it's time to start our emulator again, this time from the command line, as this is the easiest way to pass the DNS server setting:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> ~/Library/Android/sdk/emulator
emulator -list-avds</code></pre>
<p>Your output will look like this:</p>
<pre class="language-bash"><code class="language-bash">Pixel_XL_API_29</code></pre>
<p>Now we can start the emulator again and pass the pihole DNS server running on <code>192.168.0.87</code>:</p>
<pre class="language-bash"><code class="language-bash">./emulator <span class="token parameter variable">-avd</span> Pixel_XL_API_29 -dns-server <span class="token number">192.168</span>.0.87</code></pre>
<p>Pointing Chrome again to <code>http://ui</code> inside the virtual device should result in something like this now:</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-local-domains-on-local-dev-environment-from-android-emulator-using-serverless-pihole-in-docker/chrome_hello_world.png" alt="" /></p>
<p>To me, local DNS has never been that easy. How about you?</p>
Hot reload tailwindcss changes with browser-sync2020-09-08T21:00:00Zhttps://alexanderzeitler.com/articles/watch-tailwind-changes-update-browser-sync/<p>The other day I wanted <code>browser-sync</code> to include my changes made to <code>tailwind.config.js</code> or <code>tailwind.css</code> without manually restarting <code>browser-sync</code> after my <code>output.css</code> has been generated.</p>
<h3>Initial setup</h3>
<p>My initial setup has been <code>tailwindcss</code> and <code>browser-sync</code> installed and configured like this:</p>
<pre class="language-text"><code class="language-text">- tailwind.css
- tailwind.config.js</code></pre>
<p>The contents of the <code>tailwind.css</code> are based on the default setup:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@tailwind</span> base<span class="token punctuation">;</span></span>
<span class="token atrule"><span class="token rule">@tailwind</span> components<span class="token punctuation">;</span></span>
<span class="token atrule"><span class="token rule">@tailwind</span> utilities<span class="token punctuation">;</span></span></code></pre>
<p>This is the default <code>tailwind.config.js</code> file:</p>
<pre class="language-javascript"><code class="language-javascript">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">purge</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">theme</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">extend</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">variants</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>The <code>sync</code> script in <code>package.json</code> did look like this:</p>
<pre class="language-bash"><code class="language-bash">browser-sync start <span class="token parameter variable">--server</span> <span class="token parameter variable">--files</span> <span class="token punctuation">\</span>"**/*<span class="token punctuation">\</span></code></pre>
<p>Having this setup, changes to files have been monitored by <code>browser-sync</code> and updated in the browser.</p>
<p>However, changes made to <code>tailwind.css</code> or <code>tailwind.config.js</code> have not been monitored and I had to restart <code>sync</code> after running <code>npx tailwindcss build tailwind.css -o output.css</code>.</p>
<h3>Automate all the things</h3>
<p>To get rid of this manual step, I installed these <code>devDevependencies</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">yarn</span> <span class="token function">add</span> <span class="token parameter variable">-D</span> nodemon npm-run-all postcss-cli</code></pre>
<p>Next, I add the <code>dev</code>, <code>watch-dev</code> and <code>watch</code> scripts, so my <code>scripts</code> section in <code>package.json</code> ends up like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"sync"</span><span class="token operator">:</span> <span class="token string">"browser-sync start --server --files \"**/*\""</span><span class="token punctuation">,</span>
<span class="token property">"dev"</span><span class="token operator">:</span> <span class="token string">"postcss tailwind.css --output output.css"</span><span class="token punctuation">,</span>
<span class="token property">"watch:dev"</span><span class="token operator">:</span> <span class="token string">"nodemon -x npm run dev -w tailwind.config.js -w tailwind.css"</span><span class="token punctuation">,</span>
<span class="token property">"watch"</span><span class="token operator">:</span> <span class="token string">"run-p watch:dev sync"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Now I can just run <code>yarn watch</code> and <code>browser-sync</code> includes the changes made to <code>tailwind.css</code> and <code>tailwind.config.js</code> instantly.</p>
<p><code>run-p</code> is the CLI tool installed by <code>npm-run-all</code> and first starts the <code>watch:dev</code> followed by the <code>sync</code>script.
<code>watch-dev</code> runs <code>nodemon</code> which watches the changes made to <code>tailwind.config.js</code> and <code>tailwind.css</code>. If changes are made, <code>postcss</code> is run by the <code>dev</code> script to compile tailwindcss output again.</p>
<p>To make this work, I've added a <code>postcss.config.js</code>:</p>
<pre class="language-javascript"><code class="language-javascript">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"tailwindcss"</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token string">"tailwind.config.js"</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>This step does the work done by <code>tailwindcss build tailwind.css -o output.css</code> before.</p>
<p>That's it 🤷♂️</p>
Parsing JSON parameters stored in AWS Parameter Store using jq2020-01-07T17:00:00Zhttps://alexanderzeitler.com/articles/parsing-json-parameters-stored-in-aws-parameter-store-using-jq/<p>The other day I had the idea to store some JSON as a <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html">AWS Parameter Store</a> value.</p>
<p>If you read the parameter using AWS CLI:</p>
<pre class="language-bash"><code class="language-bash">aws ssm get-parameters <span class="token parameter variable">--name</span> /prod/some-json-param</code></pre>
<p>you get something like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Parameters"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"Name"</span><span class="token operator">:</span> <span class="token string">"/prod/some-json-param"</span><span class="token punctuation">,</span>
<span class="token property">"Type"</span><span class="token operator">:</span> <span class="token string">"String"</span><span class="token punctuation">,</span>
<span class="token property">"Value"</span><span class="token operator">:</span> <span class="token string">"{\n \"server\": \"\",\n \"token\": \"\"\n}"</span><span class="token punctuation">,</span>
<span class="token property">"Version"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token property">"LastModifiedDate"</span><span class="token operator">:</span> <span class="token number">1578353153.788</span><span class="token punctuation">,</span>
<span class="token property">"ARN"</span><span class="token operator">:</span> <span class="token string">"arn:aws:ssm:eu-central-1:1234567890:parameter/prod/some-json-param"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"InvalidParameters"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>What I wanted to get in a shell script, was the JSON representation of <code>Value</code> like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"server"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"token"</span><span class="token operator">:</span> <span class="token string">""</span>
<span class="token punctuation">}</span></code></pre>
<p>Thanks to the popular <a href="https://stedolan.github.io/jq/">jq</a> command, this is quite easy:</p>
<pre class="language-bash"><code class="language-bash">aws ssm get-parameters <span class="token parameter variable">--name</span> /prod/some-json-param <span class="token operator">|</span> jq <span class="token string">'.Parameters | .[] | .Value'</span></code></pre>
<p>That way, we get the value of <code>Value</code> as a string:</p>
<pre class="language-text"><code class="language-text">"{\n \"server\": \"\",\n \"token\": \"\"\n}"</code></pre>
<p>Another call to <code>jq</code> will give us the desired result:</p>
<pre class="language-bash"><code class="language-bash">aws ssm get-parameters <span class="token parameter variable">--name</span> /prod/some-json-param <span class="token operator">|</span> jq <span class="token string">'.Parameters | .[] | .Value'</span> <span class="token operator">|</span> jq <span class="token string">'.|fromjson'</span></code></pre>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"server"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"token"</span><span class="token operator">:</span> <span class="token string">""</span>
<span class="token punctuation">}</span></code></pre>
<p>Happy Coding!</p>
Refresh ASP.NET Core MVC view without reloading the application2019-12-11T17:00:00Zhttps://alexanderzeitler.com/articles/refresh-asp-net-core-website-without-reloading-application/<p>After doing no ASP.NET (Core) stuff for while, today I created an ASP.NET Core MVC 3.0 application using <code>dotnet new mvc</code>.</p>
<p>I started the application using <code>dotnet watch run</code> and I was surprised to see the whole application restarting even when I just changed a single character within a MVC View e.g. <code>./Views/Home/Index.cshtml</code>.</p>
<p>The solution was to add this NuGet package to the project:</p>
<p><code>Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation</code></p>
<p>In the <code>Startup.cs</code> I had to change this line:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ConfigureServices</span><span class="token punctuation">(</span><span class="token class-name">IServiceCollection</span> services<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token function">AddControllersWithViews</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>to this</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ConfigureServices</span><span class="token punctuation">(</span><span class="token class-name">IServiceCollection</span> services<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token function">AddControllersWithViews</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">AddRazorRuntimeCompilation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now the application doesn't have to restart for simple HTML/Razor content changes.</p>
<p>Happy coding 😉</p>
Push vs. Pull - regaining control over time2019-01-08T23:00:00Zhttps://alexanderzeitler.com/articles/push-vs-pull-regaining-control-over-time/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Push vs. Pull - regaining control over time" />
<meta name="twitter:description" content="Make Twitter and Gadgets useful again" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/push-vs-pull-regaining-control-over-time/patryk-gradys-128898-unsplash.jpg" />
<p><img src="https://alexanderzeitler.com/articles/push-vs-pull-regaining-control-over-time/patryk-gradys-128898-unsplash.jpg" alt="" />
Photo by <a href="https://unsplash.com/photos/4pPzKfd6BEg?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Patryk Grądys</a> on <a href="https://unsplash.com/search/photos/control?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p>
<p>A few weeks ago I had my tenth Twitter anniversary. But I didn't feel happy about it - quite the opposite.</p>
<p>At that time I was oscillating between leaving Twitter completely one day and following even more people because of their great Tweets the other day.</p>
<p>I started thinking about what I was expecting from Twitter and what was holding me back to get this.
To me, the problem was: many people post several Tweets you're deeply interested in and you start following them. But then they're also posting a lot of stuff you're not interested in.
Some folks argue that you "buy" the whole person and not just the part you're interested in - but at some amount of people you're following, this won't scale.</p>
<p>So I started an experiment: besides using Lists, I created search definitions using Hashtags and keywords in the <a href="https://twitter.com/search-advanced?lang=en">Twitter Advanced Search</a> and bookmarked these searches. I also muted a lot of keywords I don't want to read about (this is just distracting noise, let's be honest).</p>
<p>After that a - at that this point in time - crazy idea popped up in my mind: What if I would unfollow everybody? Shouldn't the tweets from the search lead me back to most of the people I followed anyway? Indeed, that's what happened. But now I also read Tweets from many people I had not seen before on Twitter. It's sort of that feeling when you find some awesome stuff on GitHub from people you never heard of.</p>
<p>But I was still unsure.</p>
<p>In the end I did it, because I read this Tweet by the end of 2018:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">In 2018, I made a new year's resolution to try and not follow anyone on Twitter for the year. It was the best thing I've done. Make 2019 your year of 0 following, I promise you will not regret it. <a href="https://t.co/NTPlQNEbrI">https://t.co/NTPlQNEbrI</a></p>— Carmen Hernández Andoh (@carmatrocity) <a href="https://twitter.com/carmatrocity/status/1073356921674051584?ref_src=twsrc%5Etfw">December 13, 2018</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>No I was in a rush. I also decided to invest the time saved into reading more Blogs again. So I cleaned up my blog roll and added blogs from people I found using the Twitter Search.</p>
<p>After a while I discovered this pattern:
Find people by Search via Hashtags and keywords, subscribe to their blogs and dive deep into topics again - not just bite size fragments and endless discussions on Twitter.</p>
<p>Now that I'm deciding what to search for and when, Twitter has become much less time consuming as there's a plan what to do - and not browsing around just for fun.</p>
<p>Having identified inefficient Twitter usage as a time hog I was thinking about where this was happening outside Twitter and I started questioning using my iPhone for anything else than taking Phone calls - weird.</p>
<p>To cut a long story short - this is my iPhone home screen as of today (as you can see it works for more than ten days already):</p>
<p><img src="https://alexanderzeitler.com/articles/push-vs-pull-regaining-control-over-time/iphone-screen-2019-1.png" alt="" /></p>
<p>My second iPhone screen just contains rarely used stuff or Apps I can't delete anyway.
<img src="https://alexanderzeitler.com/articles/push-vs-pull-regaining-control-over-time/iphone-screen-2019-2.png" alt="" /></p>
<p>Besides phone calls, text messages and meeting reminders, there's nothing installed which causes distraction - not even email. If it is really important, people will call anyway - anything else can wait until I'm using a Notebook which is way more efficient for doing work than a small phone screen.</p>
<p>And of course, less push, more pull. You're back in control - not others.</p>
<p>P.S.: Norbert started unfollowing everyone on Twitter today as well, so read <a href="https://www.norberteder.com/so-ist-auch-twitter-wieder-nutzbar/">his post</a> as well (in german language)</p>
Enabling the Kubernetes Dashboard for DigitalOcean Kubernetes2019-01-03T05:00:00Zhttps://alexanderzeitler.com/articles/enabling-the-kubernetes-dashboard-for-digitalocean-kubernetes/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Enabling the Kubernetes Dashboard for DigitalOcean Kubernetes" />
<meta name="twitter:description" content="Enabling the Kubernetes Dashboard for DigitalOcean Kubernetes" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/enabling-the-kubernetes-dashboard-for-digitalocean-kubernetes/k8s-dashboard.png" />
<p>DigitalOcean recently announced the public availability of it's <a href="https://www.digitalocean.com/products/kubernetes/?refcode=b6ff141992c6">managed Kubernetes offering</a> and it's pretty awesome.</p>
<p><img src="https://alexanderzeitler.com/articles/enabling-the-kubernetes-dashboard-for-digitalocean-kubernetes/k8s-control.gif" alt="" /></p>
<p>Once you created your cluster following their <a href="https://www.digitalocean.com/docs/kubernetes/quickstart/">quickstart</a>, you might want to open the Kubernetes dashboard using <code>kubectl proxy</code>. But browsing <code>http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/</code> will result in a <code>404</code> error.</p>
<p>That's because the Kubernetes dashboard is not deployed by default, so let's do this now using:</p>
<pre class="language-bash"><code class="language-bash">kubectl create <span class="token parameter variable">--kubeconfig</span><span class="token operator">=</span><span class="token string">"your-digitalocean-kubeconfig.yaml"</span> <span class="token parameter variable">-f</span> https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml</code></pre>
<p>By the way, if you don't want to always specify the <code>--kubeconfig</code> parameter, you can merge your Cluster configuration into your local Kubernetes Config using this <a href="https://github.com/digitalocean/doctl">DigitalOcean CLI</a> command (make sure to login first using your DO token) - and I'm assuming this for the next <code>kubectl</code> commands issued in this post:</p>
<pre class="language-bash"><code class="language-bash">doctl k8s cluster kubeconfig save <span class="token operator"><</span>your-do-cluster-name<span class="token operator">></span></code></pre>
<p>Once the configuration is merged, you can list the Kubernetes contexts by issuing this <code>kubectl</code> command</p>
<pre class="language-bash"><code class="language-bash">kubectl config get-contexts</code></pre>
<p>Which will output a result similar to this:</p>
<pre class="language-bash"><code class="language-bash">CURRENT NAME CLUSTER AUTHINFO NAMESPACE
do-fra1-k8s-1-12-1-do-2-fra1-xxxxxxxxxx do-fra1-k8s-1-12-1-do-2-fra1-xxxxxxxxxx do-fra1-k8s-1-12-1-do-2-fra1-xxxxxxxxxx-admin
* docker-for-desktop docker-for-desktop-cluster docker-for-desktop</code></pre>
<p>As you can see, the current active context is your local Kubernetes and if you want to issue commands against your DigitalOcean Cluster, you'll have to switch the context to this cluster by running</p>
<pre class="language-bash"><code class="language-bash">kubectl config use-context do-fra1-k8s-1-12-1-do-2-fra1-xxxxxxxxxx</code></pre>
<p>If everything went fine, you should be able to list the Nodes of your DigitalOcean cluster:</p>
<pre class="language-bash"><code class="language-bash">kubectl get nodes</code></pre>
<p>Fingers crossed 🤞, the output should be similar to this (except for the cluster name, of course):</p>
<pre class="language-bash"><code class="language-bash">NAME STATUS ROLES AGE VERSION
eloquent-hypatia-h12 Ready <span class="token operator"><</span>none<span class="token operator">></span> 63d v1.12.1</code></pre>
<p>If you try to access your Kubernetes dashboard now by running <code>kubectl proxy</code> and logging in using your Cluster configuration <code>yaml</code> file, you'll get this error:</p>
<blockquote>
<p>Not enough data to create auth info structure.</p>
</blockquote>
<p>Read on, we're solving this now 💪!</p>
<p>Next, we need to create a Service Account and a <code>ClusterRoleBinding</code> using this <code>serviceaccount.yaml</code> file:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1
<span class="token key atrule">kind</span><span class="token punctuation">:</span> ServiceAccount
<span class="token key atrule">metadata</span><span class="token punctuation">:</span>
<span class="token key atrule">name</span><span class="token punctuation">:</span> <ServiceAccountName<span class="token punctuation">></span> <span class="token comment"># replace this with the username you want to use</span>
<span class="token key atrule">namespace</span><span class="token punctuation">:</span> kube<span class="token punctuation">-</span>system
<span class="token punctuation">---</span>
<span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> rbac.authorization.k8s.io/v1beta1
<span class="token key atrule">kind</span><span class="token punctuation">:</span> ClusterRoleBinding
<span class="token key atrule">metadata</span><span class="token punctuation">:</span>
<span class="token key atrule">name</span><span class="token punctuation">:</span> <ServiceAccountName<span class="token punctuation">></span> <span class="token comment"># replace this with the username you want to use</span>
<span class="token key atrule">roleRef</span><span class="token punctuation">:</span>
<span class="token key atrule">apiGroup</span><span class="token punctuation">:</span> rbac.authorization.k8s.io
<span class="token key atrule">kind</span><span class="token punctuation">:</span> ClusterRole
<span class="token key atrule">name</span><span class="token punctuation">:</span> cluster<span class="token punctuation">-</span>admin
<span class="token key atrule">subjects</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">kind</span><span class="token punctuation">:</span> ServiceAccount
<span class="token key atrule">name</span><span class="token punctuation">:</span> <ServiceAccountName<span class="token punctuation">></span> <span class="token comment"># replace this with the username you want to use</span>
<span class="token key atrule">namespace</span><span class="token punctuation">:</span> kube<span class="token punctuation">-</span>system</code></pre>
<p>Then apply the <code>serviceaccount.yaml</code> using:</p>
<pre class="language-bash"><code class="language-bash">kubectl apply <span class="token parameter variable">-f</span> serviceaccount.yaml</code></pre>
<p>After that, we need to get the access token for the Service Account:</p>
<pre class="language-bash"><code class="language-bash">kubectl get secret <span class="token parameter variable">-n</span> kube-system</code></pre>
<p>This will give you a list like this:</p>
<pre class="language-bash"><code class="language-bash">NAME TYPE DATA AGE
csi-do-controller-sa-token-hsgv9 kubernetes.io/service-account-token <span class="token number">3</span> 62d
csi-do-node-sa-token-vz7wk kubernetes.io/service-account-token <span class="token number">3</span> 62d
default-token-tw59l kubernetes.io/service-account-token <span class="token number">3</span> 62d
digitalocean Opaque <span class="token number">1</span> 62d
kube-dns-token-7zvjw kubernetes.io/service-account-token <span class="token number">3</span> 62d
kubernetes-dashboard-certs Opaque <span class="token number">0</span> 4m
kubernetes-dashboard-key-holder Opaque <span class="token number">2</span> 4m
kubernetes-dashboard-token-fxw8d kubernetes.io/service-account-token <span class="token number">3</span> 4m
<span class="token operator"><</span>ServiceAccountName-token-xxxxx<span class="token operator">></span> kubernetes.io/service-account-token <span class="token number">3</span> 17s</code></pre>
<p>Finally display your token:</p>
<pre class="language-bash"><code class="language-bash">kubectl describe secret <span class="token operator"><</span>ServiceAccountName-token-xxxxx<span class="token operator">></span> <span class="token parameter variable">-n</span> kube-system</code></pre>
<pre class="language-bash"><code class="language-bash">Name: <span class="token operator"><</span>ServiceAccountName-token-xxxxx<span class="token operator">></span>
Namespace: kube-system
Labels: <span class="token operator"><</span>none<span class="token operator">></span>
Annotations: kubernetes.io/service-account.name<span class="token operator">=</span><span class="token operator"><</span>ServiceAccountName-token-xxxxx<span class="token operator">></span>
kubernetes.io/service-account.uid<span class="token operator">=</span><span class="token operator"><</span>some-uid<span class="token operator">></span>
Type: kubernetes.io/service-account-token
Data
<span class="token operator">==</span><span class="token operator">==</span>
ca.crt: <span class="token number">1156</span> bytes
namespace: <span class="token number">11</span> bytes
token: <span class="token operator"><</span>here goes your token<span class="token operator">></span></code></pre>
<p>When running <code>kubectl proxy</code> again, now you can enter your token in the login screen here:</p>
<p><img src="https://alexanderzeitler.com/articles/enabling-the-kubernetes-dashboard-for-digitalocean-kubernetes/k8s-dashboard-login.png" alt="" /></p>
<p>If everything went well, you should be able to browse the Kubernetes dashboard now:</p>
<p><img src="https://alexanderzeitler.com/articles/enabling-the-kubernetes-dashboard-for-digitalocean-kubernetes/k8s-dashboard.png" alt="" /></p>
Passing a ViewModel by Attribute to ASP.NET Core ViewComponents2018-12-28T01:00:00Zhttps://alexanderzeitler.com/articles/passing-viewmodel-by-attribute-to-aspnet-core-viewcomponents/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lxztlr" />
<meta name="twitter:creator" content="@lxztlr" />
<meta name="twitter:title" content="Passing a ViewModel by Attribute to ASP.NET Core ViewComponents" />
<meta name="twitter:description" content="Passing a ViewModel by Attribute to ASP.NET Core ViewComponents" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/passing-viewmodel-by-attribute-to-aspnet-core-viewcomponents/structure-twitter-card.png" />
<p>ASP.NET Core provides the concept of View components and the intro from the <a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.2">official docs</a> reads quite straight forward:</p>
<blockquote>
<p>View components are similar to partial views, but they're much more powerful. View components don't use model binding, and only depend on the data provided when calling into it. This article was written using ASP.NET Core MVC, but view components also work with Razor Pages.</p>
</blockquote>
<p>Yet, it was a bit of confusing when trying to use View components with Tag Helpers and passing fragments of the parent view model to the View Component by attributes.</p>
<p>First, lets take a look at what we're building:</p>
<p><img src="https://alexanderzeitler.com/articles/passing-viewmodel-by-attribute-to-aspnet-core-viewcomponents/structure.png" alt="" /></p>
<p>As you can see, there are two "sections" named "Books" and "Services" and both have child elements.</p>
<p>In order keep some structure and separate concerns, we don't want to render everything in the main view.
The structure, we want to establish, is this:</p>
<pre class="language-text"><code class="language-text">Index-View of HomeController
- PageSection ("Books")
- SectionItems (List<SectionItem>)
- SectionItem (Title "Kubernetes")</SectionItem></code></pre>
<p>Our complete ViewModel class structure is this - being loaded from a database or a headless CMS:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">IndexViewModel</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name">List<span class="token punctuation"><</span>PageSection<span class="token punctuation">></span></span> Sections <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PageSection</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> Title <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name">List<span class="token punctuation"><</span>SectionItem<span class="token punctuation">></span></span> SectionItems <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SectionItem</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> Text <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>For the sake of simplicity, I'll just create it inline in <code>HomeController.cs</code>:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">HomeController</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">Controller</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IActionResult</span> <span class="token function">Index</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> indexViewModel <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">IndexViewModel</span>
<span class="token punctuation">{</span>
Sections <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">List<span class="token punctuation"><</span>PageSection<span class="token punctuation">></span></span>
<span class="token punctuation">{</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">PageSection</span>
<span class="token punctuation">{</span>
Title <span class="token operator">=</span> <span class="token string">"Books"</span><span class="token punctuation">,</span>
SectionItems <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">List<span class="token punctuation"><</span>SectionItem<span class="token punctuation">></span></span>
<span class="token punctuation">{</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">SectionItem</span> <span class="token punctuation">{</span> Text <span class="token operator">=</span> <span class="token string">"ASP.NET Core in Action"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">SectionItem</span> <span class="token punctuation">{</span> Text <span class="token operator">=</span> <span class="token string">"Node.js in Action"</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">PageSection</span>
<span class="token punctuation">{</span>
Title <span class="token operator">=</span> <span class="token string">"Services"</span><span class="token punctuation">,</span>
SectionItems <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">List<span class="token punctuation"><</span>SectionItem<span class="token punctuation">></span></span>
<span class="token punctuation">{</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">SectionItem</span> <span class="token punctuation">{</span> Text <span class="token operator">=</span> <span class="token string">"ASP.NET Core Development"</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">SectionItem</span> <span class="token punctuation">{</span> Text <span class="token operator">=</span> <span class="token string">"Node.js Development"</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">SectionItem</span> <span class="token punctuation">{</span> Text <span class="token operator">=</span> <span class="token string">"Kubernetes"</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token function">View</span><span class="token punctuation">(</span>indexViewModel<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>In the end, our <code>Index.cshtml</code> for the <code>HomeController</code> will be this:</p>
<pre class="language-aspnet"><code class="language-aspnet">@model IndexViewModel
@addTagHelper *, ViewComponentsSample
@foreach (var pageSection in Model.Sections)
{
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">vc:</span>page-section</span> <span class="token attr-name">section</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@pageSection<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">vc:</span>page-section</span><span class="token punctuation">></span></span>
}</code></pre>
<p>The Tag Helper to invoke a view component uses the <code><vc></vc></code> element and it added by the <code>addTagHelper</code> directive.</p>
<p>Also, Pascal-cased class and method parameters for Tag Helpers are translated into their lower kebab case - this will be important very soon.</p>
<p>The first View component we'll implement, obviously is the <code>PageSection</code> View component (remember Tag Helpers and the kebab casing convention? 😉)</p>
<p>The path of the Components HTML is <code>~/Views/Shared/Components/PageSection/Default.cshtml</code> and this is the markup:</p>
<pre class="language-aspnet"><code class="language-aspnet">@model PageSection
@addTagHelper *, ViewComponentsSample
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>@Model.Title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">vc:</span>section-items</span> <span class="token attr-name">section-items</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@Model.SectionItems<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">vc:</span>section-items</span><span class="token punctuation">></span></span></code></pre>
<p>This component also uses the <code>@addTagHelper</code> directive and it defines a ViewModel of type <code>PageSection</code> which has been a fragment of the <code>IndexViewModel</code> from above.</p>
<p>Besides rendering the Section title it also calls another View component and passes it's own <code>SectionItems</code> (kebab-casing again) to the child View component.</p>
<p>The code behind for these two View components is inside the <code>~/ViewComponents</code> directory and their code looks like this:</p>
<p><code>PageSection.cs</code>:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PageSection</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">ViewComponent</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IViewComponentResult</span> <span class="token function">Invoke</span><span class="token punctuation">(</span><span class="token class-name">Models<span class="token punctuation">.</span>PageSection</span> section<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">View</span><span class="token punctuation">(</span><span class="token string">"~/Views/Shared/Components/PageSection/Default.cshtml"</span><span class="token punctuation">,</span> section<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p><code>SectionItems.cs</code>:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SectionItems</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">ViewComponent</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IViewComponentResult</span> <span class="token function">Invoke</span><span class="token punctuation">(</span><span class="token class-name">List<span class="token punctuation"><</span>Models<span class="token punctuation">.</span>SectionItem<span class="token punctuation">></span></span> sectionItems<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">View</span><span class="token punctuation">(</span><span class="token string">"~/Views/Shared/Components/SectionItems/Default.cshtml"</span><span class="token punctuation">,</span> sectionItems<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>There are some similarities both classes share:</p>
<ul>
<li>They derive from the <code>ViewComponent</code> base class</li>
<li>They implement the <code>Invoke</code> method which returns a <code>IViewComponentResult</code></li>
<li>They pass the model to the Views <code>cshtml</code> Template</li>
</ul>
<p>And now we have some conventions ("magic" 🤙) to understand here to get things working:</p>
<ul>
<li>The <code>Invoke</code> method is called by the<code>vc</code> TagHelper</li>
<li>The name (and of course the type) of the parameter(s) passed as Tag Helper attributes have to match the name and type of the <code>Invoke</code> method - and kebab casing is your friend again.</li>
</ul>
<p>You can nest View components at arbitrary levels, so I added another one to finally list the <code>SectionItem</code>s.</p>
<p>This is it's template:</p>
<pre class="language-aspnet"><code class="language-aspnet">@model List<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ViewComponentsSample.Models.SectionItem</span><span class="token punctuation">></span></span>
@addTagHelper *, ViewComponentsSample
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span>
@foreach (var item in Model)
{
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">vc:</span>section-item</span> <span class="token attr-name">text</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@item.Text<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">vc:</span>section-item</span><span class="token punctuation">></span></span>
}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></code></pre>
<p>And this is the code behind:</p>
<pre class="language-aspnet"><code class="language-aspnet">@model string
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>@Model<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></code></pre>
<p>Once you get it, it's pretty cool 😎</p>
<p>The sample can be found on <a href="https://github.com/PDMLab/aspnet-core-viewcomponents-sample">GitHub</a>.</p>
Running ASP.NET Core on minikube2018-11-13T11:00:00Zhttps://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/<p>There are a ton of managed Kubernetes services available today (and for sure even more tomorrow) to run your ASP.NET Core application.</p>
<p>If you want to run k8s locally you can <a href="https://github.com/kelseyhightower/kubernetes-the-hard-way">spin up a whole cluster manually</a>. Another solution is to use <code>minikube</code>.</p>
<p>So, what is <code>minikube</code> exactly?</p>
<blockquote>
<p>Minikube is a tool that makes it easy to run Kubernetes locally. Minikube runs a single-node Kubernetes cluster inside a VM on your laptop for users looking to try out Kubernetes or develop with it day-to-day.</p>
<p><a href="https://kubernetes.io/docs/setup/minikube/">https://kubernetes.io/docs/setup/minikube/</a></p>
</blockquote>
<h3>Installing minikube</h3>
<p>In order to be able to install <code>minikube</code> you need to have a Hypervisor like VirtualBox (Linux) or VMWare Fusion (macOS) installed. (This post won’t cover Windows btw.).</p>
<p>After installing your Hypervisor of choice, you have to install minikube</p>
<h3>On macOS</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-Lo</span> minikube https://storage.googleapis.com/minikube/releases/v0.30.0/minikube-darwin-amd64 <span class="token operator">&&</span> <span class="token function">chmod</span> +x minikube <span class="token operator">&&</span> <span class="token function">sudo</span> <span class="token function">cp</span> minikube /usr/local/bin/ <span class="token operator">&&</span> <span class="token function">rm</span> minikube</code></pre>
<h3>On Linux (Ubuntu 18.04 here)</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-Lo</span> minikube https://storage.googleapis.com/minikube/releases/v0.30.0/minikube-linux-amd64 <span class="token operator">&&</span> <span class="token function">chmod</span> +x minikube <span class="token operator">&&</span> <span class="token function">sudo</span> <span class="token function">cp</span> minikube /usr/local/bin/ <span class="token operator">&&</span> <span class="token function">rm</span> minikube</code></pre>
<h3>Installing kubectl</h3>
<p>In order to manage your k8s cluster or <code>minikube</code>, you need a command line tool named <code>kubectl</code>.</p>
<h3>On macOS</h3>
<pre class="language-bash"><code class="language-bash">brew <span class="token function">install</span> kubernetes-cli</code></pre>
<h3>On Linux (Ubuntu 18.04 here)</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt-get</span> update <span class="token operator">&&</span> <span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> apt-transport-https
<span class="token function">curl</span> <span class="token parameter variable">-s</span> https://packages.cloud.google.com/apt/doc/apt-key.gpg <span class="token operator">|</span> <span class="token function">sudo</span> apt-key <span class="token function">add</span> -
<span class="token builtin class-name">echo</span> <span class="token string">"deb http://apt.kubernetes.io/ kubernetes-xenial main"</span> <span class="token operator">|</span> <span class="token function">sudo</span> <span class="token function">tee</span> <span class="token parameter variable">-a</span> /etc/apt/sources.list.d/kubernetes.list
<span class="token function">sudo</span> <span class="token function">apt-get</span> update
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> kubectl</code></pre>
<h3>Running minikube</h3>
<p>Its as simple as typing <code>minikube start</code> into your terminal. And guess what? <code>minikube stop</code> will stop it 😉.</p>
<h3>Dockerizing ASP.NET Core</h3>
<p>Next, we need some ASP.NET Core Code running in a Docker Container, so lets spin up <a href="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/">JetBrains Rider</a> and create a new Web API project for example:</p>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/rider-project.png" alt="Rider rocks 😉" /></p>
<p>The default code in <code>Startup.cs</code> is fine here:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Startup</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">Startup</span><span class="token punctuation">(</span><span class="token class-name">IConfiguration</span> configuration<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
Configuration <span class="token operator">=</span> configuration<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IConfiguration</span> Configuration <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token comment">// This method gets called by the runtime. Use this method to add services to the container.</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ConfigureServices</span><span class="token punctuation">(</span><span class="token class-name">IServiceCollection</span> services<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token function">AddMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">SetCompatibilityVersion</span><span class="token punctuation">(</span>CompatibilityVersion<span class="token punctuation">.</span>Version_2_1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Configure</span><span class="token punctuation">(</span><span class="token class-name">IApplicationBuilder</span> app<span class="token punctuation">,</span> <span class="token class-name">IHostingEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
app<span class="token punctuation">.</span><span class="token function">UseMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>The default <code>ValuesController</code> is fine as well:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Route</span><span class="token attribute-arguments"><span class="token punctuation">(</span><span class="token string">"api/[controller]"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">ApiController</span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ValuesController</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">ControllerBase</span></span>
<span class="token punctuation">{</span>
<span class="token comment">// GET api/values</span>
<span class="token punctuation">[</span>HttpGet<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name">ActionResult<span class="token punctuation"><</span>IEnumerable<span class="token punctuation"><</span><span class="token keyword">string</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">Get</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name"><span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span></span> <span class="token punctuation">{</span><span class="token string">"value1"</span><span class="token punctuation">,</span> <span class="token string">"value2"</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// GET api/values/5</span>
<span class="token punctuation">[</span><span class="token function">HttpGet</span><span class="token punctuation">(</span><span class="token string">"{id}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name">ActionResult<span class="token punctuation"><</span><span class="token keyword">string</span><span class="token punctuation">></span></span> <span class="token function">Get</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> id<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token string">"value"</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// POST api/values</span>
<span class="token punctuation">[</span>HttpPost<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Post</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">FromBody</span></span><span class="token punctuation">]</span> <span class="token class-name"><span class="token keyword">string</span></span> <span class="token keyword">value</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">// PUT api/values/5</span>
<span class="token punctuation">[</span><span class="token function">HttpPut</span><span class="token punctuation">(</span><span class="token string">"{id}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Put</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> id<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">FromBody</span></span><span class="token punctuation">]</span> <span class="token class-name"><span class="token keyword">string</span></span> <span class="token keyword">value</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">// DELETE api/values/5</span>
<span class="token punctuation">[</span><span class="token function">HttpDelete</span><span class="token punctuation">(</span><span class="token string">"{id}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Delete</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> id<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Next, we need a <code>Dockerfile</code> in our project:</p>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/dockerfiler-rider.png" alt="" /></p>
<p>You can just grab the sample from the official <a href="https://docs.docker.com/engine/examples/dotnetcore/">Docker docs</a>.</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> microsoft/dotnet:sdk <span class="token keyword">AS</span> build-env</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token comment"># Copy csproj and restore as distinct layers</span>
<span class="token instruction"><span class="token keyword">COPY</span> *.csproj ./</span>
<span class="token instruction"><span class="token keyword">RUN</span> dotnet restore</span>
<span class="token comment"># Copy everything else and build</span>
<span class="token instruction"><span class="token keyword">COPY</span> . ./</span>
<span class="token instruction"><span class="token keyword">RUN</span> dotnet publish -c Release -o out</span>
<span class="token comment"># Build runtime image</span>
<span class="token instruction"><span class="token keyword">FROM</span> microsoft/dotnet:aspnetcore-runtime</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token instruction"><span class="token keyword">COPY</span> <span class="token options"><span class="token property">--from</span><span class="token punctuation">=</span><span class="token string">build-env</span></span> /app/out .</span>
<span class="token instruction"><span class="token keyword">ENTRYPOINT</span> [<span class="token string">"dotnet"</span>, <span class="token string">"HelloNetCoreOnK8s.dll"</span>]</span></code></pre>
<p>Make sure to change the second param of the <code>ENTRYPOINT</code> array to the name of your DLL.</p>
<p>Next, build your image:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> hello-netcore-k8s <span class="token builtin class-name">.</span></code></pre>
<p>You should get a result similar to this:</p>
<pre class="language-bash"><code class="language-bash">Sending build context to Docker daemon <span class="token number">1</span>.185MB
Step <span class="token number">1</span>/10 <span class="token builtin class-name">:</span> FROM microsoft/dotnet:sdk AS build-env
---<span class="token operator">></span> 6baac5bd0ea2
Step <span class="token number">2</span>/10 <span class="token builtin class-name">:</span> WORKDIR /app
---<span class="token operator">></span> Using cache
---<span class="token operator">></span> 8b1b0606dfc8
Step <span class="token number">3</span>/10 <span class="token builtin class-name">:</span> COPY *.csproj ./
---<span class="token operator">></span> Using cache
---<span class="token operator">></span> 987d3ba17281
Step <span class="token number">4</span>/10 <span class="token builtin class-name">:</span> RUN dotnet restore
---<span class="token operator">></span> Using cache
---<span class="token operator">></span> 829018788c05
Step <span class="token number">5</span>/10 <span class="token builtin class-name">:</span> COPY <span class="token builtin class-name">.</span> ./
---<span class="token operator">></span> 60d4d75c8825
Step <span class="token number">6</span>/10 <span class="token builtin class-name">:</span> RUN dotnet publish <span class="token parameter variable">-c</span> Release <span class="token parameter variable">-o</span> out
---<span class="token operator">></span> Running <span class="token keyword">in</span> 3e13616e9438
Microsoft <span class="token punctuation">(</span>R<span class="token punctuation">)</span> Build Engine version <span class="token number">15.8</span>.169+g1ccb72aefa <span class="token keyword">for</span> .NET Core
Copyright <span class="token punctuation">(</span>C<span class="token punctuation">)</span> Microsoft Corporation. All rights reserved.
Restoring packages <span class="token keyword">for</span> /app/HelloNetCoreOnK8s.csproj<span class="token punctuation">..</span>.
Generating MSBuild <span class="token function">file</span> /app/obj/HelloNetCoreOnK8s.csproj.nuget.g.props.
Generating MSBuild <span class="token function">file</span> /app/obj/HelloNetCoreOnK8s.csproj.nuget.g.targets.
Restore completed <span class="token keyword">in</span> <span class="token number">1.68</span> sec <span class="token keyword">for</span> /app/HelloNetCoreOnK8s.csproj.
HelloNetCoreOnK8s -<span class="token operator">></span> /app/bin/Release/netcoreapp2.1/HelloNetCoreOnK8s.dll
HelloNetCoreOnK8s -<span class="token operator">></span> /app/out/
Removing intermediate container 3e13616e9438
---<span class="token operator">></span> e0edd75b100e
Step <span class="token number">7</span>/10 <span class="token builtin class-name">:</span> FROM microsoft/dotnet:aspnetcore-runtime
---<span class="token operator">></span> 1fe6774e5e9e
Step <span class="token number">8</span>/10 <span class="token builtin class-name">:</span> WORKDIR /app
---<span class="token operator">></span> Using cache
---<span class="token operator">></span> 6c95530f2566
Step <span class="token number">9</span>/10 <span class="token builtin class-name">:</span> COPY <span class="token parameter variable">--from</span><span class="token operator">=</span>build-env /app/out <span class="token builtin class-name">.</span>
---<span class="token operator">></span> 34932a25e3b6
Step <span class="token number">10</span>/10 <span class="token builtin class-name">:</span> ENTRYPOINT <span class="token punctuation">[</span><span class="token string">"dotnet"</span>, <span class="token string">"HelloNetCoreOnK8s.dll"</span><span class="token punctuation">]</span>
---<span class="token operator">></span> Running <span class="token keyword">in</span> 599e41e2537f
Removing intermediate container 599e41e2537f
---<span class="token operator">></span> e24c6efee7d4
Successfully built e24c6efee7d4
Successfully tagged hello-netcore-k8s:latest</code></pre>
<h3>Hello k8s</h3>
<p>In order to create a k8s deployment, we need a yaml file which specifies what should be deployed in <code>minikube</code>.</p>
<p>Itshould be placed in our project as well:</p>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/rider-deployment-yaml.png" alt="" /></p>
<p>This is the content of the <code>deployment.yaml</code></p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> extensions/v1beta1
<span class="token key atrule">kind</span><span class="token punctuation">:</span> Deployment
<span class="token key atrule">metadata</span><span class="token punctuation">:</span>
<span class="token key atrule">name</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">spec</span><span class="token punctuation">:</span>
<span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">3</span>
<span class="token key atrule">template</span><span class="token punctuation">:</span>
<span class="token key atrule">metadata</span><span class="token punctuation">:</span>
<span class="token key atrule">labels</span><span class="token punctuation">:</span>
<span class="token key atrule">app</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">spec</span><span class="token punctuation">:</span>
<span class="token key atrule">containers</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">image</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">80</span></code></pre>
<p>The important part for now is the <code>spec/template/spec/containers</code>:</p>
<p><code>name</code> specifies the name of the containers and <code>image</code> specifies the name of the image the containers should be created from — this obviously is the name of the image we created recently.</p>
<p>So lets create that deployment now using <code>kubectl</code>:</p>
<pre class="language-bash"><code class="language-bash">kubectl create <span class="token parameter variable">-f</span> deployment.yaml</code></pre>
<p>Now check if the deployment has suceeded:</p>
<pre class="language-bash"><code class="language-bash">kubectl get deployments</code></pre>
<p>The result will look similar to this:</p>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/bash-first-deployment.png" alt="" /></p>
<p>Well... shouldn’t it be available? Lets dig a bit deeper and check the pods:</p>
<pre class="language-bash"><code class="language-bash">kubectl get pods</code></pre>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/kubectl-pods-error.png" alt="" /></p>
<p>Looks like we have a problem here. Seems <code>minikube</code> doesn’t know about our recently created image 🤔.</p>
<p>The reason is that the Docker daemon in <code>minikube</code> doesn’t know about our Docker daemon on our Linux/macOS host.</p>
<p>But there’s a simple solution for that - we can share the context using this command:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">eval</span> <span class="token variable"><span class="token variable">$(</span>minikube docker-env<span class="token variable">)</span></span></code></pre>
<p>You can stop sharing the context using eval <code>$(minikube docker-env -u)</code>.</p>
<p>Another solution would have been to push the image to the official Docker hub but we want to stay local for this sample.</p>
<p>Now we have to build our image again because of the different context — remember how to do it?</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> hello-netcore-k8s <span class="token builtin class-name">.</span></code></pre>
<p>Also we have to add the <code>imagePullPolicy</code>: Never to our <code>deployment.yaml</code> file:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> extensions/v1beta1
<span class="token key atrule">kind</span><span class="token punctuation">:</span> Deployment
<span class="token key atrule">metadata</span><span class="token punctuation">:</span>
<span class="token key atrule">name</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">spec</span><span class="token punctuation">:</span>
<span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">3</span>
<span class="token key atrule">template</span><span class="token punctuation">:</span>
<span class="token key atrule">metadata</span><span class="token punctuation">:</span>
<span class="token key atrule">labels</span><span class="token punctuation">:</span>
<span class="token key atrule">app</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">spec</span><span class="token punctuation">:</span>
<span class="token key atrule">containers</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">imagePullPolicy</span><span class="token punctuation">:</span> Never <span class="token comment"># <-- here we go!</span>
<span class="token key atrule">image</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">80</span></code></pre>
<p>This ensures that the Docker daemon doesn’ t try to pull unknown images from the Docker registry.</p>
<p>So… lets fire up our deployment again:</p>
<pre class="language-bash"><code class="language-bash">kubectl create <span class="token parameter variable">-f</span> deployment.yaml</code></pre>
<p>Validating the deployment using <code>kubectl get deployments</code> looks much better now:</p>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/bash-second-deployment.png" alt="" /></p>
<p>Also the result of <code>kubectl get pods</code> is much better now:</p>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/kubectl-pods-ok.png" alt="" /></p>
<h3>Accessing the Application using a Service</h3>
<p>So you might ask yourself: how do I access the application now?</p>
<p>In k8s and <code>minikube</code> as well, you have to create a service to expose the application to a public IP/Port. If you’ re running your application in a managed k8s environment at your cloud provider of choice, the type of the service to expose your application is likely to be a <code>LoadBalancer</code>. When using <code>minikube</code>, the only service type available is <code>nodePort</code>.</p>
<p>The command to expose your application using a <code>nodePort</code>, is this:</p>
<pre class="language-bash"><code class="language-bash">kubectl expose deployment hello-netcore-k8s <span class="token parameter variable">--type</span><span class="token operator">=</span>NodePort</code></pre>
<p>The result should be this:</p>
<pre class="language-text"><code class="language-text">service/hello-netcore-k8s exposed</code></pre>
<p>So, at which IP and Port has it been exposed? When using <code>minikube</code>, you have to use this command (this is different when using managed k8s):</p>
<pre class="language-bash"><code class="language-bash">minikube <span class="token function">service</span> hello-netcore-k8s <span class="token parameter variable">--url</span></code></pre>
<p>You’ll get an output like this:</p>
<pre class="language-text"><code class="language-text">http://192.168.99.100:31422</code></pre>
<p>Now lets call our ASP.NET Core API via <code>http get 192.168.99.100:31422/api/values</code> (I’m using <a href="https://httpie.org/">HTTPie</a> here, you can choose your preferred tool here — maybe <a href="https://www.getpostman.com/">Postman</a>):</p>
<pre class="language-text"><code class="language-text">HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 13 Nov 2018 21:30:33 GMT
Server: Kestrel
Transfer-Encoding: chunked[
"value1",
"value2"
]</code></pre>
<p>Yay! We’re done!</p>
<p>But wait — why are we running on port <code>31442</code> and not port <code>80</code>?</p>
<p>The reason for that is the default configuration of <code>minikube</code> which provides a Port range between <code>30000–32767</code> for services and by creating a service using the command from above a random port is picked from that pool.</p>
<p>We should fix this, right?</p>
<p>First, delete all services and deployments in <code>minikube</code>:</p>
<pre class="language-bash"><code class="language-bash">kubectl delete <span class="token function">service</span> hello-netcore-k8s
kubectl delete deployment hello-netcore-k8s</code></pre>
<p>Then stop <code>minikube</code> using <code>minikube stop</code>.</p>
<p>Now start minikube again using:</p>
<pre class="language-bash"><code class="language-bash">minikube start --extra-config<span class="token operator">=</span>apiserver.service-node-port-range<span class="token operator">=</span><span class="token number">80</span>-30000</code></pre>
<p>This will start <code>minikube</code> again, but with a Port range for services between <code>80-30000</code>.</p>
<p>Now that we have port <code>80</code> available, how can we use it?</p>
<p>Add a <code>services.yaml</code> to your project with this content:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1
<span class="token key atrule">kind</span><span class="token punctuation">:</span> Service
<span class="token key atrule">metadata</span><span class="token punctuation">:</span>
<span class="token key atrule">name</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">labels</span><span class="token punctuation">:</span>
<span class="token key atrule">app</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s
<span class="token key atrule">spec</span><span class="token punctuation">:</span>
<span class="token key atrule">type</span><span class="token punctuation">:</span> NodePort
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">80</span>
<span class="token key atrule">targetPort</span><span class="token punctuation">:</span> <span class="token number">80</span>
<span class="token key atrule">nodePort</span><span class="token punctuation">:</span> <span class="token number">80</span>
<span class="token key atrule">protocol</span><span class="token punctuation">:</span> TCP
<span class="token key atrule">selector</span><span class="token punctuation">:</span>
<span class="token key atrule">app</span><span class="token punctuation">:</span> hello<span class="token punctuation">-</span>netcore<span class="token punctuation">-</span>k8s</code></pre>
<p>This will assign port <code>80</code> to our service which is created using this command:</p>
<pre class="language-bash"><code class="language-bash">kubectl create <span class="token parameter variable">-f</span> services.yaml</code></pre>
<p>Now, <code>kubectl get services</code> will result in:</p>
<p><img src="https://alexanderzeitler.com/articles/running-asp-net-core-on-minikube/kubectl-services.png" alt="" /></p>
<p>As you can see, port <code>80</code> is used inside the Pod and mapped to the host port <code>80</code> as expected.</p>
<p>With that, <code>http get 192.168.99.100/api/values</code> again results in:</p>
<pre class="language-text"><code class="language-text">HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 13 Nov 2018 21:30:33 GMT
Server: Kestrel
Transfer-Encoding: chunked[
"value1",
"value2"
]</code></pre>
<p>But this time it is clearly using port <code>80</code> - and now we’re done! 🙌</p>
<p>You can find the source code <a href="https://github.com/PDMLab/aspnetcore-on-minikube">here</a>.</p>
Getting started with CQRS2018-04-11T21:45:00Zhttps://alexanderzeitler.com/articles/getting-started-with-cqrs/<p>CQRS and Event Sourcing have been there for a while now but especially in 2017 and the first months of 2018 we can see a rapid growth of inquires for CQRS based applications — why is this?</p>
<p>Many cloud based applications are built following the pattern of Microservices and are publishing or consuming events for integration purposes.</p>
<p>“The New Stack” recently released the article “<a href="https://thenewstack.io/microservices-its-all-about-the-events/">Microservices: It’s All About the Events</a>” and I want to quote this paragraph from the post:</p>
<blockquote>
<p>“One of the key things about distributed systems is that you have a whole bunch of independent control loops responsible for their own processing,” she said. One of the favored concepts in this space is Command Query Responsibility Segregation (CQRS), which separates the channels for inserting data into a data store from the channel of querying that data, so that the performance of one is not dependent on another."</p>
</blockquote>
<br />
<p>At <a href="https://pdmlab.com/">PDMLab</a>, we’re building several SaaS solutions applying the CQRS pattern and recently I’ve been asked, how I got started with CQRS.</p>
<p>Instead of repeating this answer over and over again, I decided to share my resources, that helped me getting into CQRS a few years ago, in this post.</p>
<p>First of all - of course - some videos from Greg Young, who came up with the definition of CQRS.</p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/8JKjvY4etTY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<br />
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/LDW0QWie21s" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<br />
Greg Young also has a simple CQRS sample on GitHub: [https://github.com/gregoryyoung/m-r](https://github.com/gregoryyoung/m-r)
<p>Several blog posts to get started with CQRS can be found at <a href="https://cqrs.wordpress.com/">https://cqrs.wordpress.com/</a></p>
<p>A book, which helped me getting into CQRS was "CQRS" by Mark Nijhof: <a href="https://leanpub.com/cqrs">https://cqrs.wordpress.com/</a></p>
<p>Another video worth watching is "Eventually Consistent Distributed Systems with Node.js":</p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/X_VHWQa1k0k" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<br />
<p>If you're looking for a more complete implementation of a sample application, you should have a look at the CQRS post series from Matthew Jones <a href="https://exceptionnotfound.net/tag/cqrs/">here</a> (posts) and <a href="https://github.com/exceptionnotfound/DotNetCqrsDemo">here</a> (code).</p>
<p>The samples from Matthew are based on <a href="https://github.com/gautema/CQRSlite">CQRSLite</a>, a CQRS framework for .NET Standard which we're using as well.</p>
<p>If you're looking for a CQRS framework for Node.js, you should have a look at <a href="https://www.wolkenkit.io/">wolkenkit</a>.</p>
<p>If you're around in Vancouver these days, you also should attend <a href="https://www.eventbrite.ca/e/lestercon-vancouver-2018-tickets-44856176030">Lestercon</a> where Adaptech will open source some of their Microservices / CQRS stacks and toolings.</p>
Accessing an HTTP API running on your MacBook in a Docker container from your iPhone/iPad using dnsmasq2017-04-23T00:00:00Zhttps://alexanderzeitler.com/articles/accessing-an-api-in-docker-on-macbook-from-ios-iphone-ipad-using-dnsmasq/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Accessing an HTTP API running on your MacBook in a Docker container from your iPhone/iPad using dnsmasq" />
<meta name="twitter:description" content="Accessing an HTTP API running on your MacBook in a Docker container from your iPhone/iPad using dnsmasq" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/accessing-an-api-in-docker-on-macbook-from-ios-iphone-ipad-using-dnsmasq/dnsmasqui.png" />
<p>Please consider the following scenario:</p>
<p>On my MacBook Pro I want to access a web application (SPA/PWA) and an API both running in Docker containers behind another nginx Docker container (reverse proxy managing SSL) from an iOS device.</p>
<p>So I have:</p>
<ul>
<li>1 iOS device having IP <code>192.168.178.77</code></li>
<li>1 MacBook Pro having IP <code>192.168.178.64</code></li>
<li>1 Docker container running nginx having two <code>server_name</code> settings: app.dev and api.dev, both using self-signed SSL certificates.</li>
</ul>
<p>The challenge: the're no option in the local Router to point DNS entries to <code>192.168.178.64</code>.</p>
<p><code>/etc/hosts</code> has these entries:</p>
<pre class="language-text"><code class="language-text">192.168.178.64 app.dev
192.168.178.64 api.dev</code></pre>
<p>The easiest solution I've found is to use <code>dnsmasq</code> running in a Docker container.</p>
<p>So what is <code>dnsmasq</code>?</p>
<blockquote>
<p>Dnsmasq is a lightweight, easy to configure, DNS forwarder and DHCP server. It is designed to provide DNS and optionally, DHCP, to a small network. It can serve the names of local machines which are not in the global DNS. The DHCP server integrates with the DNS server and allows machines with DHCP-allocated addresses to appear in the DNS with names configured either in each host or in a central configuration file. Dnsmasq supports static and dynamic DHCP leases and BOOTP/TFTP for network booting of diskless machines</p>
</blockquote>
<p><a href="https://wiki.debian.org/HowTo/dnsmasq">source</a></p>
<p>In order to start <code>dnsmasq</code> in Docker container, simply run this command:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token punctuation">\</span>
<span class="token parameter variable">--name</span> dnsmasq <span class="token punctuation">\</span>
<span class="token parameter variable">-d</span> <span class="token punctuation">\</span>
<span class="token parameter variable">-p</span> <span class="token number">53</span>:53/udp <span class="token punctuation">\</span>
<span class="token parameter variable">-p</span> <span class="token number">5380</span>:8080 <span class="token punctuation">\</span>
<span class="token parameter variable">-v</span> /opt/dnsmasq.conf:/etc/dnsmasq.conf <span class="token punctuation">\</span>
--log-opt <span class="token string">"max-size=100m"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">-e</span> <span class="token string">"USER=foo"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">-e</span> <span class="token string">"PASS=bar"</span> <span class="token punctuation">\</span>
jpillora/dnsmasq</code></pre>
<p>This will run <code>dnsmasq</code> mapping a configuration from <code>/opt/dnsmasq.conf</code> into the container.</p>
<p>Make sure <code>/opt/dnsmsq.conf</code> at least contains this line:</p>
<pre><code>address=/dev/192.168.178.64
</code></pre>
<p>This will route all traffic to <code>*.dev</code> to <code>192.168.178.64</code>.</p>
<p><code>dnsmasq</code> will also inspect your local <code>/etc/hosts</code> file and route traffic appropriately.</p>
<p>It also provides a simple UI where you can verify your <code>hosts</code> file got hooked up correctly:
Just browse to <code>http://localhost:5380</code> and enter the credentials <code>foo</code> and <code>bar</code> from the <code>docker run</code> command above.</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-an-api-in-docker-on-macbook-from-ios-iphone-ipad-using-dnsmasq/dnsmasqui.png" alt="" /></p>
<p>The UI output should look like this:</p>
<pre class="language-text"><code class="language-text">[webproc] 2017/04/23 18:35:54 loaded config files changes from disk
[webproc] 2017/04/23 18:35:54 agent listening on http://0.0.0.0:8080...
dnsmasq: started, version 2.76 cachesize 150
dnsmasq: compile time options: IPv6 GNU-getopt no-DBus no-i18n no-IDN DHCP DHCPv6 no-Lua TFTP no-conntrack ipset auth no-DNSSEC loop-detect inotify
dnsmasq: reading /etc/resolv.conf
dnsmasq: using nameserver 192.168.65.1#53
dnsmasq: read /etc/hosts - 7 addresses</code></pre>
<p>The next step is to update the DNS settings on the iOS and macOS devices.<br />
The iOS device has to point to the IP address of the macOS device:</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-an-api-in-docker-on-macbook-from-ios-iphone-ipad-using-dnsmasq/ios.png" alt="" /></p>
<p>The macOS device has to point to itself:</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-an-api-in-docker-on-macbook-from-ios-iphone-ipad-using-dnsmasq/macosnetwork.png" alt="" /></p>
<p>Click "Advanced...", then select "DNS":</p>
<p><img src="https://alexanderzeitler.com/articles/accessing-an-api-in-docker-on-macbook-from-ios-iphone-ipad-using-dnsmasq/macosnetworkdns.png" alt="" /></p>
<p>The last step is to call the <code>https://app.dev</code> and <code>https://api.dev</code> URIs on the iOS device and confirm the SSL warnings.</p>
<p>Other solutions to this seem to be http://xip.io/ or http://nip.io/ but I didn't try them...</p>
<p>Happy developing!</p>
Fixing Chrome 58+ [missing_subjectAltName] with openssl when using self signed certificates2017-04-23T00:00:00Zhttps://alexanderzeitler.com/articles/Fixing-Chrome-missing_subjectAltName-selfsigned-cert-openssl/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Fixing Chrome 58+ [missing_subjectAltName] with openssl when using self signed certificates" />
<meta name="twitter:description" content="Fixing Chrome 58+ [missing_subjectAltName] with openssl when using self signed certificates" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/Fixing-Chrome-missing_subjectAltName-selfsigned-cert-openssl/missing_subjectAltName.png" />
<p>Since version 58, Chrome requires SSL certificates to use SAN (Subject Alternative Name) instead of the popular Common Name (CN), thus <a href="https://groups.google.com/a/chromium.org/forum/#!msg/security-dev/IGT2fLJrAeo/csf_1Rh1AwAJ">CN support has been removed</a>.<br />
If you're using self signed certificates (but not only!) having only CN defined, you get an error like this when calling a website using the self signed certificate:</p>
<p><img src="https://alexanderzeitler.com/articles/Fixing-Chrome-missing_subjectAltName-selfsigned-cert-openssl/missing_subjectAltName.png" alt="" /></p>
<p>Here's how to create a self signed certificate with SAN using <code>openssl</code></p>
<p>First, lets create a root CA cert using <code>createRootCA.sh</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/usr/bin/env bash</span>
<span class="token function">mkdir</span> ~/ssl/
openssl genrsa <span class="token parameter variable">-des3</span> <span class="token parameter variable">-out</span> ~/ssl/rootCA.key <span class="token number">2048</span>
openssl req <span class="token parameter variable">-x509</span> <span class="token parameter variable">-new</span> <span class="token parameter variable">-nodes</span> <span class="token parameter variable">-key</span> ~/ssl/rootCA.key <span class="token parameter variable">-sha256</span> <span class="token parameter variable">-days</span> <span class="token number">1024</span> <span class="token parameter variable">-out</span> ~/ssl/rootCA.pem</code></pre>
<p>Next, create a file <code>createselfsignedcertificate.sh</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/usr/bin/env bash</span>
<span class="token function">sudo</span> openssl req <span class="token parameter variable">-new</span> <span class="token parameter variable">-sha256</span> <span class="token parameter variable">-nodes</span> <span class="token parameter variable">-out</span> server.csr <span class="token parameter variable">-newkey</span> rsa:2048 <span class="token parameter variable">-keyout</span> server.key <span class="token parameter variable">-config</span> <span class="token operator"><</span><span class="token punctuation">(</span> <span class="token function">cat</span> server.csr.cnf <span class="token punctuation">)</span>
<span class="token function">sudo</span> openssl x509 <span class="token parameter variable">-req</span> <span class="token parameter variable">-in</span> server.csr <span class="token parameter variable">-CA</span> ~/ssl/rootCA.pem <span class="token parameter variable">-CAkey</span> ~/ssl/rootCA.key <span class="token parameter variable">-CAcreateserial</span> <span class="token parameter variable">-out</span> server.crt <span class="token parameter variable">-days</span> <span class="token number">500</span> <span class="token parameter variable">-sha256</span> <span class="token parameter variable">-extfile</span> v3.ext</code></pre>
<p>Then, create the openssl configuration file <code>server.csr.cnf</code> referenced in the openssl command above:</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>req<span class="token punctuation">]</span>
default_bits <span class="token operator">=</span> <span class="token number">2048</span>
prompt <span class="token operator">=</span> no
default_md <span class="token operator">=</span> sha256
distinguished_name <span class="token operator">=</span> dn
<span class="token punctuation">[</span>dn<span class="token punctuation">]</span>
<span class="token assign-left variable">C</span><span class="token operator">=</span>US
<span class="token assign-left variable">ST</span><span class="token operator">=</span>New York
<span class="token assign-left variable">L</span><span class="token operator">=</span>Rochester
<span class="token assign-left variable">O</span><span class="token operator">=</span>End Point
<span class="token assign-left variable">OU</span><span class="token operator">=</span>Testing Domain
<span class="token assign-left variable">emailAddress</span><span class="token operator">=</span>your-administrative-address@your-awesome-existing-domain.com
CN <span class="token operator">=</span> localhost</code></pre>
<p>Now we need to create the <code>v3.ext</code> file in order to create a X509 v3 certificate instead of a v1 which is the default when not specifying a extension file:</p>
<pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">authorityKeyIdentifier</span><span class="token operator">=</span>keyid,issuer
<span class="token assign-left variable">basicConstraints</span><span class="token operator">=</span>CA:FALSE
keyUsage <span class="token operator">=</span> digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName <span class="token operator">=</span> @alt_names
<span class="token punctuation">[</span>alt_names<span class="token punctuation">]</span>
DNS.1 <span class="token operator">=</span> localhost</code></pre>
<p>In order to create your cert, first run <code>createRootCA.sh</code> which we created first.
Next, run <code>createselfsignedcertificate.sh</code> to create the self signed cert using <code>localhost</code> as the SAN and CN.</p>
<p>After adding the <code>rootCA.pem</code> to the list of your trusted root CAs, you can use the <code>server.key</code> and <code>server.crt</code> in your web server and browse <code>https://localhost</code> using Chrome 58 or later:</p>
<p><img src="https://alexanderzeitler.com/articles/Fixing-Chrome-missing_subjectAltName-selfsigned-cert-openssl/validsan.jpg" alt="" /></p>
<p>You can also verify your certificate to contain the SAN by calling</p>
<pre class="language-bash"><code class="language-bash">openssl x509 <span class="token parameter variable">-text</span> <span class="token parameter variable">-in</span> server.crt <span class="token parameter variable">-noout</span></code></pre>
<p>This should look like this:</p>
<pre class="language-text"><code class="language-text">Certificate:
Data:
Version: 3 (0x2)
Serial Number: 17237690484651272016 (0xef38942aa5c52750)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=New York, L=Rochester, O=End Point, CN=localhost/your-administrative-address@your-awesome-existing-domain.com
Validity
Not Before: Apr 23 16:07:38 2017 GMT
Not After : Sep 5 16:07:38 2018 GMT
Subject: C=US, ST=New York, L=Rochester, O=End Point, OU=Testing Domain/emailAddress=your-administrative-address@your-awesome-existing-domain.com, CN=localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b2:e3:bd:ed:28:04:85:ea:75:ee:d2:82:e1:eb:
f5:5f:7f:cf:7e:cb:70:de:86:9f:75:7c:f3:71:e7:
da:16:fb:bc:1f:89:bc:47:08:77:ca:33:20:f1:c1:
9e:e3:20:8d:89:14:7e:c1:0a:12:d2:59:24:56:9b:
77:90:5f:69:d1:a5:f1:00:38:93:1b:a7:75:f1:33:
e2:da:dc:32:a9:0a:85:7d:9a:20:81:ca:20:ee:86:
ce:e2:a0:52:d2:ab:11:34:e5:52:99:3a:81:c6:9f:
6b:0f:6a:02:2b:38:a6:84:c9:ba:fa:9b:ef:0a:89:
22:4b:79:86:3c:bd:44:a5:54:fb:cf:4d:8b:d1:44:
03:35:22:de:69:77:c8:fa:4d:c6:01:25:08:9f:4d:
a9:79:7a:aa:ca:03:b6:e4:51:57:22:27:5f:a7:12:
11:f3:e6:00:29:f6:58:be:2c:aa:09:e4:06:45:d9:
3f:75:a7:f0:75:bd:2b:a6:bb:6d:ad:93:bb:b9:1d:
d7:75:39:4e:9b:1d:0e:39:cc:17:74:88:f7:e2:b7:
85:12:96:e0:cb:42:56:d0:11:e0:84:86:e5:14:a5:
f2:6d:43:5d:f9:59:ae:61:7f:01:ae:95:b8:92:27:
1d:1c:02:d7:ad:fb:ee:f6:25:38:60:c8:41:20:17:
80:69
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
keyid:5A:8D:89:64:BD:F2:3E:C2:D7:7B:BE:17:84:F4:29:E8:C5:32:35:34
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Subject Alternative Name:
DNS:localhost
Signature Algorithm: sha256WithRSAEncryption
27:1d:d6:84:50:33:d2:ff:b1:06:9b:fa:f1:40:7d:47:11:bc:
f7:80:fd:26:87:0e:91:9f:14:be:1f:1d:9b:32:d1:fb:d6:8d:
af:30:8a:88:38:8c:1c:bf:77:98:8e:cd:06:48:82:fa:09:b9:
3c:0d:38:c4:a0:da:b7:4d:f5:81:5f:5a:76:04:61:f8:c2:1a:
17:ad:56:7c:72:ba:f6:65:7f:7f:e7:5e:b2:34:ba:13:23:57:
84:f1:c5:ca:dd:5b:55:69:95:71:44:4a:30:53:61:5c:ad:47:
d8:9c:d5:a2:1b:18:2d:e1:19:35:3e:3f:b2:7e:fd:bf:f3:d0:
45:dc:f5:57:f0:1b:cd:70:1b:e0:34:de:27:98:89:b4:a5:25:
a5:6c:29:c3:89:a6:a5:c5:4d:f5:45:3b:47:8e:13:45:23:07:
5e:d6:59:0d:96:c6:a3:f0:c5:3d:ee:a8:ad:36:96:43:13:a1:
b8:55:f6:c7:10:7e:8f:5d:09:ef:61:17:2a:9c:3b:50:28:c8:
e3:8d:a6:34:06:50:d4:3e:d5:17:ea:7d:31:97:d3:ee:df:b5:
23:66:5e:22:b7:e4:fa:36:4f:9a:d5:f0:a3:f9:b4:2b:27:02:
0b:41:94:d1:a1:f7:1b:2c:7e:74:e6:14:c3:b5:67:15:d2:ca:
02:77:57:a6</code></pre>
<p>Watch for this line <code>Version: 3 (0x2)</code> as well as <code>X509v3 Subject Alternative Name:</code> (and below).</p>
<p>Happy self signing!</p>
Non-Interactive Azure Resource Manager (ARM) login2016-06-25T00:00:00Zhttps://alexanderzeitler.com/articles/Non-Interactive-Azure-Resource-Manager-Login/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Non-Interactive Azure Resource Manager (ARM) login" />
<meta name="twitter:description" content="How to log into Azure Resource Manager without entering credentials" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/Non-Interactive-Azure-Resource-Manager-Login/arm.png" />
<p><a href="https://azure.microsoft.com/en-us/documentation/articles/resource-group-overview/">Azure Resource Manager</a> allows you to deploy, manage, and monitor all of the resources for your solution as a group, rather than handling these resources individually.</p>
<p>TL;DR from the docs:</p>
<blockquote>
<p>With Resource Manager, you can create a simple template (in JSON format) that defines deployment and configuration of your application. This template is known as a Resource Manager template and provides a declarative way to define deployment. By using a template, you can repeatedly deploy your application throughout the app lifecycle and have confidence your resources are deployed in a consistent state.</p>
</blockquote>
<p>Before you can deploy a resource group using PowerShell, you have to login using the <code>Login-AzureRmAccount</code> command.</p>
<p>By default, this opens a dialog where you can enter your Azure credentials:</p>
<p><img src="https://alexanderzeitler.com/articles/Non-Interactive-Azure-Resource-Manager-Login/login.png" alt="" /></p>
<p>This is ok if you are working with ARM on your local machine, but you don't want this behavior on your CI-Server for example.</p>
<p>Luckily, the <code>Login-AzureRmAccount</code> command also accepts an <code>-Credential</code> parameter which is an object consisting of your username and password.</p>
<p>The following PowerShell commands show how to create this object:</p>
<pre class="language-powershell"><code class="language-powershell"><span class="token variable">$accountName</span> =<span class="token string">"your account name"</span>
<span class="token variable">$password</span> = <span class="token function">ConvertTo-SecureString</span> <span class="token string">"your password"</span> <span class="token operator">-</span>AsPlainText <span class="token operator">-</span>Force
<span class="token variable">$credential</span> = <span class="token function">New-Object</span> System<span class="token punctuation">.</span>Management<span class="token punctuation">.</span>Automation<span class="token punctuation">.</span>PSCredential<span class="token punctuation">(</span><span class="token variable">$accountName</span><span class="token punctuation">,</span> <span class="token variable">$password</span><span class="token punctuation">)</span></code></pre>
<p>Now you can login using the <code>Login-AzureRmAccount</code> command like this:</p>
<pre class="language-powershell"><code class="language-powershell">Login-AzureRmAccount <span class="token operator">-</span>Credential <span class="token variable">$credential</span></code></pre>
<p>If you're providing username and password using environment variables for example, you can set <code>$accountName</code> and <code>password</code> using these commands:</p>
<pre class="language-powershell"><code class="language-powershell"><span class="token variable">$accountName</span> = <span class="token punctuation">(</span><span class="token function">get-item</span> env:<span class="token string">"ACCOUNTNAME"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Value
<span class="token variable">$password</span> = <span class="token punctuation">(</span><span class="token function">get-item</span> env:<span class="token string">"PASSWORD"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Value</code></pre>
<p>Happy deployment!</p>
Mongoose toObject and toJSON transform behavior with sub-documents2016-06-08T00:00:00Zhttps://alexanderzeitler.com/articles/mongoose-tojson-toobject-transform-with-subdocuments/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Mongoose toObject and toJSON transform behavior with sub-documents" />
<meta name="twitter:description" content="Mongoose toObject and toJSON transform behavior with sub-documents" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/mongoose-tojson-toobject-transform-with-subdocuments/mongoose.png" />
<p>Mongoose supports two Schema options to transform Objects after querying MongoDb: <code>toObject</code> and <code>toJSON</code>.</p>
<p>In general you can access the returned object in the <code>transform</code> method <code>toObject</code> or <code>toJSON</code> as described in <a href="http://mongoosejs.com/docs/api.html#document_Document-toObject">the docs</a>.</p>
<p>Where things get interesting is when you're trying to use sub-documents and want them to be not touched by the transform method of the root or parent document.</p>
<p>After playing around with <code>toObject</code> and <code>toJSON</code> transforms with sub-documents, I observed the behaviors described as follows.</p>
<p>First, our two schemas for context:</p>
<p>User.js:</p>
<pre class="language-js"><code class="language-js"><span class="token string">'use strict'</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> UserSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">name</span><span class="token operator">:</span> String
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">"User"</span><span class="token punctuation">,</span> UserSchema<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Post.js:</p>
<pre class="language-js"><code class="language-js"><span class="token string">'use strict'</span>
<span class="token keyword">let</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> PostSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">title</span><span class="token operator">:</span> String<span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">type</span><span class="token operator">:</span> mongoose<span class="token punctuation">.</span>Schema<span class="token punctuation">.</span>Types<span class="token punctuation">.</span>ObjectId<span class="token punctuation">,</span>
<span class="token literal-property property">ref</span><span class="token operator">:</span> <span class="token string">'User'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">comments</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> String<span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">type</span><span class="token operator">:</span> mongoose<span class="token punctuation">.</span>Schema<span class="token punctuation">.</span>Types<span class="token punctuation">.</span>ObjectId<span class="token punctuation">,</span>
<span class="token literal-property property">ref</span><span class="token operator">:</span> <span class="token string">'User'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">toObject</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token function-variable function">transform</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">doc<span class="token punctuation">,</span> ret</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">delete</span> ret<span class="token punctuation">.</span>_id<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">toJSON</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token function-variable function">transform</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">doc<span class="token punctuation">,</span> ret</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">delete</span> ret<span class="token punctuation">.</span>_id<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">"Post"</span><span class="token punctuation">,</span> PostSchema<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>As you can see, <code>mongoose.Schema</code> accepts a second parameter which contains the definitions for <code>toObject</code> and <code>toJSON</code>.</p>
<p>Next, let's use both Schemas in simple sample:</p>
<p>app.js</p>
<pre class="language-js"><code class="language-js"><span class="token string">'use strict'</span><span class="token punctuation">;</span>
<span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./database"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> User <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./User'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
Post <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./Post'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> alex <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Alex"</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> joe <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Joe"</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
alex<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
joe<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> post <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"Hello World"</span><span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> alex<span class="token punctuation">.</span>_id<span class="token punctuation">,</span>
<span class="token literal-property property">comments</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">"Nice post!"</span><span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> joe<span class="token punctuation">.</span>_id
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">"Thanks :)"</span><span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> alex<span class="token punctuation">.</span>_id
<span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
post<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Post<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">populate</span><span class="token punctuation">(</span><span class="token string">'postedBy'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">populate</span><span class="token punctuation">(</span><span class="token string">'comments.postedBy'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> posts</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>posts<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The result is as follows:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">[</span> <span class="token punctuation">{</span> comments<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span>Object<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>Object<span class="token punctuation">]</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
__v<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
postedBy<span class="token operator">:</span> <span class="token punctuation">{</span> __v<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> name<span class="token operator">:</span> 'Alex' <span class="token punctuation">}</span><span class="token punctuation">,</span>
title<span class="token operator">:</span> 'Hello World' <span class="token punctuation">}</span> <span class="token punctuation">]</span></code></pre>
<p>As you can see, <code>_id</code> from both <code>Post</code> and <code>User</code> get removed by the <code>toObject</code> transformation.</p>
<p>Next, we'll replace <code>console.log(posts)</code> as follwos:</p>
<pre class="language-js"><code class="language-js">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>posts<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">'\t'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The result is the following JSON:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Hello World"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"57588aa352559c927c98c793"</span><span class="token punctuation">,</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Alex"</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
<span class="token property">"comments"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"Nice post!"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"57588aa352559c927c98c794"</span><span class="token punctuation">,</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Joe"</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"57588aa352559c927c98c797"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"Thanks :)"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"57588aa352559c927c98c793"</span><span class="token punctuation">,</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Alex"</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"57588aa352559c927c98c796"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span></code></pre>
<p>The <code>_id</code> from <code>Post</code> gets removed while <code>_id</code> from <code>User</code> remains.</p>
<p>The same behavior can be seen when we call <code>toJSON</code> explicitly:</p>
<pre class="language-js"><code class="language-js"> <span class="token keyword">let</span> json <span class="token operator">=</span> posts<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">p</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> p<span class="token punctuation">.</span><span class="token function">toJSON</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>json<span class="token punctuation">)</span></code></pre>
<p>Result:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">[</span> <span class="token punctuation">{</span> title<span class="token operator">:</span> 'Hello World'<span class="token punctuation">,</span>
postedBy<span class="token operator">:</span> <span class="token punctuation">{</span> _id<span class="token operator">:</span> 57588c7ebf8340cc859660e1<span class="token punctuation">,</span> name<span class="token operator">:</span> 'Alex'<span class="token punctuation">,</span> __v<span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
__v<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
comments<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span>Object<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>Object<span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span> title<span class="token operator">:</span> 'Hello World'<span class="token punctuation">,</span>
postedBy<span class="token operator">:</span> <span class="token punctuation">{</span> _id<span class="token operator">:</span> 57588c97fc8232548659e6d4<span class="token punctuation">,</span> name<span class="token operator">:</span> 'Alex'<span class="token punctuation">,</span> __v<span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
__v<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
comments<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span>Object<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>Object<span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span></code></pre>
<p>So it looks like <code>toObject</code> is being applied to sub-documents while <code>toJSON</code> is not. And this is different from what the <a href="http://mongoosejs.com/docs/api.html#document_Document-toObject">documentation</a> says:</p>
<blockquote>
<p>Transforms are applied only to the document and are not applied to sub-documents.</p>
</blockquote>
<p>The other option is that I'm wrong but questions on stackoverflow show it really might be the documentation.</p>
Deploying Ubuntu Mate Desktop as a developer environment in a Azure VM2016-06-01T00:00:00Zhttps://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Deploying Ubuntu Mate Desktop as a developer environment in a Azure VM'" />
<meta name="twitter:description" content="Want to install a Ubuntu Desktop in Azure without going nuts?" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/ubuntumateazurevm.jpg" />
<blockquote>
<p>TL;DR: While boring looking XFCE installation on Ubuntu Server is shown everywhere, this post shows you how to get a nice Mate Desktop on Ubuntu in Azure up and running and RDP into it.</p>
</blockquote>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/ubuntumateazurevm.jpg" alt="" /></p>
<p>In Windows Server environments you just spin up a Server and have a GUI, install Visual Studio and start developing.</p>
<p>But with Ubuntu Server things get a little bit more complicated.</p>
<p>I've chosen (read: learned to choose) Mate Desktop, because it is one of the environments that looks pretty nice but doesn't require 3D acceleration enabled which you just don't have in cloud environments (at least not in the general compute instances).</p>
<p>First, create a Azure VM instance of your choice with a username and password, enable inbound Port 3389 (RDP) in its NIC security group settings and SSH into it.</p>
<p>Enabling Port 3389 for RDP, first select "Network interfaces":<br />
<img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/vmdetails.png" alt="" /></p>
<p>Next, select the "Network security group":
<img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/vmnic.png" alt="" /></p>
<p>Select "Inbound security rules":
<img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/vmnicsecuritygroup.png" alt="" /></p>
<p>You'll see the default inbound rules:
<img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/inboundrulesdefault.png" alt="" /></p>
<p>Add a new rule for RDP via port 3389:<br />
<img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/addrdprule.png" alt="" /></p>
<p>Check the updated inbound rules:<br />
<img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/inboundrulesupdated.png" alt="" /></p>
<p>After that, install the latest updates:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt-get</span> udpate
<span class="token function">sudo</span> <span class="token function">apt-get</span> upgrade
<span class="token function">sudo</span> <span class="token function">apt-get</span> dist-ugprade</code></pre>
<p>With that done, install the Ubuntu Mate desktop and <code>xrdp</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">DEBIAN_FRONTEND</span><span class="token operator">=</span>noninteractive
<span class="token function">sudo</span> apt-add-repository ppa:ubuntu-mate-dev/ppa
<span class="token function">sudo</span> apt-add-repository ppa:ubuntu-mate-dev/trusty-mate
<span class="token function">sudo</span> <span class="token function">apt-get</span> update <span class="token operator">&&</span> <span class="token function">sudo</span> <span class="token function">apt-get</span> upgrade
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> --no-install-recommends ubuntu-mate-core ubuntu-mate-desktop
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> mate-core mate-desktop-environment mate-notification-daemon
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> xrdp</code></pre>
<p>Now, make Mate the default desktop environment for <code>xrdp</code> sessions (also for new users being created afterwards):</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">echo</span> mate-session<span class="token operator">></span> ~/.xsession
<span class="token function">sudo</span> <span class="token function">cp</span> ~/.xsession /etc/skel</code></pre>
<p>Finally, we'll allow changing the host post port we can connect to (this allows reconnecting to an abandoned session):</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/xrdp/xrdp.ini</code></pre>
<p>Change <code>port=-1</code> in the <code>[xrdp1]</code> section to <code>port=ask-1</code>.</p>
<p>With the changes done, restart <code>xrdp</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">service</span> xrdp restart</code></pre>
<p>Now we're able to RDP into our Azure VM from Windows:</p>
<p>You should get the <code>xrdp</code> login screen were you enter your users credentials and make sure <code>sesman-Xvnc</code> module is selected:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/rdplogin.png" alt="" /></p>
<p>If everything went well, you should get this connection log:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/rdploginsuccess.png" alt="" /></p>
<p>After a second or two, you should also get the Ubuntu Mate desktop screen:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-a-azure-vm/ubuntumateazurevm.jpg" alt="" /></p>
<p>If you think we're done now: no.</p>
<h2>Running software in the Mate environment</h2>
<p>Here are some tips for running software in that environment.</p>
<h3>Ubuntu Software Center</h3>
<p>First of all, Ubuntu Software Center doesn't work due to some configuration errors I didn't get behind.<br />
So if you have some nice <code>.deb</code> packages like Dropbox, just install them via Terminal:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> dpkg <span class="token parameter variable">-i</span> <span class="token operator"><</span>package<span class="token operator">></span>.deb</code></pre>
<h3>Changing the timezone</h3>
<p>If you want to change the timezone of your instance, you can't unlock and edit the setting in the UI.</p>
<p>Thus, you have to run <code>sudo dpkg-reconfigure tzdata</code> in Terminal and select the appropriate setting.</p>
<h3>Dropbox</h3>
<p>If you plan to install Dropbox, just download the <code>.deb</code> file and install it as described before.<br />
You can start Dropbox using <code>start dropbox -i</code> to install the daemon.</p>
<p>After the installation you might see a weird or even no notification icon.<br />
Just restart your Azure VM and everything should be ok.</p>
<p>If you still experience issues, you can also install <code>caja-dropbox</code> with is optimizied for the caja file manager of MATE.</p>
<h3>Restart / Shutdown via Mate</h3>
<p>Speaking of restarting: You might notice that using the Mate logout / shutdown / restart feature won't end your RDP session.
Just use <code>sudo reboot</code> or <code>sudo shutdown now -h</code> in Terminal.</p>
<h3>Visual Studio Code / ATOM</h3>
<p>It just doesn't work in <code>xrdp</code> environments as of now - even with the latest Insider build installed.</p>
<p>Today I published a <a href="https://gist.github.com/AlexZeitler/ddd6bbf46f5a260b88565b953f5c1d3b">gist</a> which describes a fix to get VS Code / ATOM in Ubuntu via RDP working.</p>
<p>I would be happy if you share further tips in the comments.</p>
Deploying Ubuntu Mate Desktop as a developer environment in AWS EC22016-04-29T00:00:00Zhttps://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Deploying Ubuntu Mate Desktop as a developer environment in AWS EC2" />
<meta name="twitter:description" content="Want to install a Ubuntu Desktop in the cloud without going nuts?" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/ubuntumateonawsec2_twitter.png" />
<blockquote>
<p>TL;DR: If you don't want to find out the solution by running over 40 Ubuntu installations with several desktop environments on AWS, read on.</p>
</blockquote>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/ubuntumateonawsec2_twitter.png" alt="" /></p>
<p>On a current project the idea came up to put Ubuntu Desktop developer environments into AWS EC2 instead of local installations (VMs) and RDP into it.</p>
<p>If you have a Windows background, you would expect to just spin up a server and have a UI...</p>
<p>But with Ubuntu things get a little *cough* bit more complicated.</p>
<p>In general, there are several options:</p>
<ul>
<li>Install an Ubuntu or Debian Server and install an Ubuntu Desktop environment onto it, then connect via VNC to it</li>
<li>Install an Ubuntu or Debian Server and install an Ubuntu Desktop environment onto it, then connect via RDP to it</li>
<li>Create an AMI based on an existing AMI</li>
<li>Create an AMI from scratch</li>
</ul>
<p>Another option that sounds appealing <em>won't</em> work: Create a Windows Server VM, activate Hyper-V, install VMware Workstation or VirtualBox and just run your Ubuntu Desktop inside it.<br />
The first two options won't work because you can't have nested Hypervisors in AWS EC2 and VirtualBox would only allow 32bit OS installations because of this constraint.</p>
<p>So, back to the viable options: First, I tried to install Ubuntu Server 14.04 from the official AWS AMI, install the XFCE or GNOME-Desktop on it as well as <code>vncserver</code>.<br />
While it works in general, the VNC performance and quality is not acceptable in 2016 (I expect true color and reasonable screen resolutions) - especially with GNOME or more complex desktop environments.<br />
Besides that, XFCE feels like late 90th:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/xfce.jpg" alt="" /></p>
<p>So, lets head over to the next option - the one I ended up with:</p>
<p>Install Ubuntu Server from the official AWS EC2 AMI, put Ubuntu Mate desktop on it and RDP into it.<br />
What sounds like a straight forward plan, ended up as a *interesting* journey.</p>
<p>I've chosen (read: learned to choose) Mate, because it is one of the environments that looks pretty nice but doesn't require 3D acceleration enabled which you just don't have in cloud environments (at least not in the general compute instances).</p>
<p>If you're ok with XCFE, the folks at AWS have created a <a href="https://aws.amazon.com/premiumsupport/knowledge-center/connect-to-linux-desktop-from-windows/">manual</a> for that (on which this post partially is based on).</p>
<p>First, create a AWS EC2 instance of your choice, enable inbound Port 3389 (RDP) in its security group settings and SSH into it.</p>
<p>Enabling Port 3389:
<img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/securitygroupindescription.jpg" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/securitydetails.jpg" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/securityrdp.jpg" alt="" /></p>
<p>After that, install the latest updates:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt-get</span> udpate
<span class="token function">sudo</span> <span class="token function">apt-get</span> upgrade
<span class="token function">sudo</span> <span class="token function">apt-get</span> dist-ugprade</code></pre>
<p>Next, enable password authentication for <code>sshd</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/ssh/sshd_config</code></pre>
<p>Find the line <code>PasswordAuthentication no</code> and change it to <code>PasswordAuthentication yes</code></p>
<p>Save the <code>sshd_config</code> and restart <code>sshd</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> /etc/init.d/ssh restart</code></pre>
<p>Next, create a password for your default <code>ubuntu</code> user:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token parameter variable">-i</span>
<span class="token function">passwd</span> ubuntu</code></pre>
<p>Then, create a user which you want to be your Desktop user, I'll use <code>awsgui</code> here:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">useradd</span> <span class="token parameter variable">-m</span> awsgui
<span class="token function">sudo</span> <span class="token function">passwd</span> awsgui
<span class="token function">sudo</span> <span class="token function">usermod</span> <span class="token parameter variable">-aG</span> admin awsgui
<span class="token function">sudo</span> <span class="token function">usermod</span> <span class="token parameter variable">-aG</span> <span class="token function">sudo</span> awsgui
<span class="token function">su</span> - awsgui</code></pre>
<p>After that, install the Ubuntu Mate desktop and <code>xrdp</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">DEBIAN_FRONTEND</span><span class="token operator">=</span>noninteractive
<span class="token function">sudo</span> apt-add-repository ppa:ubuntu-mate-dev/ppa
<span class="token function">sudo</span> apt-add-repository ppa:ubuntu-mate-dev/trusty-mate
<span class="token function">sudo</span> <span class="token function">apt-get</span> update <span class="token operator">&&</span> <span class="token function">sudo</span> <span class="token function">apt-get</span> upgrade
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> --no-install-recommends ubuntu-mate-core ubuntu-mate-desktop
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> mate-core mate-desktop-environment mate-notification-daemon
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> xrdp</code></pre>
<p>Now, make Mate the default desktop environment for <code>xrdp</code> sessions (also for new users being created afterwards):</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">echo</span> mate-session<span class="token operator">></span> ~/.xsession
<span class="token function">sudo</span> <span class="token function">cp</span> /home/awsgui/.xsession /etc/skel</code></pre>
<p>Finally, we'll allow changing the host post port we can connect to (this allows reconnecting to an abandoned session):</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/xrdp/xrdp.ini</code></pre>
<p>Change <code>port=-1</code> in the <code>[xrdp1]</code> section to <code>port=ask-1</code>.</p>
<p>With the changes done, restart <code>xrdp</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">service</span> xrdp restart</code></pre>
<p>Now we're able to RDP into our AWS EC2 instance from Windows:</p>
<p>First, enter the public DNS entry (later on you'll assign an Elastic IP, of course) into the Windows Remote Desktop Client:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/rdp-login.png" alt="" /></p>
<p>Next you should get the <code>xrdp</code> login screen were you enter your <code>awsgui</code> credentials and make sure <code>sesman-Xvnc</code> module is selected:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/xrdp-login.jpg" alt="" /></p>
<p>If everything went well, you should get this connection log:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/connectionlog.png" alt="" /></p>
<p>After a second or two, you should also get the Ubuntu Mate desktop screen:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/matedesktop.jpg" alt="" /></p>
<p>If you think we're done now: no.</p>
<p>Lets consider, we installed an EC2 instance with an SSD, that volume is mount to <code>/mnt</code>.</p>
<p>To make use of it, we need to <code>chown</code> it for the <code>awsgui</code> user - open a Terminal window by hitting <code><Ctrl><Alt><T></code> in the Mate environment. Then run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chown</span> awsgui /mnt</code></pre>
<p>Now, we can make use of <code>/mnt</code>.</p>
<h2>Running software in the Mate environment</h2>
<p>Here are some tips for running software in that environment.</p>
<h3>Ubuntu Software Center</h3>
<p>First of all, Ubuntu Software Center doesn't work due to some configuration errors I didn't get behind.<br />
So if you have some nice <code>.deb</code> packages like Dropbox, just install them via Terminal:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> dpkg <span class="token parameter variable">-i</span> <span class="token operator"><</span>package<span class="token operator">></span>.deb</code></pre>
<h3>Changing the timezone</h3>
<p>If you want to change the timezone of your instance, you can't unlock and edit the setting in the UI.</p>
<p>Thus, you have to run <code>sudo dpkg-reconfigure tzdata</code> in Terminal and select the appropriate setting.</p>
<h3>Dropbox</h3>
<p>If you plan to install Dropbox, just download the <code>.deb</code> file and install it as described before.<br />
You can start Dropbox using <code>start dropbox -i</code> to install the daemon.</p>
<p>After the installation you might see a weird or even no notification icon.<br />
Just restart your EC2 instance and everything should be ok.</p>
<h3>Restart / Shutdown via Mate</h3>
<p>Speaking of restarting: You might notice that using the Mate logout / shutdown / restart feature won't end your RDP session.
Just use <code>sudo reboot</code> or <code>sudo shutdown now -h</code> in Terminal.</p>
<h3>Docker on <code>/mnt</code></h3>
<p>If you're running Docker and want to use the aforementioned volume <code>/mnt</code> (because otherwise it might eat up your small boot volume) for the images and volumes, just change your Docker config:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/default/docker</code></pre>
<p>Add this line or change / extend your <code>DOCKER_OPTS</code> entry as follows:</p>
<pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">DOCKER_OPTS</span><span class="token operator">=</span><span class="token string">"-g /mnt/.docker"</span></code></pre>
<p>Before you restart your Docker service, make sure to create the folder:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> /mnt/.docker</code></pre>
<p>Now restart Docker by running <code>sudo service docker restart</code>.</p>
<p><code>docker pull node:4.2.3</code> should pull Node 4.2.3 and put it to <code>/mnt/.docker</code>:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-ubuntu-mate-desktop-as-developer-environment-on-aws-ec2/dockeronmnt.jpg" alt="" /></p>
<h3>Visual Studio Code</h3>
<p>It just doesn't work in <code>xrdp</code> environments as of now - even with the latest Insider build installed.</p>
<p>I guess working with the environment will show some more caveats and if I get them solved, I'll keep the solutions posted.</p>
<p>I also would be happy if you share further tips in the comments.</p>
Debugging mocha tests in a Docker container using Visual Studio Code2016-04-10T00:00:00Zhttps://alexanderzeitler.com/articles/debugging-mocha-tests-in-a-docker-container-using-visual-studio-code/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Debugging a mocha tests in a Docker container using Visual Studio Code" />
<meta name="twitter:description" content="Your tests run in a Docker container - how to debug them?" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/debugging-mocha-tests-in-a-docker-container-using-visual-studio-code/debugmochaindocker_twitter.png" />
<p>In a current Node.js / Docker customer project I had to debug some mocha tests because Docker containers use UTC and you know: that timezone stuff...</p>
<p>In the end, I was debugging my mocha tests using WebStorm and node-inspector inside the containers and got stuff running.</p>
<p>As I have been playing around with Visual Studio Code recently and <a href="https://alexanderzeitler.com/articles/debugging-a-nodejs-es6-application-in-a-docker-container-using-visual-studio-code/">debugging Node.js in Docker containers</a> as well as <a href="https://alexanderzeitler.com/articles/debugging-mocha-tests-using-visual-studio-code-and-nvm/">debugging mocha tests</a> (both using VS Code), this thought came up: "how would I be able to debug mocha tests using Visual Studio Code in Docker containers?"</p>
<p>In the end, it worked out to be pretty staight forwarded.</p>
<p>Make sure to have ES6 enabled in <code>jsconfig.json</code> for the sample code:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"compilerOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"target"</span><span class="token operator">:</span> <span class="token string">"ES6"</span><span class="token punctuation">,</span>
<span class="token property">"module"</span><span class="token operator">:</span> <span class="token string">"commonjs"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>First, I have a little express application inside <code>./index.js</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'hello world'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This application isn't really required for our tests but my aforementioned scenario was like it. Furthermore we need a process that prevents our container from exiting after he has been started, so we'll stick with that little app.</p>
<p>Next, we have a <code>Dockerfile</code>:</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> node:4.2.3</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 3000</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 5858</span>
<span class="token instruction"><span class="token keyword">COPY</span> . /app</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token instruction"><span class="token keyword">RUN</span> cd /app; npm install</span>
<span class="token instruction"><span class="token keyword">CMD</span> [<span class="token string">"node"</span>,<span class="token string">"index.js"</span>]</span></code></pre>
<p>As this post is about debugging mocha tests, here's our little test (<code>./test/test.js</code>) we want to debug inside the container:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> assert <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'assert'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'Truth'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should be told'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">done</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
assert<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token function">done</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>The idea of how I want to debug this test inside a Docker container is as follows:</p>
<ol>
<li>Start the container</li>
<li>Run mocha inside the container with <code>--debug-brk</code> at port <code>5858</code> to wait for Visual Studio Code to attach to the debugger</li>
<li>Attach Visual Studio code and debug the test</li>
</ol>
<p>There is another viable method:</p>
<ol>
<li>Don't use our app in <code>index.js</code></li>
<li>Call mocha with <code>--debug-brk</code> and port <code>5858</code> in the <code>Dockerfile</code> <code>CMD</code></li>
<li>Start the container</li>
<li>Attach Visual Studio code and debug the test</li>
</ol>
<p>As I don't want to have a container dedicated for debugging of mocha tests, I prefer the first version.</p>
<p>Starting the container consists of building the image and running the container. This can be easily automated using <code>nodemon</code>, <code>grunt-watch</code> or something similar. You could even use a Docker Volume and not restart the container at all.</p>
<p>Whatever automation you choose, here are the commands to build and run the container:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">docker</span> build <span class="token parameter variable">-t</span> vscodemocha <span class="token builtin class-name">.</span>
$ <span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">--name</span> vscodemocha <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 <span class="token parameter variable">-p</span> <span class="token number">5858</span>:5858 vscodemocha </code></pre>
<p>With our container being up and running, we can configure Visual Studio Code to run mocha inside the container.</p>
<p>The Visual Studio Code <code>attach</code> Configuration allows to provide a <code>preLaunchTask</code> which is a task that can be defined inside the tasks configuration in <code>tasks.json</code>.</p>
<p>So we'll simply create a <code>runmochaindocker</code> task based on the <code>bash</code> command in <code>tasks.json</code> (inside the <code>.vscode</code> folder):</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"0.1.0"</span><span class="token punctuation">,</span>
<span class="token property">"isShellCommand"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token property">"command"</span><span class="token operator">:</span> <span class="token string">"bash"</span><span class="token punctuation">,</span>
<span class="token property">"args"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"-c"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"tasks"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"taskName"</span><span class="token operator">:</span> <span class="token string">"runmochaindocker"</span><span class="token punctuation">,</span>
<span class="token property">"showOutput"</span><span class="token operator">:</span> <span class="token string">"always"</span><span class="token punctuation">,</span>
<span class="token property">"suppressTaskName"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token property">"args"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"docker exec -i vscodemocha bash -c \"./node_modules/mocha/bin/mocha --debug-brk=5858 -t 10000 test/test.js\""</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>Having that done, we're now referencing that task in the <code>attach</code> configuration in <code>launch.json</code>:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"0.2.0"</span><span class="token punctuation">,</span>
<span class="token property">"configurations"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Attach to Mocha in Docker"</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"node"</span><span class="token punctuation">,</span>
<span class="token property">"request"</span><span class="token operator">:</span> <span class="token string">"attach"</span><span class="token punctuation">,</span>
<span class="token property">"port"</span><span class="token operator">:</span> <span class="token number">5858</span><span class="token punctuation">,</span>
<span class="token property">"preLaunchTask"</span><span class="token operator">:</span> <span class="token string">"runmochaindocker"</span><span class="token punctuation">,</span>
<span class="token property">"address"</span><span class="token operator">:</span> <span class="token string">"localhost"</span><span class="token punctuation">,</span>
<span class="token property">"restart"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"sourceMaps"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"outDir"</span><span class="token operator">:</span> <span class="token null keyword">null</span><span class="token punctuation">,</span>
<span class="token property">"localRoot"</span><span class="token operator">:</span> <span class="token string">"${workspaceRoot}/"</span><span class="token punctuation">,</span>
<span class="token property">"remoteRoot"</span><span class="token operator">:</span> <span class="token string">"/app/"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>As already said, <code>runmochaindocker</code> now is defined as a <code>preLaunchTask</code>. And as already described in <a href="http://localhost:4000/articles/debugging-a-nodejs-es6-application-in-a-docker-container-using-visual-studio-code/">my recent post on debugging a Node.js app in a Docker container</a>, I set <code>localRoot</code> and <code>remoteRoot</code> accordingly.</p>
<p>Guess what? We're done!</p>
<p>Just hit F5 now, and you'll get this:</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-mocha-tests-in-a-docker-container-using-visual-studio-code/runmochaindocker.png" alt="" /></p>
<p>Then, hit F5 again:</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-mocha-tests-in-a-docker-container-using-visual-studio-code/debugmocha.png" alt="" /></p>
<p>(To be honest, I'm not sure why the debugger breaks here - but we can ignore that).</p>
<p>Hit F5 one last time, and we're there: debugging our mocha test in a Docker container:</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-mocha-tests-in-a-docker-container-using-visual-studio-code/debugmochaindocker.png" alt="" /></p>
<p>Happy debugging!</p>
Debugging mocha tests with Visual Studio Code when Node.js is installed using nvm2016-04-09T15:00:00Zhttps://alexanderzeitler.com/articles/debugging-mocha-tests-using-visual-studio-code-and-nvm/<p>In my development environments, I never install Node.j using <code>apt-get</code> but use <a href="https://github.com/creationix/nvm">nvm</a> instead to be able to use multiple Node.js versions in parallel.</p>
<p>To be able to debug mocha tests using Visual Studio Code, I'm using this <code>launch.json</code>:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"0.2.0"</span><span class="token punctuation">,</span>
<span class="token property">"configurations"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Debug Mocha tests"</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"node"</span><span class="token punctuation">,</span>
<span class="token property">"request"</span><span class="token operator">:</span> <span class="token string">"launch"</span><span class="token punctuation">,</span>
<span class="token property">"program"</span><span class="token operator">:</span> <span class="token string">"${workspaceRoot}/node_modules/mocha/bin/_mocha"</span><span class="token punctuation">,</span>
<span class="token property">"stopOnEntry"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"args"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"-t"</span><span class="token punctuation">,</span> <span class="token string">"10000"</span><span class="token punctuation">,</span> <span class="token string">"test/test.js"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"cwd"</span><span class="token operator">:</span> <span class="token string">"${workspaceRoot}"</span><span class="token punctuation">,</span>
<span class="token property">"preLaunchTask"</span><span class="token operator">:</span> <span class="token null keyword">null</span><span class="token punctuation">,</span>
<span class="token property">"runtimeExecutable"</span><span class="token operator">:</span> <span class="token string">"/home/alex/.nvm/versions/node/v4.2.3/bin/node"</span><span class="token punctuation">,</span>
<span class="token property">"runtimeArgs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"--nolazy"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"env"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"NODE_ENV"</span><span class="token operator">:</span> <span class="token string">"development"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"externalConsole"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"sourceMaps"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"outDir"</span><span class="token operator">:</span> <span class="token null keyword">null</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>The important part is the <code>runtimeExecutable</code> which points Visual Studio Code to the Node.js version I've installed using <code>nvm</code>.</p>
<p>If you don't use this setting, you'll get <code>Cannot launch target (reason: spawn node ENOENT).</code>:</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-mocha-tests-using-visual-studio-code-and-nvm/error.png" alt="" /></p>
<p>Also make sure to change the <code>args</code> to change the mocha timeout in order to avoid timeout errors in the debug console.</p>
<p>With that being done, hit F5 and debug your tests:</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-mocha-tests-using-visual-studio-code-and-nvm/debuggingmochatests.png" alt="" /></p>
<p>As promised, no timeout errors:</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-mocha-tests-using-visual-studio-code-and-nvm/debuggingmochatestsconsoleoutput.png" alt="" /></p>
Debugging a ES6 Node.js application in a Docker container using Visual Studio Code2016-04-08T21:45:00Zhttps://alexanderzeitler.com/articles/debugging-a-nodejs-es6-application-in-a-docker-container-using-visual-studio-code/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Debugging a ES6 Node.js application in a Docker container using Visual Studio Code" />
<meta name="twitter:description" content="Curious how to debug a Node.js application in a Docker Container? Look no further!" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/debugging-a-nodejs-es6-application-in-a-docker-container-using-visual-studio-code/runattachactivebreakpoint_twitter1.png" />
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Yesterday I just tried to debug an ES6 Node.js application using Visual Studio Code and noticed that I couldn't remote debug.</p>
<p>So I just asked on Twitter and got some replies from Erich Gamma and Andre Weinand today:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/alexzeitler_">@alexzeitler_</a> <a href="https://twitter.com/code">@code</a> pls see the remote debugging section here <a href="https://t.co/XoMlvRis5Z">https://t.co/XoMlvRis5Z</a> // <a href="https://twitter.com/weinand">@weinand</a></p>— ErichGamma (@ErichGamma) <a href="https://twitter.com/ErichGamma/status/718426030424395776">April 8, 2016</a></blockquote>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/alexzeitler_">@alexzeitler_</a> <a href="https://twitter.com/ErichGamma">@ErichGamma</a> <a href="https://twitter.com/code">@code</a> Sorry, but some changes from the release notes have not yet found their way into the doc.</p>— Andre Weinand (@weinand) <a href="https://twitter.com/weinand/status/718449866016497664">April 8, 2016</a></blockquote>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/ErichGamma">@erichgamma</a> <a href="https://twitter.com/alexzeitler_">@alexzeitler_</a> <a href="https://twitter.com/code">@code</a> and here the section about node.js remote debugging: <a href="https://t.co/TbzNwjKX7y">https://t.co/TbzNwjKX7y</a></p>— Andre Weinand (@weinand) <a href="https://twitter.com/weinand/status/718464941418815490">April 8, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>As things didn't make it into official docs until now, I'll wrap it up here in a block post.</p>
<p>To keep things simple, this is our Node.js application:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'hello world'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Add a <code>jsconfig.json</code> into the root of the project:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"compilerOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"target"</span><span class="token operator">:</span> <span class="token string">"ES6"</span><span class="token punctuation">,</span>
<span class="token property">"module"</span><span class="token operator">:</span> <span class="token string">"commonjs"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Create a <code>Dockerfile</code>:</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> node:4.2.3</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 3000</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 5858</span>
<span class="token instruction"><span class="token keyword">COPY</span> . /app</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token instruction"><span class="token keyword">RUN</span> cd /app; npm install</span>
<span class="token instruction"><span class="token keyword">CMD</span> [<span class="token string">"node"</span>, <span class="token string">"--debug=5858"</span>,<span class="token string">"index.js"</span>]</span></code></pre>
<p>Create a <code>launch.json</code> (or create it by clicking the <code>Debug</code> button in VS Code):</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"0.2.0"</span><span class="token punctuation">,</span>
<span class="token property">"configurations"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Launch"</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"node"</span><span class="token punctuation">,</span>
<span class="token property">"request"</span><span class="token operator">:</span> <span class="token string">"launch"</span><span class="token punctuation">,</span>
<span class="token property">"program"</span><span class="token operator">:</span> <span class="token string">"${workspaceRoot}/index.js"</span><span class="token punctuation">,</span>
<span class="token property">"stopOnEntry"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"args"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"cwd"</span><span class="token operator">:</span> <span class="token string">"${workspaceRoot}"</span><span class="token punctuation">,</span>
<span class="token property">"preLaunchTask"</span><span class="token operator">:</span> <span class="token null keyword">null</span><span class="token punctuation">,</span>
<span class="token property">"runtimeExecutable"</span><span class="token operator">:</span> <span class="token null keyword">null</span><span class="token punctuation">,</span>
<span class="token property">"runtimeArgs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"--nolazy"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"env"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"NODE_ENV"</span><span class="token operator">:</span> <span class="token string">"development"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"externalConsole"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"sourceMaps"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"outDir"</span><span class="token operator">:</span> <span class="token null keyword">null</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Attach"</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"node"</span><span class="token punctuation">,</span>
<span class="token property">"request"</span><span class="token operator">:</span> <span class="token string">"attach"</span><span class="token punctuation">,</span>
<span class="token property">"port"</span><span class="token operator">:</span> <span class="token number">5858</span><span class="token punctuation">,</span>
<span class="token property">"address"</span><span class="token operator">:</span> <span class="token string">"localhost"</span><span class="token punctuation">,</span>
<span class="token property">"restart"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"sourceMaps"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token property">"outDir"</span><span class="token operator">:</span> <span class="token null keyword">null</span><span class="token punctuation">,</span>
<span class="token property">"localRoot"</span><span class="token operator">:</span> <span class="token string">"${workspaceRoot}/"</span><span class="token punctuation">,</span>
<span class="token property">"remoteRoot"</span><span class="token operator">:</span> <span class="token string">"/app/"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>The important part in the <code>launch.json</code> is the <code>Attach</code> configuration, especially the <code>localRoot</code> and <code>remoteRoot</code> properties:</p>
<p>As our application on the host is in <code>/index.js</code>, <code>${workspaceRoot}/</code> is the correct setting.</p>
<p>As you can see in the <code>Dockerfile</code>, inside the container our app runs in the <code>/app</code> working directory.</p>
<p>So I've set the <code>remoteRoot</code> to <code>/app/</code></p>
<p>Next, we can build our Docker image:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">docker</span> build <span class="token parameter variable">-t</span> hellovscode <span class="token builtin class-name">.</span></code></pre>
<p>With that finished, we can run a container based on the image with port mappings for <code>3000</code> and <code>5858</code> activated:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 <span class="token parameter variable">-p</span> <span class="token number">5858</span>:5858 hellovscode</code></pre>
<p>Next start the <code>Attach</code> Debug configuration in VS Code:</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-a-nodejs-es6-application-in-a-docker-container-using-visual-studio-code/runattach.png" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/debugging-a-nodejs-es6-application-in-a-docker-container-using-visual-studio-code/runattachactive.png" alt="" /></p>
<p>As you can see, I have already set a breakpoint.</p>
<p>Now, issue a request to <code>http://localhost:3000/</code> - and we're remote debugging Node.js / ES6 in Docker Container using Visual Studio Code!</p>
<p><img src="https://alexanderzeitler.com/articles/debugging-a-nodejs-es6-application-in-a-docker-container-using-visual-studio-code/runattachactivebreakpoint.png" alt="" /></p>
<p>If you're using Docker-Machine, make sure you're using the IP address of the Docker Machine your container are running in.</p>
<p>You can get the IP address of the Docker Machine using:</p>
<pre class="language-bash"><code class="language-bash">$ docker-machine <span class="token function">ip</span> <span class="token operator"><</span>machinename<span class="token operator">></span></code></pre>
<p>Use this IP address in the "address" property of your VS Code Attach configuration in the <code>launch.json</code>.</p>
<p>The source code including the Visual Studio Code Workspace Settings can be found <a href="https://github.com/PDMLab/vscode-debug-es6-node-docker-sample">here</a></p>
Deploying a private, secured Docker Registry within 15 minutes2016-04-08T18:45:00Zhttps://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Deploying a private, secured Docker Registry within 15 minutes" />
<meta name="twitter:description" content="Need a private Docker registry with RBAC, GUI, AD/LDAP support and more?" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/harborprojectrepository_twitter.png" />
<p>After using Docker in <a href="https://alexanderzeitler.com/articles/docker-machine-and-docker-compose-developer-workflows">development</a>, you might want to put your application into production. Depending on your need, you might not want to push your private Docker images for your application to the Docker Hub, but have a private registry instead.</p>
<p>Docker has been providing the private registry on GitHub for a long time and it works pretty well.
The downside is that it doesn't provide security features like RBAC.</p>
<p>That's where 3rd party solutions kick in.</p>
<p><a href="https://pdmlab.com/">We</a>'ve been evaluating several solutions like <a href="https://aws.amazon.com/ecr/">AWS EC2 Container Registry (ECR)</a> and others.<br />
In the end, we're now running on <a href="https://github.com/vmware/harbor">Harbor</a>, an Open Source registry server from VMware build around the aforementioned Docker registry.</p>
<p>Harbor is being developed in Golang and provides these key features:</p>
<ul>
<li>Access Control: RBAC (Role Based Access Control) is provided. User management can be integrated with existing enterprise identity services like AD/LDAP.</li>
<li>Audit: All access to the registry are logged and can be used for audit purpose.</li>
<li>GUI: User friendly single-pane-of-glass management console.</li>
<li>Project based approach to separate images and access to them</li>
</ul>
<p>First, here are some screenshots from harbor, starting with the admin dashboard:
<img src="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/harboradmindashboard.png" alt="" /></p>
<p>Beside user self registration (can be turned off), the Admin can invite users:
<img src="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/harboradminuserinvite.png" alt="" /></p>
<p>Admins can create projects:
<img src="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/harboradminaddproject.png" alt="" /></p>
<p>You can see all images for a particular project:
<img src="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/harborprojectrepository.png" alt="" /></p>
<p>The <a href="https://github.com/vmware/harbor/blob/master/docs/user_guide.md">Harbor User Guide</a> shows some more screenshots.</p>
<p>The goal of this post is to get a Harbor instance running on <a href="https://www.digitalocean.com/">DigitalOcean</a> with HTTPS enabled using Docker Machine.</p>
<p>I'm assuming that you're familiar with <a href="https://docs.docker.com/machine/">Docker Machine</a> basics and have it up and running.
Furthermore, you'll need to have the <a href="https://docs.docker.com/engine/">Docker engine</a> and <a href="https://docs.docker.com/compose/">Docker Compose</a> installed which are required by Harbor for deployment.<br />
And because we want to enable HTTPS, you should have grabbed a certificate from <a href="https://www.digicert.com/">DigiCert</a> or somewhere else.</p>
<p>First, clone the latest Harbor version from GitHub:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">git</span> clone git@github.com:vmware/harbor.git</code></pre>
<p>Inside the <code>Deploy</code> folder there are several configuration options within the <code>harbor.cfg</code> file.</p>
<p>There are two options you have to configure at a minimum:</p>
<pre class="language-text"><code class="language-text">hostname = harbor.yourdomain.com
ui_url_protocol = https</code></pre>
<p>Inside the <code>Deploy/config/nginx</code> folder there are two nginx configuration files named <code>nginx.conf</code> and <code>nginx.https.conf</code>.</p>
<p>The file being used by Harbor is <code>nginx.conf</code>. Because we want to use HTTPS, we need to get Harbor use <code>nginx.https.conf</code>:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">mv</span> nginx.conf nginx.conf.bak
$ <span class="token function">cp</span> nginx.https.conf nginx.conf</code></pre>
<p>Then we have to modify the new <code>nginx.conf</code> and replace the two occurrences of <code>harbordomain.com</code> with our own Harbor URI, e.g. <code>harbor.yourdomain.com</code>.</p>
<p>Regarding the certificate, there a two options: Use the cert filename from DigiCert (in my case) and the <code>server.key</code> file and modify the <code>nginx.conf</code> accordingly or rename the certificate to <code>harbordomain.crt</code>.</p>
<p>Either case, you have to copy the cert file and the key file from your Certificate Signing Request to the <code>Deploy/config/nginx/cert</code> folder.</p>
<p>The SSL settings within the <code>nginx.conf</code> should look like this:</p>
<pre class="language-text"><code class="language-text"># SSL
ssl_certificate /etc/nginx/cert/harbor_yourdomain_com.pem;
ssl_certificate_key /etc/nginx/cert/harbor_yourdomain_com.key;</code></pre>
<p>Next, from the bash, run the following command within the <code>Deploy</code> folder:</p>
<pre class="language-bash"><code class="language-bash">$ ./prepare</code></pre>
<p>This will copy the settings to their appropriate destinations.</p>
<p>With that done, lets create our Docker Machine instance for deployment.
To create a DigitalOcean Droplet using Docker Machine, we need an API access token which can be created at the DigitalOcean Website (Azure or AWS are similar):</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/digitaloceantokens.png" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/digitaloceangeneratetoken.png" alt="" /></p>
<p>Copy your token and keep it safe, you won't see it again at the DigitalOcean website.</p>
<p>Now, back to the bash, we can create a Droplet using Docker Machine - make sure it has at least 2GB of RAM:</p>
<pre class="language-bash"><code class="language-bash">$ docker-machine create <span class="token parameter variable">--driver</span> digitalocean --digitalocean-access-token <span class="token operator"><</span>yourtokenfromabove<span class="token operator">></span> --digitalocean-size 2GB harbor.yourdomain.com</code></pre>
<p>Docker Machine provides more options for the <code>--driver</code> like the region. Just read the <a href="https://docs.docker.com/machine/drivers/">Docker Machine Driver Documentation</a> for more.</p>
<p>When the Droplet is up and running (this should take only a few seconds), activate that particular Docker Machine instance:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">eval</span> <span class="token variable"><span class="token variable">$(</span>docker-machine <span class="token function">env</span> harbor.yourdomain.com<span class="token variable">)</span></span></code></pre>
<p>Because we want to spin up the containers required by Harbor using Docker Compose, we have to copy the settings from the <code>Deploy/config</code> directory first to our Docker Machine instance.</p>
<p>First, get the absolute path to the <code>Deploy</code> directory (<code>cd</code> first into it):</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">echo</span> <span class="token environment constant">$PWD</span></code></pre>
<p>The result should be something like this:</p>
<pre class="language-text"><code class="language-text">/home/<yourusername>/src/harbor/Deploy</yourusername></code></pre>
<p>Now we create the exact same absolute folder structure (including the <code>config</code> subfolder) inside the Docker Machine instance and copy the contents of our local <code>Deploy/config</code> folder into it:</p>
<pre class="language-bash"><code class="language-bash">$ docker-machine <span class="token function">ssh</span> harbor.ỳourdomain.com 'mkdir <span class="token parameter variable">-p</span> /home/<span class="token operator"><</span>yourusername<span class="token operator">></span>/src/harbor/Deploy/config
$ docker-machine <span class="token function">scp</span> <span class="token parameter variable">-r</span> ./config harbor.yourdomain.com:<span class="token environment constant">$PWD</span></code></pre>
<p>Next, build the container images using Docker Compose (inside the <code>Deploy</code> folder):</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">docker-compose</span> build</code></pre>
<p>And as a last step, we're spinning up our containers:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">docker-compose</span> up <span class="token parameter variable">-d</span></code></pre>
<p>Now you should be able to browse <code>https://harbor.yourdomain.com</code>:</p>
<p><img src="https://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/harborupandrunning.png" alt="" /></p>
<p>And of course, you can start pushing and pulling Docker images to and from your own registry now!</p>
<p>After creating a new project (I'm using <code>test</code> here) and user as an Admin inside Harbor, you can push new images to Harbor like this:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">docker</span> tag customerservice:1.0.0 harbor.yourdomain.com/test/customerservice:1.0.0
$ <span class="token function">docker</span> login <span class="token parameter variable">-u</span> <span class="token operator"><</span>username<span class="token operator">></span> <span class="token parameter variable">-p</span> <span class="token operator"><</span>password<span class="token operator">></span> <span class="token parameter variable">-e</span> <span class="token operator"><</span>email<span class="token operator">></span> harbor.yourdomain.com
$ <span class="token function">docker</span> push harbor.yourdomain.com/test/customerservice:1.0.0</code></pre>
<p>And we're done!</p>
<p>As a side note: If you want to disable user self registration, apply this setting in <code>harbor.cfg</code>:</p>
<pre class="language-text"><code class="language-text">self_registration = off</code></pre>
<p>P.S.: This post mostly is a combination of the <a href="https://github.com/vmware/harbor/blob/master/docs/installation_guide.md">Harbor Installation Guide</a> and the <a href="https://github.com/vmware/harbor/blob/master/contrib/deploying_using_docker_machine.md">Deploying Harbor using Docker Machine Guide</a> (the latter I contributed to the Harbor project, so I didn't just copy existing stuff).</p>
Getting all registered routes in express.js2016-04-06T21:45:00Zhttps://alexanderzeitler.com/articles/getting-all-registered-routes-in-express-js/<p>To get all routes registered in express.js, this snippet gets the work done:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span>_router<span class="token punctuation">.</span>stack<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">middleware</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>middleware<span class="token punctuation">.</span>route<span class="token punctuation">)</span> <span class="token punctuation">{</span>
routes<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>
<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>middleware<span class="token punctuation">.</span>route<span class="token punctuation">.</span>methods<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> -> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>middleware<span class="token punctuation">.</span>route<span class="token punctuation">.</span>path<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>routes<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Possible result:</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>
<span class="token string">"post -> /customers"</span>
<span class="token string">"put -> /customers/:id"</span>,
<span class="token string">"delete -> /customers/:id"</span>
<span class="token punctuation">]</span></code></pre>
Debugging Node.js + TypeScript in a Docker Container with WebStorm2016-04-06T11:00:00Zhttps://alexanderzeitler.com/articles/debugging-node-js-typescript-in-a-docker-container-with-webstorm/<p>In order to be able to debug Node.js with TypeScript in a Docker container (or in general: debug remote) using WebStorm, <code>compilerOptions</code> in your <code>tsconfig.json</code> need to contain <code>inlineSources</code> and <code>inlineSourceMap</code> being set to <code>true</code>.</p>
<p>So your <code>tsconfig.json</code> might look like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"compilerOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"outDir"</span><span class="token operator">:</span> <span class="token string">"build"</span><span class="token punctuation">,</span>
<span class="token property">"inlineSources"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token property">"inlineSourceMap"</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"include"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"src/**/*"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"exclude"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"**/*.spec.ts"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
Some thoughts on Open Source Software2016-02-18T14:00:00Zhttps://alexanderzeitler.com/articles/some-thoughts-on-open-source-software/<blockquote>
<p>TL;DR There's no alternative to OSS but we need to improve how we're dealing with it.</p>
</blockquote>
<p>Hannes Preishuber recently wrote a <a href="http://blog.ppedv.de/post/2016/02/10/Werte-schatzen-Werte-schopfen.aspx">blog post</a> (in german language) about appreciation of work as well as quality and documentation of software nowadays.</p>
<p>While I agree with Hannes that someone's work has to be appreciated, I disagree with his assumptions that OSS is responsible for bad software quality and service / documentation in general.</p>
<p>Today a huge amount of the cloud infrastructure and services run on OSS and companies like MongoDb have build a business around their OSS products.</p>
<p>If quality and service wouldn't be warranted here, that business won't work.</p>
<p>On the other hand, from a developer perspective, OSS can become frustrating anyway:<br />
Independently from the platform, be it Node.js / JavaScript or .NET, new OSS projects are born, gain huge adoption - and die.</p>
<p>The problem here is not that these projects die in general because there are several reasons for that.<br />
One reason is, the community has decided to choose another OSS project as state of the art.<br />
Another one is, the core maintainer has decided to no longer maintain the project and makes an official statement about that.<br />
Some maintainers also look for a successor who maintains the project in the future so the project doesn't die at all.</p>
<p>The real problem in OSS are those projects which start, grow over time and then die slowly - or from an adopters perspective: they become zombies.
Typical indications for a project dying slowly are unmerged pull requests, unanswered and unresolved issues and of course no further commits to at least update the projects own depedencies.</p>
<p>As said in the introduction, there are no (or only few alternatives) to OSS. Even typical enterprise developers use OSS these days.<br />
All of our customers use OSS in some way and frameworks like Angular will push this trend forward.</p>
<p>When advising customers about using external dependencies (not only OSS) in their code base, we tell them to look for indicators of OSS zombie projects and have countermeasures on hand if a depedency is no longer maintained.</p>
<p>On the other hand, I came to the conclusion that OSS projects, especially the many smaller ones need to be handled in another way opposed to how they're handled nowadays.</p>
<p>If you look at Cassandra, Node.js or Microsoft .NET as OSS projects, they're all under the hood of a foundation which supports these projects. These foundations like Apache Foudation, Linux Foundation, or .NET Foundation provide guidance, mentoring as well as legal, technical and fincancial support.</p>
<p>But I think we should have another form of support or foundation in order to avoid zombie projects and respect criticism from people like Hannes.</p>
<p>After thinking about it for a while and talking to some fellows about it, here are my thoughts:</p>
<p>In general we should distinguish between the "ownership" of the project and the copyright / license for the code.<br />
If you decide to start a new OSS project on GitHub, GitLab, BitBucket or whereever, beside selecting a license for your project, you should be able to transfer the ownership of the project to sort of a "registry".</p>
<p>This registry should provide these features:</p>
<p>The initiator of the project becomes the "general manager" of the project and can work on the project as we all used to work on OSS as before.<br />
But in addition to that, the registry provides the following features:</p>
<ul>
<li>A general manager can resign itself if he doesn't want to maintain to project any longer. The project then is in a pool of projects "looking for new general manager". Other people can apply for that role and take over projects in a governed and transparent way. Community could even vote the next general manager if more persons apply as general manager for a particular project.</li>
<li>A general manager can be resigned by the registry which might sound frightening at a first sight. But I think there are several advantages. For example, if a core maintainer doesn't even respond to pull requests or issues in some manner in a reasonable time, he might get warnings from the registry. After a specific number of warnings he could be resigned by the registry and the project gets in the pool as described above.</li>
<li>Projects can have proxies. If the general manager is temporarily not able to maintain the project, the proxy can maintain the project during that period.</li>
</ul>
<p>Some might argue that this is way to much overhead but I'm sure I'm not the first who ran into zombie projects and the bigger your codebase is or if you're maintaining a huge number of critical projects for your customers, you don't want to kill the living deads in your code every now and then. And your customers should not have to pay for this problem.</p>
<p>Please let me know your thoughts about in the comments.</p>
Resizing a VMware Workstation VM partition using GParted - get the swap partition out of my way!2016-02-04T21:00:00Zhttps://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/<blockquote>
<p>TL;DR Hard Disk space can only be replaced with more Hard Disk space</p>
</blockquote>
<p>Disclaimer: If you're doing things wrong described here you'll loose your data - create a backup first!</p>
<p>Nowadays it's casual to use VMs for development environments. It's also casual that projects grow and you run out of disk space inside the VMs. Luckily, on the host there's always enough space available which can be assigned to an existing VM (isn't it?).</p>
<p>Expanding the partition in the VMware Hardware options / hard disk details is pretty easy. Just stop the VM, click the expand button and increase as required:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/vmwareexpand.PNG" alt="" /></p>
<p>Now we have some more space for partitions in our VM but we want to increase the size of an existing partition.
To manage the partitions, we're using <a href="http://gparted.org/">GParted</a>, which is available as bootable ISO-Image.</p>
<p>Assign the downloaded ISO in your VMware CD-ROM drive and boot from it.
You can select the boot device if you hit ESC right after powering on the VM.</p>
<p>If the boot sequence is to fast, you can add this line to your VMs <code>.vmx</code> file:</p>
<pre class="language-shell"><code class="language-shell">bios.bootDelay <span class="token operator">=</span> <span class="token string">"2000"</span></code></pre>
<p>This will delay the boot sequence for 2000 ms and show the options.</p>
<p>If you're running Ubuntu in your VM, it is likely that there might be a swap partition (<code>/dev/sda5</code> here) between the main partition and the aforementioned created, unallocated new space and your partitions might look like this:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted1.PNG" alt="" /></p>
<p>What we want to achieve now, is to merge the 10 GB unallocated space at the end of the list to the <code>/dev/sda1</code> partition.</p>
<p>First, we'll select <code>/dev/sda2</code> (NOT <code>/dev/sda5</code>!):</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted2.PNG" alt="" /></p>
<p>Then, we click "Resize/Move":</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted3.PNG" alt="" /></p>
<p>Next, we increase the size of <code>/dev/sda2</code> to use all available space:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted4.PNG" alt="" /></p>
<p>Now we select <code>/dev/sda5</code> and click <code>Resize/Move</code> again:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted5.PNG" alt="" /></p>
<p>In this step, we move <code>/dev/sda5</code> inside <code>/dev/sda2</code> to right end:
<img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted6.PNG" alt="" /></p>
<p>Then again, we select <code>/dev/sda2</code>:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted7.PNG" alt="" /></p>
<p>After clicking <code>Resize/Move</code> now we shrink <code>/dev/sda2</code> to the size of <code>/dev/sda5</code> by moving the left arrow to the right until it is blocked:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted8.PNG" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted9.PNG" alt="" /></p>
<p>Now we select <code>/dev/sda1</code> again:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted10.PNG" alt="" /></p>
<p>After clicking "Resize/Move" again, we finally can expand <code>/dev/sda1</code> to the right:</p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted11.PNG" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/resizing-a-vmware-workstation-partition-using-gparted/gparted12.PNG" alt="" /></p>
<p>The final step is hitting the "Apply" button, confirm everything, cross fingers and wait for the operations to succeed.</p>
<p>As GParted already tells you in almost every step: Be careful with what you're doing and create a backup before you're trying to modify your partitions!</p>
Workflows: using Docker Machine and Docker Compose together in development2016-02-01T01:00:00Zhttps://alexanderzeitler.com/articles/docker-machine-and-docker-compose-developer-workflows/<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@alexzeitler_" />
<meta name="twitter:creator" content="@alexzeitler_" />
<meta name="twitter:title" content="Using Docker Machine and Docker Compose together in development" />
<meta name="twitter:description" content="Ever used Docker Machine and Compose together? Read about it here!" />
<meta name="twitter:image" content="https://alexanderzeitler.com/articles/docker-machine-and-docker-compose-developer-workflows/docker-machine-and-compose-twitter.png" />
<p><a href="https://pdmlab.com/">We</a>'re using the Docker eco system now for a while in development and production.<br />
As you may know, the Docker eco system not only consists of the Docker engine (client and server), but it also provides <a href="https://docs.docker.com/machine/">Docker Machine</a> and <a href="https://docs.docker.com/compose/">Docker Compose</a>.<br />
While Docker Compose makes linking containers much easier, Docker Machine provides an abstraction for your environment where your Docker engine is being executed in.
Thus, Machine allows you to manage your Docker engine Host in a consistent way no matter whether you're on AWS, Azure or on premise - or even your local dev environment.</p>
<p>Docker Machine and Docker Compose work together pretty well in production, but when using them in development, you might experience some issues - some of which I'll describe in this post and show solutions for it.</p>
<p>This post is not about using Docker, Docker Machine or Compose in production!</p>
<h1>Plain Docker development workflow with Node.js / Express</h1>
<p>First, some basics. Let's consider we're composing an Docker application using a MongoDb database and we're implementing a Node.js application that uses MongoDb.</p>
<p>A basic Docker workflow without Machine and Compose would look like this:</p>
<p>Create a <code>Dockerfile</code> for our Node.js application:</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> node:4.2.3</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 3000</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 5858</span>
<span class="token instruction"><span class="token keyword">COPY</span> . /app</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token instruction"><span class="token keyword">RUN</span> cd /app; npm install</span>
<span class="token instruction"><span class="token keyword">CMD</span> [<span class="token string">"node"</span>, <span class="token string">"app.js"</span>]</span></code></pre>
<p>Create our simplified Node.js appliation in <code>app.js</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>express<span class="token punctuation">.</span><span class="token function">static</span><span class="token punctuation">(</span><span class="token string">'public'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Place an <code>index.html</code> inside the <code>public</code> folder:</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
Hello
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Now we can build our image like this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> my-node-app <span class="token builtin class-name">.</span></code></pre>
<p>Then we can run our container based on this image:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 my-node-app</code></pre>
<p>Pointing our browser to <code>http://localhost:3000/</code> will return this result:</p>
<p><img src="https://alexanderzeitler.com/articles/docker-machine-and-docker-compose-developer-workflows/localhost3000static.png" alt="" /></p>
<p>If we're changing something in our <code>index.html</code>, we have to stop and remove our container, rebuild the image and start the container again:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> stop my-node-app-container
<span class="token function">docker</span> <span class="token function">rm</span> my-node-app-container
<span class="token function">docker</span> build <span class="token parameter variable">-t</span> my-node-app <span class="token builtin class-name">.</span>
<span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 my-node-app</code></pre>
<p>Also this happens quite fast, we can get it even faster by mounting our local project folder as a volume for our container:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> my-node-app <span class="token builtin class-name">.</span>
<span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-v</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>:/app <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 my-node-app</code></pre>
<p>By adding the <code>-v $(pwd):/app</code> parameter to our <code>docker run</code> command, we're mapping the local project folder into the <code>/app</code> folder in our container.</p>
<p>Now we can modify the content in below our <code>public</code> folder and just hit F5 in our browser to get the latest changes.</p>
<p>Let's get a bit further and add an API to our application and change the <code>app.js</code> as follows:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>express<span class="token punctuation">.</span><span class="token function">static</span><span class="token punctuation">(</span><span class="token string">'public'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/hello'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'world'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>After this, we can run HTTPIe (or curl / wget) <code>http get localhost:3000/hello</code> and receive an output like this:</p>
<pre class="language-bash"><code class="language-bash">HTTP/1.1 <span class="token number">200</span> OK
Connection: keep-alive
Content-Length: <span class="token number">5</span>
Content-Type: text/html<span class="token punctuation">;</span> <span class="token assign-left variable">charset</span><span class="token operator">=</span>utf-8
Date: Sat, <span class="token number">23</span> Jan <span class="token number">2016</span> <span class="token number">20</span>:55:07 GMT
ETag: W/<span class="token string">"5-fXkwN6B2AYZXSwKC8vQ15w"</span>
X-Powered-By: Express
world</code></pre>
<p>Changing something in the API and running HTTPIe won't result in updated output.<br />
This is because express needs to be initialized again to run the app with the changes made.</p>
<p>We have two options to re-initialize the container: restart the container or kill it and run it again.</p>
<p>Restarting the container is a single command: <code>docker restart my-node-app-container</code>.<br />
Also this command is pretty simple, it may take a few seconds to gracefully stop and start the container.</p>
<p>The second option is a bit faster and looks like this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> <span class="token function">kill</span> my-node-app-container
<span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-v</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>:/app <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 my-node-app</code></pre>
<h1>Plain Docker workflow with Node.js and MongoDb</h1>
<p>As said earlier in this post, our goal is to have an application that runs on Node.js and uses MongoDb.<br />
Adding MongoDb to our game is as simple as this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>some-mongo <span class="token parameter variable">-p</span> <span class="token number">27017</span>:27017 mongo</code></pre>
<p>In order to connect our Node.js App container with the MongoDb container, we have to run it using:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-v</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>:/app <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 <span class="token parameter variable">--link</span> some-mongo:mongo my-node-app</code></pre>
<p>Now let's change our Node.js application to use MongoDb and write the request body of a <code>POST</code> to <code>/hello</code> to the <code>documents</code> colleciton in MongoDb:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> bodyparser <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'body-parser'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> MongoClient <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongodb'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>MongoClient<span class="token punctuation">;</span>
<span class="token keyword">var</span> url <span class="token operator">=</span> <span class="token string">'mongodb://'</span> <span class="token operator">+</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">MONGO_PORT_27017_TCP_ADDR</span> <span class="token operator">+</span> <span class="token string">':27017/dockerdemo'</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> db<span class="token punctuation">;</span>
MongoClient<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> database</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Connected correctly to server"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
db <span class="token operator">=</span> database<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>bodyparser<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>express<span class="token punctuation">.</span><span class="token function">static</span><span class="token punctuation">(</span><span class="token string">'public'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> <span class="token function-variable function">insertDocument</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">db<span class="token punctuation">,</span> document<span class="token punctuation">,</span> callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// Get the documents collection</span>
<span class="token keyword">var</span> collection <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">collection</span><span class="token punctuation">(</span><span class="token string">'documents'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Insert some documents</span>
collection<span class="token punctuation">.</span><span class="token function">insertOne</span><span class="token punctuation">(</span>document<span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">callback</span><span class="token punctuation">(</span>err<span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span>ops<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/hello'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">var</span> data <span class="token operator">=</span> req<span class="token punctuation">.</span>body<span class="token punctuation">;</span>
<span class="token function">insertDocument</span><span class="token punctuation">(</span>db<span class="token punctuation">,</span> data<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">201</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/hello'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'world'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Sending a request using to our API like this <code>http post http://localhost:3000/hello name=PDMLab</code> will result in a successful response:</p>
<pre class="language-bash"><code class="language-bash">HTTP/1.1 <span class="token number">201</span> Created
Connection: keep-alive
Content-Length: <span class="token number">50</span>
Content-Type: application/json<span class="token punctuation">;</span> <span class="token assign-left variable">charset</span><span class="token operator">=</span>utf-8
Date: Wed, <span class="token number">27</span> Jan <span class="token number">2016</span> <span class="token number">21</span>:54:21 GMT
ETag: W/<span class="token string">"32-YLqScoop9RrYa0x7JH+FIg"</span>
X-Powered-By: Express
<span class="token punctuation">{</span>
<span class="token string">"_id"</span><span class="token builtin class-name">:</span> <span class="token string">"56a93c8d51a0630100b28294"</span>,
<span class="token string">"name"</span><span class="token builtin class-name">:</span> <span class="token string">"PDMLab"</span>
<span class="token punctuation">}</span></code></pre>
<p>Using <code>nodemon</code> we can <code>kill</code> and <code>start</code> the containers as shown before and we have a nice development workflow.</p>
<h1>Simplifying container linking using Docker Compose</h1>
<p>Now lets go one step further and add Docker Compose to handle the container linking.</p>
<p>Using Compose, you don't link containers using the Docker CLI, but instead you create a file named <code>docker-compose.yml</code> (you can name it as you like but the Compose CLI uses this by default).</p>
<p>The Compose file for our application looks like this:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">mongo</span><span class="token punctuation">:</span>
<span class="token key atrule">image</span><span class="token punctuation">:</span> mongo<span class="token punctuation">:</span>2.6.11
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">"27017:27017"</span>
<span class="token key atrule">application</span><span class="token punctuation">:</span>
<span class="token key atrule">build</span><span class="token punctuation">:</span> .
<span class="token key atrule">command</span><span class="token punctuation">:</span> node <span class="token punctuation">-</span><span class="token punctuation">-</span>debug=5858 app.js <span class="token punctuation">-</span><span class="token punctuation">-</span>color=always
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">"3000:3000"</span>
<span class="token punctuation">-</span> <span class="token string">"5858:5858"</span>
<span class="token key atrule">volumes</span> <span class="token punctuation">:</span>
<span class="token punctuation">-</span> ./<span class="token punctuation">:</span>/app
<span class="token key atrule">links</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> mongo</code></pre>
<p>When issuing the <code>http POST</code> as shown above, we'll get a similar result.</p>
<p>Refreshing our <code>index.html</code> without restarting the containers still works. This happens because we're mapping the current folder to the <code>/app</code> folder using inside the <code>volumes</code> section of the <code>appliation</code> container definition.</p>
<p>Restarting the container can be automated using nodemon like this:</p>
<pre class="language-bash"><code class="language-bash">nodemon <span class="token parameter variable">-x</span> <span class="token function">docker-compose</span> up</code></pre>
<p>This will ensure that changes to the API part of our application are applied as shown before.</p>
<h1>Adding Docker Machine to the scene</h1>
<p>Now it's time to introduce Docker Machine and show how it plays together with our existing setup.</p>
<p>We can simply create a new Docker Machine instance named <code>default</code>:</p>
<pre class="language-bash"><code class="language-bash">docker-machine create <span class="token parameter variable">--driver</span><span class="token operator">=</span>virtualbox default</code></pre>
<p>To make sure our Docker client talks to the Docker engine in our <code>default</code> machine, we'll update the environment:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">eval</span> <span class="token variable"><span class="token variable">$(</span>docker-machine <span class="token function">env</span> default<span class="token variable">)</span></span></code></pre>
<p>First, lets try to build and run our first sample from above with Docker Machine:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 my-node-app</code></pre>
<p>Pointing the browser again to <code>http://localhost:3000</code> will fail to access the site.</p>
<p>This happens because port <code>3000</code> is not open in VirtualBox where the VM (our machine named <code>default</code>) is running. To solve this, we can open the port using the VirtualBox port forwading Network setting for the machine or simply run <code>VBoxManage</code> on the console:</p>
<pre class="language-bash"><code class="language-bash">VBoxManage modifyvm <span class="token string">"default"</span> <span class="token parameter variable">--natpf1</span> <span class="token string">"default,tcp,,3000,,3000"</span></code></pre>
<p>Trying to open the website again then will result in the expected <code>Hello World</code> response.</p>
<p>Another option (my preference) to access our application running in the machine, is to use the IP address of the machine. To retrieve it, we call</p>
<pre class="language-bash"><code class="language-bash">docker-machine <span class="token function">ip</span> default</code></pre>
<p>This will result, for example, in <code>192.168.99.100</code> and pointing our browser to <code>http://192.168.99.100:3000/</code> will return the expected response as well.</p>
<h1>Docker Machine and Volumes</h1>
<p>The next thing we tried was the usage of Volumes and we'll now try this with Docker Machine as well.</p>
<p>Our command was this, so lets use it again:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-v</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>:/app <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 my-node-app</code></pre>
<p>The console result will look fine at a first sight:</p>
<pre class="language-bash"><code class="language-bash">098b5a3387df42092bec984105bf9f6725e11088d3f62d72a893f586cb33bc50</code></pre>
<p>But when we try to open the application in our browser, it fails again.</p>
<p>Looking at our list of running containers using <code>docker ps</code> doesn't show our <code>my-node-app-container</code> running.<br />
What happened?</p>
<p>Let's dig a bit deeper using <code>docker logs my-node-app-container</code>.</p>
<p>The result will look like this:</p>
<pre class="language-bash"><code class="language-bash">module.js:339
throw err<span class="token punctuation">;</span>
^
Error: Cannot <span class="token function">find</span> module <span class="token string">'/app/app.js'</span>
at Function.Module._resolveFilename <span class="token punctuation">(</span>module.js:337:15<span class="token punctuation">)</span>
at Function.Module._load <span class="token punctuation">(</span>module.js:287:25<span class="token punctuation">)</span>
at Function.Module.runMain <span class="token punctuation">(</span>module.js:467:10<span class="token punctuation">)</span>
at startup <span class="token punctuation">(</span>node.js:136:18<span class="token punctuation">)</span>
at node.js:963:3</code></pre>
<p>The reason for this is that we're telling the Docker engine to map our local directory to the <code>/app</code> folder inside the container. The problem here: our local folder doesn't exist in the Docker Machine <code>default</code> thats running inside VirtualBox.</p>
<p>Docker Machine provides a solution for this. We can use the <code>ssh</code> command provided by Docker Machine to create the "local" folder inside the machine and then use the Docker Machine <code>scp</code> command to copy the files from our host into that folder inside the machine:</p>
<pre class="language-bash"><code class="language-bash">docker-machine <span class="token function">ssh</span> default <span class="token function">mkdir</span> <span class="token parameter variable">-p</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>
docker-machine <span class="token function">scp</span> <span class="token parameter variable">-r</span> <span class="token builtin class-name">.</span> default:<span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span></code></pre>
<p>Copying the files has to be done every time our source code changes. Because the scp command is a all or nothing operation, I was looking for another solution that copies only the file changed. One option would be Grunt or Gulp to get the particular file change but I'm not a friend of these tools.</p>
<p>A wide spread tool is <code>rsync</code> which exactly does what we want: copy all changed files.</p>
<p>The good part of the story: in the end it works. The bad part: There's "a little" work to do.</p>
<p>First, to make sure all further <code>rsync</code> commands work as expected, we need to get the SSH key to be used by rsync.</p>
<p>This is done by the following commands:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">eval</span> <span class="token variable"><span class="token variable">$(</span>ssh-agent<span class="token variable">)</span></span>
ssh-add ~/.docker/machine/machines/default/id_rsa</code></pre>
<p>Next, we need to know that the OS running inside Docker Machine does NOT provide an <code>rsync</code> installation out of the box.
To the resuce, boot2docker (based on Tiny Core Linux), the OS running inside Docker Machine, comes with an package manager named <code>tce</code> and using the following command, we can installed <code>rsync</code> inside our Docker Machine:</p>
<pre class="language-bash"><code class="language-bash">docker-machine <span class="token function">ssh</span> default -- tce-load <span class="token parameter variable">-wi</span> <span class="token function">rsync</span></code></pre>
<p>Next, we have to create the directory with the same path as our local path in our Docker Machine and then sync our local directory to the Docker Machine:</p>
<pre class="language-bash"><code class="language-bash">docker-machine <span class="token function">ssh</span> default <span class="token function">mkdir</span> <span class="token parameter variable">-p</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>
<span class="token function">rsync</span> <span class="token parameter variable">-avzhe</span> <span class="token function">ssh</span> <span class="token parameter variable">--relative</span> --omit-dir-times <span class="token parameter variable">--progress</span> ./ docker@<span class="token variable"><span class="token variable">$(</span>docker-machine <span class="token function">ip</span> default<span class="token variable">)</span></span><span class="token builtin class-name">:</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span></code></pre>
<p>After this, we'll kill our <code>my-node-app-container</code> and restart it:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> <span class="token function">kill</span> my-node-app-container
<span class="token function">docker</span> start my-node-app-container</code></pre>
<p>In the end, you'll put the <code>rsync</code> command and restarting of the container into a <code>.sh</code> file and call this from <code>nodemon</code>.</p>
<p>Running this container together with the MongoDb container works similar to the sample shown in the section without MongoDb but of course with the <code>rsync</code> stuff applied.</p>
<h1>Docker Machine and Docker Compose together</h1>
<p>The last part of this post will show how to run your containers in Docker Machine and get the composition done by Docker Compose.</p>
<p>Our <code>docker-compose.yml</code> file looks exactly the same as before:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">mongo</span><span class="token punctuation">:</span>
<span class="token key atrule">image</span><span class="token punctuation">:</span> mongo<span class="token punctuation">:</span>2.6.11
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">"27017:27017"</span>
<span class="token key atrule">application</span><span class="token punctuation">:</span>
<span class="token key atrule">build</span><span class="token punctuation">:</span> .
<span class="token key atrule">command</span><span class="token punctuation">:</span> node <span class="token punctuation">-</span><span class="token punctuation">-</span>debug=5858 app.js <span class="token punctuation">-</span><span class="token punctuation">-</span>color=always
<span class="token key atrule">ports</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">"3000:3000"</span>
<span class="token punctuation">-</span> <span class="token string">"5858:5858"</span>
<span class="token key atrule">volumes</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> ./<span class="token punctuation">:</span>/app
<span class="token key atrule">links</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> mongo</code></pre>
<p>In order to get Docker Machine and Compose work together and have a smooth development workflow, you'll have to add the <code>docker-compose up</code> command to your aforementioned <code>.sh</code> script and restart this by <code>nodemon</code> like this:</p>
<pre class="language-bash"><code class="language-bash">nodemon <span class="token parameter variable">-x</span> syncandrestartcontainers.sh</code></pre>
<p>Finally, <code>syncandrestartcontainers.sh</code> will look like this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">eval</span> <span class="token variable"><span class="token variable">$(</span>ssh-agent<span class="token variable">)</span></span>
ssh-add ~/.docker/machine/machines/default/id_rsa
<span class="token function">rsync</span> <span class="token parameter variable">-avzhe</span> <span class="token function">ssh</span> <span class="token parameter variable">--relative</span> --omit-dir-times <span class="token parameter variable">--progress</span> ./ docker@<span class="token variable"><span class="token variable">$(</span>docker-machine <span class="token function">ip</span> default<span class="token variable">)</span></span><span class="token builtin class-name">:</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>
<span class="token function">docker-compose</span> up</code></pre>
<p>Some tips for the praxis when using this workflow:</p>
<p>Before starting this workflow</p>
<ul>
<li>stop your Docker Machine and restart it using <code>docker-machine restart default</code> (don't forget <code>eval $(docker-machine env default)</code> after it)</li>
<li>delete the "local" directory inside the Docker Machine using <code>docker-machine ssh default sudo -i "sudo rm -rf $(pwd)"</code></li>
<li>create the "local" directory inside the Docker Machine using <code>docker-machine ssh default mkdir -p $(pwd)</code></li>
</ul>
<h3>Update 2016, 3rd February: One more thing... Docker networking</h3>
<p>As suggested in the comments, this update of the post shows you how to use Docker networking which has been introduced in Docker 1.9.<br />
You can read the basics about Docker network <a href="https://docs.docker.com/engine/userguide/networking/">here</a> - please read them first and then continue here...</p>
<p>Welcome back ;-)</p>
<p>First, lets create a custom bridged network named <code>my-app-network</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> network create <span class="token parameter variable">--driver</span><span class="token operator">=</span>bridge my-app-network</code></pre>
<p>Next, we'll start our MongoDb container connected to the network:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>some-mongo <span class="token parameter variable">--net</span><span class="token operator">=</span>my-app-network <span class="token parameter variable">-p</span> <span class="token number">27017</span>:27017 mongo:2.6.11</code></pre>
<p>As Docker networking doesn't use environment variables to share connection settings but the container names instead, we need to change the MongoDb connection string in our <code>app.js</code> accordingly:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> url <span class="token operator">=</span> <span class="token string">'mongodb://some-mongo:27017/dockerdemo'</span><span class="token punctuation">;</span></code></pre>
<p>Then, rebuild our image:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> my-node-app <span class="token builtin class-name">.</span></code></pre>
<p>And finally, run our app container connected to the network as well:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-it</span> <span class="token parameter variable">--name</span><span class="token operator">=</span>my-node-app-container <span class="token parameter variable">-v</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">pwd</span><span class="token variable">)</span></span>:/app <span class="token parameter variable">-p</span> <span class="token number">3000</span>:3000 <span class="token parameter variable">--net</span><span class="token operator">=</span>my-app-network my-node-app</code></pre>
<p>Issuing a <code>http post http://localhost:3000/hello name=PDMLab</code> again works fine as before.</p>
<p>Docker networking also works fine with Docker Machine, just make sure you follow the steps (rsync etc.) shown above.</p>
<p>Docker networking in Docker Compose is experimental at the moment so I won't show that now.</p>
<p>End of Update 2016, 3rd February</p>
<p>Happy developing using Docker Machine and Compose ;-)</p>
<p>P.S.: If you came up with other solutions, please let me know in the comments.</p>
Chrome crashes on Ubuntu in VMware Workstation2016-01-29T17:30:00Zhttps://alexanderzeitler.com/articles/chrome-crashing-in-ubuntu-vmware-workstation/<p>If you're using Chrome on Ubuntu in a VMware Workstation VM it is likely, that it has been crashing after being in the background for a while. Sometimes it also kills the VM entirely.</p>
<p>For me the solution was to disable hardware accelartion completely - the performance isn't that bad after all.</p>
<p>Go to Chrome settings, then show advanced settings and disable hardware acceleration:</p>
<p><img src="https://alexanderzeitler.com/articles/chrome-crashing-in-ubuntu-vmware-workstation/disablehardwareaccelerationinchrome.png" alt="Disabling hardware acceleration in Chrome" /></p>
Rename Visual Studio project including files, folders and namespaces2016-01-29T17:00:00Zhttps://alexanderzeitler.com/articles/rename-visual-studio-project-namespaces-and-folders-automate-everything/<p>TL;DR</p>
<blockquote>
<p>It's 2016 and Visual Studio doesn't support us in renaming Projects including files, folders and namespaces.</p>
</blockquote>
<p>Considering you have a Visual Studio solution named <code>PDMLab.AwesomeProject.WithAnUglyTypo</code> and this solution contains multiple projects all starting with <code>PDMLab.AwesomeProject.WithAnUglyTypo</code>. The projects also may contain default namespace names starting using the solution name and the assemblies might be named accordingly.</p>
<p>One solution is to use Notepad++ to replace every occurrence of <code>PDMLab.AwesomeProject.WithAnUglyTypo</code> and rename the folders by hand or another tool.<br />
Well, this works but I'm more a console user...</p>
<p>That said, the solution shown below asumes you have git bash installed.</p>
<p>Replacing the contents like namespaces in the files:</p>
<pre class="language-shell"><code class="language-shell"><span class="token comment"># run this in the root folder of your project</span>
<span class="token function">find</span> <span class="token builtin class-name">.</span> <span class="token parameter variable">-name</span> <span class="token string">"*.*"</span> <span class="token parameter variable">-print0</span> <span class="token operator">|</span> <span class="token function">xargs</span> <span class="token parameter variable">-0</span> <span class="token function">sed</span> <span class="token parameter variable">-i</span> <span class="token string">''</span> <span class="token parameter variable">-e</span> <span class="token string">'s/PDMLab\.AwesomeProject\.WithAnUglyTypo/PDMLab.AwesomeProject.WithoutAnUglyTypo/g'</span></code></pre>
<p>Renaming files and folders:</p>
<pre class="language-shell"><code class="language-shell"><span class="token comment"># run this in the root folder of your project</span>
<span class="token keyword">for</span> <span class="token for-or-select variable">i</span> <span class="token keyword">in</span> <span class="token variable"><span class="token variable">$(</span><span class="token function">find</span> * <span class="token parameter variable">-maxdepth</span> <span class="token number">1</span><span class="token variable">)</span></span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token function">mv</span> <span class="token variable">$i</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> $i <span class="token operator">|</span> <span class="token function">sed</span> <span class="token string">'s/PDMLab\.AwesomeProject\.WithAnUglyTypo/PDMLab.AwesomeProject.WithoutAnUglyTypo/'</span><span class="token variable">)</span></span><span class="token punctuation">;</span> <span class="token keyword">done</span></code></pre>
<p>The advantage of this solution: You can create an alias for both commands that expects the old and new name as an param and you're done.</p>
<p>Make sure you have a backup of your files before you try that - I'm not responsible if something goes wrong...</p>
<p>Happy renaming :)</p>
Docker Machine: NS_ERROR_FAILURE Failed to create the host-only adapter2016-01-16T17:00:00Zhttps://alexanderzeitler.com/articles/docker-machine-ns-error-failure-failed-to-create-the-host-only-adapter/<p>If you try to start a docker machine using <code>docker-machine start <machinename></code>, you might receive this error message:</p>
<pre class="language-bash"><code class="language-bash">Progress state: NS_ERROR_FAILURE
VBoxManage: error: Failed to create the host-only adapter
VBoxManage: error: VBoxNetAdpCtl: Error <span class="token keyword">while</span> adding new interface: failed to <span class="token function">open</span> /dev/vboxnetctl: No such <span class="token function">file</span> or directory
VBoxManage: error: Details: code NS_ERROR_FAILURE <span class="token punctuation">(</span>0x80004005<span class="token punctuation">)</span>, component HostNetworkInterface, interface IHostNetworkInterface
VBoxManage: error: Context: <span class="token string">"int handleCreate(HandlerArg*, int, int*)"</span> at line <span class="token number">66</span> of <span class="token function">file</span> VBoxManageHostonly.cpp</code></pre>
<p>To resolve this issue, you have to setup the <code>vboxdrv</code> kernel module using</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> /etc/init.d/vboxdrv setup</code></pre>
A lap around AWS and docker-machine2015-08-16T21:00:00Zhttps://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/<p>This post will show you how can create a Docker Machine instance on AWS (EC2) starting from scratch. This means you're starting with your free AWS account with nothing configured after you have signed up for your free AWS trial account.</p>
<p>To create a Docker machine instance in AWS, the <code>docker-machine</code> command requires several <a href="https://docs.docker.com/machine/drivers/aws/">params for the AWS driver</a>.</p>
<p>At a minimum, we need to provide these params:</p>
<ul>
<li>amazonec2-access-key</li>
<li>amazonec2-secret-key</li>
<li>amazonec2-region</li>
<li>amazonec2-zone</li>
<li>amazonec2-vpc-id</li>
</ul>
<p>To get the <code>amazonec2-access-key</code> and the <code>amazonec2-secret-key</code> we need to have an Amazon EC2 Access Key and an Amazon EC2 Secret Key.</p>
<p>Both of them can be obtained by <a href="https://console.aws.amazon.com/iam/home?#users">creating a user in IAM</a>.</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/iamempty.png" alt="" /></p>
<p>Clicking on the "Create New Users" button will bring up this view where we create a new user named "awsdockeruser":</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/awsdockeruser.png" alt="" /></p>
<p>Make sure to check "Generate an access key for each user".</p>
<p>After creating the user, we'll get both keys:</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/awsiamkeys.png" alt="" /></p>
<p>Make sure to create a copy at safe place as this is the last time you'll see them in IAM.</p>
<p>In order to manage AWS EC2 instances we need the appropriate permissions.
To assign the permission to manage EC2 instances our <code>awsdockeruser</code> needs to be a member of a group which has that permission.</p>
<p>So lets create that group in the IAM dashboard:</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/awsiamgroups.png" alt="" /></p>
<p>We'll called it "awsdockergroup"</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/awsdockergroup.png" alt="" /></p>
<p>Next, assign the policy to manage EC2 instances (there might be lower privileges that are sufficient):</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/ec2fullaccess.png" alt="" /></p>
<p>Next, lets add the "awsdockeruser" to our group:</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/awsdockergroupdetails.png" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/awsdockeruserinawsdockergroup.png" alt="" /></p>
<p>Next, we need to know region, zone and VPC id.
These can be obtained by using the AWS CLI.</p>
<p>On Linux and OS X, it can be installed using <a href="https://pip.pypa.io/en/stable/installing.html">PIP</a> package manager.</p>
<pre class="language-bash"><code class="language-bash">pip <span class="token function">install</span> awscli</code></pre>
<p>Then AWS CLI needs to be configured:</p>
<pre class="language-bash"><code class="language-bash">aws configure</code></pre>
<p>AWS CLI configurations asks you for your <code>AWS Access Key ID</code>, your <code>AWS Secret Access Key</code> (remember them? 😀), <code>Default region name</code> (<a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html">codes can be found here</a> - I've choosen <code>eu-central-1</code>), and <code>Default output format</code> which I set to <code>json</code>.</p>
<p>To get the vpc-id, just run:</p>
<pre class="language-bash"><code class="language-bash">aws ec2 describe-subnets</code></pre>
<p>The output will look like this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">{</span>
<span class="token string">"Subnets"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token string">"VpcId"</span><span class="token builtin class-name">:</span> <span class="token string">"<avpcid>"</span>,
<span class="token string">"CidrBlock"</span><span class="token builtin class-name">:</span> <span class="token string">"172.31.0.0/20"</span>,
<span class="token string">"MapPublicIpOnLaunch"</span><span class="token builtin class-name">:</span> true,
<span class="token string">"DefaultForAz"</span><span class="token builtin class-name">:</span> true,
<span class="token string">"State"</span><span class="token builtin class-name">:</span> <span class="token string">"available"</span>,
<span class="token string">"AvailabilityZone"</span><span class="token builtin class-name">:</span> <span class="token string">"eu-central-1a"</span>,
<span class="token string">"SubnetId"</span><span class="token builtin class-name">:</span> <span class="token string">"<asubnetid>"</span>,
<span class="token string">"AvailableIpAddressCount"</span><span class="token builtin class-name">:</span> <span class="token number">4091</span>
<span class="token punctuation">}</span>,
<span class="token punctuation">{</span>
<span class="token string">"VpcId"</span><span class="token builtin class-name">:</span> <span class="token string">"<anothervpcid>"</span>,
<span class="token string">"CidrBlock"</span><span class="token builtin class-name">:</span> <span class="token string">"172.31.16.0/20"</span>,
<span class="token string">"MapPublicIpOnLaunch"</span><span class="token builtin class-name">:</span> true,
<span class="token string">"DefaultForAz"</span><span class="token builtin class-name">:</span> true,
<span class="token string">"State"</span><span class="token builtin class-name">:</span> <span class="token string">"available"</span>,
<span class="token string">"AvailabilityZone"</span><span class="token builtin class-name">:</span> <span class="token string">"eu-central-1b"</span>,
<span class="token string">"SubnetId"</span><span class="token builtin class-name">:</span> <span class="token string">"<anothersubnetid>"</span>,
<span class="token string">"AvailableIpAddressCount"</span><span class="token builtin class-name">:</span> <span class="token number">4091</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>The <code>amazonec2-zone</code> param is the last character of the <code>AvailabilityZone</code> of the subnet you choose to use, so <code>a</code> or <code>b</code> here.</p>
<p>Ok, it's time to spin up our Docker machine instance...</p>
<pre class="language-bash"><code class="language-bash">docker-machine create <span class="token punctuation">\</span>
<span class="token parameter variable">-d</span> amazonec2 <span class="token punctuation">\</span>
--amazonec2-access-key <span class="token operator"><</span>YOURACCESSKEY<span class="token operator">></span> <span class="token punctuation">\</span>
--amazonec2-secret-key <span class="token operator"><</span>YOURSECRETKEY<span class="token operator">></span> <span class="token punctuation">\</span>
--amazonec2-zone a <span class="token punctuation">\</span>
--amazonec2-region eu-central-1 <span class="token punctuation">\</span>
--amazonec2-vpc-id <span class="token operator"><</span>YOURVPCID<span class="token operator">></span>
awsdocker</code></pre>
<p>After about 60 seconds, your console should confirm your machine is ready to rock'n roll</p>
<pre class="language-bash"><code class="language-bash">Launching instance<span class="token punctuation">..</span>.
To see how to connect Docker to this machine, run: docker-machine <span class="token function">env</span> awsdocker</code></pre>
<p>To connect to the machine, run this command:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">eval</span> <span class="token variable"><span class="token variable">$(</span>docker-machine <span class="token function">env</span> awsdocker<span class="token variable">)</span></span></code></pre>
<p>To make sure everything works as expected, just run <code>docker ps</code>.</p>
<p>If you're working with various machines, you might want to know which is your current active machine:</p>
<p><img src="https://cloud.githubusercontent.com/assets/287480/9203727/129f86ce-4059-11e5-8988-953b6f6fe231.png" alt="" /></p>
<p>You can have this in your prompt if you use my bash prompt definiton from <a href="https://gist.github.com/AlexZeitler/69140578a1bb13483242">here</a>.</p>
<p>Using <code>docker-machine ip awsdocker</code> you can get the public IP address of your machine.</p>
<p>If you're deploying some containers, you might wonder, why you can`t access your containers exposed ports like http://<machine-ip>:<someport>: because firewall 😱.</p>
<p>So head over to the <a href="https://eu-central-1.console.aws.amazon.com/ec2/v2/home?region=eu-central-1#SecurityGroups:sort=groupId">EC2 dashboard</a> "Security Groups" section, select your "docker-machine" Security Group (which has been created when spinning up your machine) and make sure to allow some inbound traffic:</p>
<p><img src="https://alexanderzeitler.com/articles/a-lap-around-aws-and-docker-machine/vpcsecuritygroup.png" alt="" /></p>
<p>Happy shipping! 😀</p>
Integration-testing ASP.NET 5 / MVC 6 Controllers on DNX Beta 42015-05-16T21:00:00Zhttps://alexanderzeitler.com/articles/Integration-testing-ASP.NET-5-Controllers-with-DNX-Beta4/<p>In ASP.NET 5 bootstrapping a self host test server has changed compared to ASP.NET 4 / Web API.</p>
<p>This post will show you how you can run integration tests using ASP.NET 5 / MVC 6 API Controllers on Beta 4 of DNX.</p>
<p>First, the Controller implementation:</p>
<p>CustomersController.cs:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Route</span><span class="token attribute-arguments"><span class="token punctuation">(</span><span class="token string">"api/[controller]"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CustomersController</span>
<span class="token punctuation">{</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">HttpGet</span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IActionResult</span> <span class="token function">GetAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ObjectResult</span><span class="token punctuation">(</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">List<span class="token punctuation"><</span>Customer<span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">Customer</span>
<span class="token punctuation">{</span>
CompanyName <span class="token operator">=</span> <span class="token string">"PDMLab"</span><span class="token punctuation">,</span>
AddressLine <span class="token operator">=</span> <span class="token string">"Ludwig-Erhard-Allee 10"</span><span class="token punctuation">,</span>
ZipCode <span class="token operator">=</span> <span class="token string">"76131"</span><span class="token punctuation">,</span>
City <span class="token operator">=</span> <span class="token string">"Karlsruhe"</span><span class="token punctuation">,</span>
Website <span class="token operator">=</span> <span class="token string">"https://pdmlab.com"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Our tests will use Xunit, so we'll add Xunit to our <code>project.json</code> file:</p>
<pre class="language-json"><code class="language-json">...
<span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"Microsoft.AspNet.Mvc"</span><span class="token operator">:</span> <span class="token string">"6.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.IIS"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.WebListener"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.StaticFiles"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"xunit"</span><span class="token operator">:</span> <span class="token string">"2.1.0-*"</span><span class="token punctuation">,</span>
<span class="token property">"xunit.runner.dnx"</span><span class="token operator">:</span> <span class="token string">"2.1.0-beta2-build79"</span><span class="token punctuation">,</span>
<span class="token property">"xunit.runner.visualstudio"</span><span class="token operator">:</span> <span class="token string">"2.1.0-beta1-build1051"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
...
</code></pre>
<p>Furthermore, our <code>HttpClient</code> we're using resides in <code>Microsoft.AspNet.WebApi.Client</code> which we'll add also to our <code>project.json</code> file:</p>
<pre class="language-json"><code class="language-json">...
<span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"Microsoft.AspNet.Mvc"</span><span class="token operator">:</span> <span class="token string">"6.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.IIS"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.WebListener"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.StaticFiles"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.WebApi.Client"</span><span class="token operator">:</span> <span class="token string">"5.2.3"</span><span class="token punctuation">,</span>
<span class="token property">"xunit"</span><span class="token operator">:</span> <span class="token string">"2.1.0-*"</span><span class="token punctuation">,</span>
<span class="token property">"xunit.runner.dnx"</span><span class="token operator">:</span> <span class="token string">"2.1.0-beta2-build79"</span><span class="token punctuation">,</span>
<span class="token property">"xunit.runner.visualstudio"</span><span class="token operator">:</span> <span class="token string">"2.1.0-beta1-build1051"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
...
</code></pre>
<p>Finally, we need <code>Kestrel</code> to run our tests on *nix based systems as well as <code>Microsoft.AspNet.Hosting</code> which contains the classes required for bootstrapping a self host server - :</p>
<pre class="language-json"><code class="language-json">...
<span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"Kestrel"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Hosting"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Mvc"</span><span class="token operator">:</span> <span class="token string">"6.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.IIS"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.WebListener"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.StaticFiles"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.WebApi.Client"</span><span class="token operator">:</span> <span class="token string">"5.2.3"</span><span class="token punctuation">,</span>
<span class="token property">"xunit"</span><span class="token operator">:</span> <span class="token string">"2.1.0-*"</span><span class="token punctuation">,</span>
<span class="token property">"xunit.runner.dnx"</span><span class="token operator">:</span> <span class="token string">"2.1.0-beta2-build79"</span><span class="token punctuation">,</span>
<span class="token property">"xunit.runner.visualstudio"</span><span class="token operator">:</span> <span class="token string">"2.1.0-beta1-build1051"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
...
</code></pre>
<p>The final part required is the test itself:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CustomerControllerIntegrationTest</span>
<span class="token punctuation">{</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Fact</span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ShouldReturnCustomer</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> config <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Configuration</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
config<span class="token punctuation">.</span><span class="token function">AddCommandLine</span><span class="token punctuation">(</span><span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token string">"--server.urls"</span><span class="token punctuation">,</span> <span class="token string">"http://localhost:5001"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> serverFactoryLocation <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">IsMono</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
serverFactoryLocation <span class="token operator">=</span> <span class="token string">"Microsoft.AspNet.Server.WebListener"</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
serverFactoryLocation <span class="token operator">=</span> <span class="token string">"Kestrel"</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token class-name"><span class="token keyword">var</span></span> context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">HostingContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
Configuration <span class="token operator">=</span> config<span class="token punctuation">,</span>
ServerFactoryLocation <span class="token operator">=</span> serverFactoryLocation<span class="token punctuation">,</span>
ApplicationName <span class="token operator">=</span> <span class="token string">"AspNet5IntegrationTesting"</span><span class="token punctuation">,</span>
StartupMethods <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">StartupMethods</span><span class="token punctuation">(</span>builder <span class="token operator">=></span> builder<span class="token punctuation">.</span><span class="token function">UseMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> services <span class="token operator">=></span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token function">AddMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> services<span class="token punctuation">.</span><span class="token function">BuildServiceProvider</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">HostingEngine</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Start</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token class-name"><span class="token keyword">var</span></span> client <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">HttpClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name"><span class="token keyword">var</span></span> customers <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">GetAsync</span><span class="token punctuation">(</span><span class="token string">"http://localhost:5001/api/customers"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Result
<span class="token punctuation">.</span>Content<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">ReadAsAsync</span><span class="token generic class-name"><span class="token punctuation"><</span>List<span class="token punctuation"><</span>Customer<span class="token punctuation">></span><span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Result<span class="token punctuation">;</span>
Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span>customers<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>CompanyName<span class="token punctuation">,</span><span class="token string">"PDMLab"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name"><span class="token keyword">bool</span></span> <span class="token function">IsMono</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> Type<span class="token punctuation">.</span><span class="token function">GetType</span><span class="token punctuation">(</span><span class="token string">"Mono.Runtime"</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>The implementation is pretty straight forward:</p>
<p>First we're adding the host and port our test server should listen on to our application configuration.</p>
<p>Then we're checking if we're running on mono and depdending on the result, we're bootstrapping Kestrel oder WebListener.</p>
<p>After that we're bootstrapping a <code>HostingContext</code> instance which configures our StartupMethods (which contains the stuff that resides in your <code>Startup.cs</code> normally), Configuration and the host to use.</p>
<p>Using this context we're firing up a new <code>HostingEngine</code> instance which gets disposedd after the assertions have finished.</p>
<p>That's it... almost, because there's one caveat:</p>
<p>The tests currently fail on Kestrel / *nix with this error:</p>
<pre class="language-bash"><code class="language-bash">xUnit.net DNX <span class="token builtin class-name">test</span> runner <span class="token punctuation">(</span><span class="token number">64</span>-bit DNX <span class="token number">4.5</span>.1<span class="token punctuation">)</span>
Copyright <span class="token punctuation">(</span>C<span class="token punctuation">)</span> <span class="token number">2015</span> Outercurve Foundation.
Discovering: AspNet5IntegrationTesting
Discovered: AspNet5IntegrationTesting
Starting: AspNet5IntegrationTesting
AspNet5IntegrationTesting.Tests.CustomerControllerIntegrationTest.ShouldReturnCustomer <span class="token punctuation">[</span>FAIL<span class="token punctuation">]</span>
System.ArgumentException <span class="token builtin class-name">:</span> GCHandle value belongs to a different domain
Stack Trace:
at System.Runtime.InteropServices.GCHandle.op_Explicit <span class="token punctuation">(</span>IntPtr value<span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at System.Runtime.InteropServices.GCHandle.FromIntPtr <span class="token punctuation">(</span>IntPtr value<span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Server.Kestrel.Networking.UvMemory.DestroyMemory <span class="token punctuation">(</span>IntPtr memory<span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Server.Kestrel.Networking.UvLoopHandle.ReleaseHandle <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at System.Runtime.InteropServices.SafeHandle.RunRelease <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at System.Runtime.InteropServices.SafeHandle.Dispose <span class="token punctuation">(</span>Boolean disposing<span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at System.Runtime.InteropServices.SafeHandle.Dispose <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Server.Kestrel.KestrelThread.ThreadStart <span class="token punctuation">(</span>System.Object parameter<span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Server.Kestrel.KestrelThread.Stop <span class="token punctuation">(</span>TimeSpan <span class="token function">timeout</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Server.Kestrel.KestrelEngine.Dispose <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Kestrel.ServerFactory+<span class="token operator"><></span>c__DisplayClass3_0.<span class="token operator"><</span>Start<span class="token operator">></span>b__1 <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Server.Kestrel.Disposable.Dispose <span class="token punctuation">(</span>Boolean disposing<span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Server.Kestrel.Disposable.Dispose <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Hosting.HostingEngine+<span class="token operator"><></span>c__DisplayClass9_0.<span class="token operator"><</span>Start<span class="token operator">></span>b__1 <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at Microsoft.AspNet.Hosting.HostingEngine+Disposable.Dispose <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at AspNet5IntegrationTesting.Tests.CustomerControllerIntegrationTest.ShouldReturnCustomer <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
at <span class="token punctuation">(</span>wrapper managed-to-native<span class="token punctuation">)</span> System.Reflection.MonoMethod:InternalInvoke <span class="token punctuation">(</span>System.Reflection.MonoMethod,object,object<span class="token punctuation">[</span><span class="token punctuation">]</span>,System.Exception<span class="token operator">&</span><span class="token punctuation">)</span>
at System.Reflection.MonoMethod.Invoke <span class="token punctuation">(</span>System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object<span class="token punctuation">[</span><span class="token punctuation">]</span> parameters, System.Globalization.CultureInfo culture<span class="token punctuation">)</span> <span class="token punctuation">[</span>0x00000<span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token operator"><</span>filename unknown<span class="token operator">></span>:0
Finished: AspNet5IntegrationTesting
<span class="token operator">==</span><span class="token operator">=</span> TEST EXECUTION SUMMARY <span class="token operator">==</span><span class="token operator">=</span>
AspNet5IntegrationTesting Total: <span class="token number">1</span>, Errors: <span class="token number">0</span>, Failed: <span class="token number">1</span>, Skipped: <span class="token number">0</span>, Time: <span class="token number">0</span>.697s
</code></pre>
<p>If somebody has an idea about this, please send me a <a href="https://github.com/AlexZeitler/AspNet5IntegrationTesting">pull request at GitHub</a> (yes, you can grab the source over there) ;-)</p>
<p>Update [2015/05/17]:
This is a known Kestrel issue as described <a href="https://github.com/aspnet/KestrelHttpServer/issues/9">here</a> (<a href="https://github.com/aspnet/KestrelHttpServer/issues/9#issuecomment-67818250">Description starts here</a>) and a fix is on the way.</p>
Deploying a ASP.NET MVC 6 API as Azure API App in Azure App Services2015-05-02T10:00:00Zhttps://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/<h3>Setting some context...</h3>
<p>Disclaimer: this post is based on Visual Studio 2015 RC, DNX beta 4 and Azure SDK 2.6 and might become obsolete (hopefully pretty fast).</p>
<p>At Build 2015 "<a href="https://channel9.msdn.com/Events/Build/2015/C9-16">The Lesser Scotts</a>" have been showing how to create a Azure API App based on ASP.NET Web API 2 using the Azure SDK Tools in Visual Studio 2013 which you should definitely <a href="https://channel9.msdn.com/Events/Build/2015/2-628">watch</a> before reading this post.</p>
<p>Well, deploying stable stuff is pretty neat.<br />
But my first thought of course has been "What about <a href="https://github.com/aspnet/home">ASP.NET MVC 6 on DNX</a> beta 4?".</p>
<p>So I asked the lesser Scotts at Twitter:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/Tweetazeitlertolesserscotts.png" alt="" /></p>
<p>Scotts answer was pretty promising:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/Tweetshanselmantoalexzeitler.png" alt="" /></p>
<p>My next step of course was installing <a href="http://azure.microsoft.com/en-us/downloads/archive-net-downloads/">Azure SDK 2.6 for Visual Studio 2015 RC</a> and trying to create a new Azure API App as shown in the video above.</p>
<p>But there were no templates... so back to Twitter - this time Brady Gaster was my victim:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/Tweetalexzeitlertobradygaster.png" alt="" /></p>
<p>No promising answer but at least some facts :)</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/Tweetbradygastertoalexzeitler.png" alt="" /></p>
<p>Ok, lets do some reverse engineering using Visual Studio 2013 and Azure SDK 2.6 and find out what happens when you're using the SDK tooling - I'll skip this step here and show you, how to get stuff done in Visual Studio 2015 RC and <a href="https://channel9.msdn.com/Events/Build/2015/2-687">ASP.NET MVC 6 on DNX beta 4</a> now.</p>
<h3>First things first, the ASP.NET MVC 6 API</h3>
<p>First, create a new ASP.NET Web Application in Visual Studio 2015 RC:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/NewProject.PNG" alt="" /></p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/NewvNextWebApi.PNG" alt="" /></p>
<p>After this, your solution should look like this:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/NewProjectSolutionExplorer.PNG" alt="" /></p>
<p>As the default <code>ValuesController.cs</code> in the <code>Controllers</code> folder is pretty useless (and it will cause problems as you can read later on), we'll delete it and create a <code>SpeakersController.cs</code> instead.</p>
<p>SpeakersController.cs:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Collections<span class="token punctuation">.</span>Generic</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">HelloApiApps<span class="token punctuation">.</span>Models</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Mvc</span><span class="token punctuation">;</span>
<span class="token keyword">namespace</span> <span class="token namespace">HelloApiApps<span class="token punctuation">.</span>Controllers</span>
<span class="token punctuation">{</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Route</span><span class="token attribute-arguments"><span class="token punctuation">(</span><span class="token string">"api/[controller]"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SpeakersController</span>
<span class="token punctuation">{</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">HttpGet</span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IEnumerable<span class="token punctuation"><</span>Speaker<span class="token punctuation">></span></span> <span class="token function">GetAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">List<span class="token punctuation"><</span>Speaker<span class="token punctuation">></span></span>
<span class="token punctuation">{</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">Speaker</span>
<span class="token punctuation">{</span>
Id <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span>
FirstName <span class="token operator">=</span> <span class="token string">"Scott"</span><span class="token punctuation">,</span>
LastName <span class="token operator">=</span> <span class="token string">"Hanselman"</span><span class="token punctuation">,</span>
Twitter <span class="token operator">=</span> <span class="token string">"shanselman"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">Speaker</span>
<span class="token punctuation">{</span>
Id <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">,</span>
FirstName <span class="token operator">=</span> <span class="token string">"Scott"</span><span class="token punctuation">,</span>
LastName <span class="token operator">=</span> <span class="token string">"Hunter"</span><span class="token punctuation">,</span>
Twitter <span class="token operator">=</span> <span class="token string">"coolcsh"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">Speaker</span>
<span class="token punctuation">{</span>
Id <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span>
FirstName <span class="token operator">=</span> <span class="token string">"Damian"</span><span class="token punctuation">,</span>
LastName <span class="token operator">=</span> <span class="token string">"Edwards"</span><span class="token punctuation">,</span>
Twitter <span class="token operator">=</span> <span class="token string">"DamianEdwards"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>We also create a folder <code>Models</code> and create a <code>Speaker.cs</code> in it:</p>
<p>Speaker.cs:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">namespace</span> <span class="token namespace">HelloApiApps<span class="token punctuation">.</span>Models</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Speaker</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> FirstName <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> LastName <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> Twitter <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>At this time, you should be able to test your API locally at <code>http://localhost:someport/api/speakers</code> and you should get the JSON representation of our speaker list.</p>
<h3>Creating the Swagger API definition file</h3>
<p>According to the Scott's video we now would create the Swagger file, but <a href="http://www.nuget.org/packages/Swashbuckle/">Swashbuckle</a> shown in the video doesn't work with ASP.NET MVC 6 right now.</p>
<p>To the rescue, there's already a work in progress project on GitHub which is porting Swashbuckle to ASP.NET MVC 6: <a href="https://github.com/domaindrivendev/Ahoy">Ahoy</a>!</p>
<p>We create a local clone of Ahoy and for the sake of keeping this post simple, we add the <code>Swashbuckle.Swagger</code> project inside our API Solution as existing project:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/AddSwaggerProject.PNG" alt="" /></p>
<p>Next, we have to register Swashbuckle / Swagger inside our <code>Startup.cs</code></p>
<p>Startup.cs:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">using</span> <span class="token namespace">System</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Collections<span class="token punctuation">.</span>Generic</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Linq</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Threading<span class="token punctuation">.</span>Tasks</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Builder</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Hosting</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Http</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Routing</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>Framework<span class="token punctuation">.</span>DependencyInjection</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Swashbuckle<span class="token punctuation">.</span>Application</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Swashbuckle<span class="token punctuation">.</span>Swagger</span><span class="token punctuation">;</span>
<span class="token keyword">namespace</span> <span class="token namespace">HelloApiApps</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Startup</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">Startup</span><span class="token punctuation">(</span><span class="token class-name">IHostingEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">// This method gets called by a runtime.</span>
<span class="token comment">// Use this method to add services to the container</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ConfigureServices</span><span class="token punctuation">(</span><span class="token class-name">IServiceCollection</span> services<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token function">AddMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers.</span>
<span class="token comment">// You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json.</span>
<span class="token comment">// services.AddWebApiConventions();</span>
services<span class="token punctuation">.</span><span class="token function">AddSwagger</span><span class="token punctuation">(</span>s <span class="token operator">=></span>
<span class="token punctuation">{</span>
s<span class="token punctuation">.</span><span class="token function">SwaggerGenerator</span><span class="token punctuation">(</span>c <span class="token operator">=></span>
<span class="token punctuation">{</span>
c<span class="token punctuation">.</span>Schemes <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token string">"http"</span><span class="token punctuation">,</span> <span class="token string">"https"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
c<span class="token punctuation">.</span><span class="token function">SingleApiVersion</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">Info</span>
<span class="token punctuation">{</span>
Version <span class="token operator">=</span> <span class="token string">"v1"</span><span class="token punctuation">,</span>
Title <span class="token operator">=</span> <span class="token string">"Swashbuckle Sample API"</span><span class="token punctuation">,</span>
Description <span class="token operator">=</span> <span class="token string">"A sample API for testing Swashbuckle"</span><span class="token punctuation">,</span>
TermsOfService <span class="token operator">=</span> <span class="token string">"Some terms ..."</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
s<span class="token punctuation">.</span><span class="token function">SchemaGenerator</span><span class="token punctuation">(</span>opt <span class="token operator">=></span> opt<span class="token punctuation">.</span>DescribeAllEnumsAsStrings <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// Configure is called after ConfigureServices is called.</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Configure</span><span class="token punctuation">(</span><span class="token class-name">IApplicationBuilder</span> app<span class="token punctuation">,</span> <span class="token class-name">IHostingEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment">// Configure the HTTP request pipeline.</span>
app<span class="token punctuation">.</span><span class="token function">UseStaticFiles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Add MVC to the request pipeline.</span>
app<span class="token punctuation">.</span><span class="token function">UseMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Add the following route for porting Web API 2 controllers.</span>
<span class="token comment">// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");</span>
<span class="token comment">// Add MVC to the request pipeline.</span>
app<span class="token punctuation">.</span><span class="token function">UseMvc</span><span class="token punctuation">(</span>routes <span class="token operator">=></span>
<span class="token punctuation">{</span>
routes<span class="token punctuation">.</span><span class="token function">EnableSwagger</span><span class="token punctuation">(</span><span class="token string">"swagger/docs/{apiVersion}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>The default Swagger URL for Swashbuckle has been (as shown in the video): <code>http://somehost:someport/swagger/docs/v1</code>.</p>
<p>By default, with Ahoy this has changed to <code>http://somehost:someport/swagger/v1/swagger.json</code> whereas in both cases <code>v1</code> depends on the <code>Version</code> property being set during Swashbuckle registration in <code>Startup.cs</code> inside the <code>ConfigureServices</code> method in the code shown above.</p>
<p>Thus, we have to change the route definition back to the old format as Azure API Apps expect it in that format.
You can see that inside the <code>Configure</code> method of the <code>Startup.cs</code> in the code shown above.</p>
<p>Next, we can open up our App in a browser again and browse to <code>http://localhost:someport/swagger/docs/v1</code> and we should get the JSON representation.</p>
<h3>Adding Azure API Apps Metadata</h3>
<p>Now we need to add the Metadata for the Azure API Apps manually as we don't have the tooling support right now...</p>
<p>First, add a JSON file named <code>apiapp.json</code> in the root of the API App project:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/ApiAppsJSON.PNG" alt="" /></p>
<p>Then paste this content into it:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"$schema"</span><span class="token operator">:</span> <span class="token string">"http://json-schema.org/schemas/2014-11-01/apiapp.json#"</span><span class="token punctuation">,</span>
<span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"HelloApiApps"</span><span class="token punctuation">,</span>
<span class="token property">"namespace"</span><span class="token operator">:</span> <span class="token string">"microsoft.com"</span><span class="token punctuation">,</span>
<span class="token property">"gateway"</span><span class="token operator">:</span> <span class="token string">"2015-01-14"</span><span class="token punctuation">,</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span><span class="token punctuation">,</span>
<span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"HelloApiApps"</span><span class="token punctuation">,</span>
<span class="token property">"summary"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"author"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"endpoints"</span><span class="token operator">:</span> <span class="token null keyword">null</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Next, add the following folders and files to your <code>wwwroot</code> folder in the API App solution:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/MetadataFolderAndFiles.PNG" alt="" /></p>
<p>The content of the <code>apiDefinition.swagger.json</code> is the output from <code>http://localhost:someport/swagger/docs/v1</code>.
Copy and paste it into the file.</p>
<p>Important Note: Make sure your API Controllers don't contain ambiguous method names (e.g. "Get" twice), this will cause problems at the moment.
Thanks to <a href="https://twitter.com/mohit">@Mohit</a> for <a href="https://social.msdn.microsoft.com/Forums/en-US/621c2d06-51d7-48ff-8a02-d97fdda7b3e3/cannot-get-the-api-definition?forum=AzureAPIApps">sorting that out</a>!</p>
<p>The last file required, is the <code>apiappconfig.azureresource.json</code> and it's content is this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"$schema"</span><span class="token operator">:</span> <span class="token string">"http://schemas.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#"</span><span class="token punctuation">,</span>
<span class="token property">"contentVersion"</span><span class="token operator">:</span> <span class="token string">"1.0.0.0"</span><span class="token punctuation">,</span>
<span class="token property">"parameters"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"$system"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"Object"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"resources"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p><strong><Update [2015/05/02]>:</strong>
Instead of providing a static snapshot of your Swagger API definition, you can also provide an endpoint where the current swagger definition can be read from. This solves two problems: the route can be the new route template Ahoy introduces and your Swagger definition is always up to date when add new API endpoints (Controllers / Methods).</p>
<p>Just update the <code>apiapp.json</code> and add an fill the endpoint property (please note that I'm use the new default Ahoy route here):</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"$schema"</span><span class="token operator">:</span> <span class="token string">"http://json-schema.org/schemas/2014-11-01/apiapp.json#"</span><span class="token punctuation">,</span>
<span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"HelloApiApps"</span><span class="token punctuation">,</span>
<span class="token property">"namespace"</span><span class="token operator">:</span> <span class="token string">"microsoft.com"</span><span class="token punctuation">,</span>
<span class="token property">"gateway"</span><span class="token operator">:</span> <span class="token string">"2015-01-14"</span><span class="token punctuation">,</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span><span class="token punctuation">,</span>
<span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"HelloApiApps"</span><span class="token punctuation">,</span>
<span class="token property">"summary"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"author"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"endpoints"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"apiDefinition"</span><span class="token operator">:</span> <span class="token string">"/swagger/v1/swagger.json"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Kudos go to <a href="https://twitter.com/pkefal">@pkefal</a> for the hint (By the way: if you want to create API Apps using Node.js you should definitely read his <a href="http://azure.microsoft.com/en-us/documentation/articles/app-service-api-nodejs-api-app/">article</a>)</p>
<p><strong></end of update [2015/05/02]></strong></p>
<p>Another step, the Azure SDK tooling does, is adding two NuGet packages and we have to do that also.
Update your <code>project.json</code> and add <code>Microsoft.Azure.AppService.ApiApps.Service</code> and <code>System.IdentityModel.Tokens.Jwt</code>to it.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"webroot"</span><span class="token operator">:</span> <span class="token string">"wwwroot"</span><span class="token punctuation">,</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0-*"</span><span class="token punctuation">,</span>
<span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"Microsoft.AspNet.Mvc"</span><span class="token operator">:</span> <span class="token string">"6.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.IIS"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.WebListener"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.Azure.AppService.ApiApps.Service"</span><span class="token operator">:</span> <span class="token string">"0.9.40"</span><span class="token punctuation">,</span>
<span class="token property">"System.IdentityModel.Tokens.Jwt"</span><span class="token operator">:</span> <span class="token string">"4.0.2.202250711"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.StaticFiles"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Swashbuckle.Swagger"</span><span class="token operator">:</span> <span class="token string">"1.0.0-*"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"commands"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"web"</span><span class="token operator">:</span> <span class="token string">"Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"frameworks"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"dnx451"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"dnxcore50"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"exclude"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"wwwroot"</span><span class="token punctuation">,</span>
<span class="token string">"node_modules"</span><span class="token punctuation">,</span>
<span class="token string">"bower_components"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"publishExclude"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"node_modules"</span><span class="token punctuation">,</span>
<span class="token string">"bower_components"</span><span class="token punctuation">,</span>
<span class="token string">"**.xproj"</span><span class="token punctuation">,</span>
<span class="token string">"**.user"</span><span class="token punctuation">,</span>
<span class="token string">"**.vspscc"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
</code></pre>
<h3>Publishing the API App</h3>
<p>If there were tooling support, you could create a new Azure API App using the tooling.
Yet: no tooling, thus we have to use the new Azure Portal.</p>
<p>Login using your credentials and create a new Azure API App.</p>
<p>Disclaimer: Depending on your plan and the settings, this will cost you money!</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/AzurePortalCreateApiApp.PNG" alt="" /></p>
<p>After a few seconds, you'll get the success notification inside the Azure Portal and you should have a new tile on the Portal startpage and clicking on it should get you some details:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/AzurePortalApiAppCreated.PNG" alt="" /></p>
<p>Now make sure to set the "Access Level" inside the "Settings" / "Application Settings" to "public (anonymous)" and click the "Save" button afterwards:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/AzurePortalAccessLevel.PNG" alt="" /></p>
<p>Back to Visual Studio 2015 RC, we can now publish our Azure API App by right clicking the project and selecting "Publish...":</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/VSPublish.png" alt="" /></p>
<p>The next dialog allows you to select where to publish:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/PublishWeb.PNG" alt="" /></p>
<p>Select "Microsoft Azure Web Apps" and click "Next".</p>
<p>Now from "Existing Web Apps", select the API App you just created inside the Azure Portal and click "OK":</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/PublishSelectexisting.PNG" alt="" /></p>
<p>The next dialog sums up the connection details and can be confirmed with "Next":</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/PublishCreateAppConnection.PNG" alt="" /></p>
<p>During the next step, you can choose your DNX version to be used for the API App - the defaults are ok, click "Next":</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/PublishSettingsCLRVersion.PNG" alt="" /></p>
<p>The last step is to hit the "Publish" button:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/PublishFinalize.PNG" alt="" /></p>
<p>The publishing process should take only a few seconds:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/PublishActivtity.PNG" alt="" /></p>
<p>When it is finished, it should open up your default browser showing you this:</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/Deployed.PNG" alt="" /></p>
<p>Back to the Azure Portal you should now be able to click on the "API Definition" tile and see the API Operations and be able to download the Swagger using the button above the list of Operations.</p>
<p><img src="https://alexanderzeitler.com/articles/Deploying-a-ASP-NET-MVC-6-API-as-Azure-API-App-in-Azure-App-Services/AzurePortalApiDefinition.PNG" alt="" /></p>
<p>That's it!</p>
lowerCamelCase JSON with ASP.NET MVC 62015-05-01T17:00:00Zhttps://alexanderzeitler.com/articles/lowerCamelCase-JSON-with-ASP-NET-MVC6-ASP-NET-5/<p>With ASP.NET MVC 6 the default output for JSON is still UpperCamelCase.</p>
<p>To change that to lowerCamelCase JSON, simply add this to the <code>ConfigureServices</code> method:</p>
<p><code>Startup.cs</code>:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ConfigureServices</span><span class="token punctuation">(</span><span class="token class-name">IServiceCollection</span> services<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token function">AddMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
services<span class="token punctuation">.</span><span class="token function">ConfigureMvc</span><span class="token punctuation">(</span>options <span class="token operator">=></span>
<span class="token punctuation">{</span>
<span class="token punctuation">(</span>options<span class="token punctuation">.</span>OutputFormatters<span class="token punctuation">.</span><span class="token function">First</span><span class="token punctuation">(</span>f <span class="token operator">=></span> f<span class="token punctuation">.</span>Instance <span class="token keyword">is</span> <span class="token class-name">JsonOutputFormatter</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Instance <span class="token keyword">as</span>
<span class="token class-name">JsonOutputFormatter</span><span class="token punctuation">)</span><span class="token punctuation">.</span>SerializerSettings<span class="token punctuation">.</span>ContractResolver <span class="token operator">=</span>
<span class="token keyword">new</span> <span class="token constructor-invocation class-name">CamelCasePropertyNamesContractResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>That's it!</p>
Running ASP.NET 5 Beta 4 in Docker with DNX runtime #aspnet5 #docker2015-04-27T11:00:00Zhttps://alexanderzeitler.com/articles/Running-ASP.NET-5-beta4-in-Docker-with-DNX-runtime/<h3>Update [2015-05-02]</h3>
<p>As Microsoft has updated it's <a href="https://registry.hub.docker.com/u/microsoft/aspnet/">Dockerfile</a> to support DNX (beta 4 as of this Update), the introduction of this post has become obsolete and you can jump directly over <a href="https://alexanderzeitler.com/articles/Running-ASP.NET-5-beta4-in-Docker-with-DNX-runtime/#dockerfile2">here (in this post)</a>.</p>
<p></end of update> [2015-05-02]</p>
<p>Microsoft recently changed the naming for the <a href="https://github.com/aspnet/home">ASP.NET 5</a> runtime from "K" to "<a href="https://github.com/aspnet/DNX">DNX</a>".
With that, the following K utilities have been renamed:</p>
<ul>
<li>kpm -> dnu</li>
<li>kvm -> dnvm</li>
<li>k -> dnx</li>
</ul>
<p>When you're trying to run DNX with the <a href="https://registry.hub.docker.com/u/microsoft/aspnet/">current ASP.NET 5 Docker image</a> being provided by Microsoft, you'll fail.</p>
<p>Based on the Dockerfile Microsoft provides <a href="https://registry.hub.docker.com/u/microsoft/aspnet/dockerfile/">here</a>, I created a local Docker image to be able to run ASP.NET 5 Beta 4 with the new DNX runtime.</p>
<p>The Dockerfile for the base ASP.NET 5 image goes here:</p>
<h3>Update [2015-05-01]</h3>
<p>You don't have to do the following step, there's also a working Microsoft Dockerfile, just jump to <a href="https://alexanderzeitler.com/articles/Running-ASP.NET-5-beta4-in-Docker-with-DNX-runtime/#dockerfile1">here (in this post)</a>, to read the instructions how to use it.</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> mono:3.12</span>
<span class="token comment"># Get build dependencies, download/build/install mono 4.1.0</span>
<span class="token instruction"><span class="token keyword">RUN</span> apt-get update -qq <span class="token operator">\</span>
&& apt-get install -qqy git autoconf libtool automake build-essential mono-devel gettext unzip <span class="token operator">\</span>
&& git clone https://github.com/mono/mono.git <span class="token operator">\</span>
&& cd mono <span class="token operator">\</span>
&& git reset --hard 53dc56ee39a8e3b013231957aca4671b202c6410 <span class="token operator">\</span>
&& ./autogen.sh --prefix=<span class="token string">"/usr/local"</span> <span class="token operator">\</span>
&& make <span class="token operator">\</span>
&& make install <span class="token operator">\</span>
&& cd .. <span class="token operator">\</span>
&& rm mono -r</span>
<span class="token comment"># Install aspnet 1.0.0-beta4</span>
<span class="token instruction"><span class="token keyword">ENV</span> DNX_FEED https://www.myget.org/F/aspnetmaster/api/v2</span>
<span class="token instruction"><span class="token keyword">ENV</span> DNX_USER_HOME /opt/dnx</span>
<span class="token instruction"><span class="token keyword">RUN</span> curl -sSL https://raw.githubusercontent.com/aspnet/Home/7d6f78ed7a59594ce7cdb54a026f09cb0cbecb2a/dnvminstall.sh | DNX_BRANCH=master sh</span>
<span class="token instruction"><span class="token keyword">RUN</span> bash -c <span class="token string">"source $DNX_USER_HOME/dnvm/dnvm.sh \
&& dnvm install 1.0.0-beta4 -a default \
&& dnvm alias default | xargs -i ln -s $DNX_USER_HOME/runtimes/{} $DNX_USER_HOME/runtimes/default"</span></span>
<span class="token comment"># Install libuv for Kestrel from source code (binary is not in wheezy and one in jessie is still too old)</span>
<span class="token instruction"><span class="token keyword">RUN</span> LIBUV_VERSION=1.4.2 <span class="token operator">\</span>
&& curl -sSL https://github.com/libuv/libuv/archive/v<span class="token variable">${LIBUV_VERSION}</span>.tar.gz | tar zxfv - -C /usr/local/src <span class="token operator">\</span>
&& cd /usr/local/src/libuv-<span class="token variable">$LIBUV_VERSION</span> <span class="token operator">\</span>
&& sh autogen.sh && ./configure && make && make install <span class="token operator">\</span>
&& rm -rf /usr/local/src/libuv-<span class="token variable">$LIBUV_VERSION</span> <span class="token operator">\</span>
&& ldconfig</span>
<span class="token comment"># Update NuGet feeds</span>
<span class="token instruction"><span class="token keyword">RUN</span> mkdir -p ~/.config/NuGet/</span>
<span class="token instruction"><span class="token keyword">RUN</span> curl -o ~/.config/NuGet/NuGet.Config -sSL https://gist.githubusercontent.com/AlexZeitler/a3412a4d4eeee60f8ce8/raw/45b0b5312845099cdf5da560829e75949d44d65f/NuGet.config</span>
<span class="token instruction"><span class="token keyword">ENV</span> PATH <span class="token variable">$PATH</span>:<span class="token variable">$DNX_USER_HOME</span>/runtimes/default/bin</span></code></pre>
<p>Now lets build the base image:</p>
<p><code>sudo docker build -t pdmlab/aspnet:1.0.0 .</code></p>
<p>Based on that, lets create a Dockerfile for our ASP.NET 5 Beta 4 DNX web application:</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> pdmlab/aspnet:1.0.0</span>
<span class="token instruction"><span class="token keyword">ADD</span> . /app</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token instruction"><span class="token keyword">RUN</span> [<span class="token string">"dnu"</span>, <span class="token string">"restore"</span>]</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 5004</span>
<span class="token instruction"><span class="token keyword">ENTRYPOINT</span> [<span class="token string">"dnx"</span>, <span class="token string">"./src/HelloMvc6"</span>, <span class="token string">"kestrel"</span>]</span></code></pre>
<h3><a name="dockerfile1"></a> Update [2015-05-01]</h3>
<p>You can also use this Dockerfile using the Microsoft Beta 4 base Image:</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> microsoft/aspnet:vs-1.0.0-beta4</span>
<span class="token instruction"><span class="token keyword">ADD</span> . /app</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token instruction"><span class="token keyword">RUN</span> [<span class="token string">"dnu"</span>, <span class="token string">"restore"</span>]</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 5004</span>
<span class="token instruction"><span class="token keyword">ENTRYPOINT</span> [<span class="token string">"dnx"</span>, <span class="token string">"./src/HelloMvc6"</span>, <span class="token string">"kestrel"</span>]</span></code></pre>
<p></end of update> [2015-05-01]</p>
<h3><a name="dockerfile2"></a> Update [2015-05-02]</h3>
<p>You can use this Dockerfile using the Microsoft DNX Beta 4 base Image:</p>
<pre class="language-dockerfile"><code class="language-dockerfile"><span class="token instruction"><span class="token keyword">FROM</span> microsoft/aspnet:1.0.0-beta4</span>
<span class="token instruction"><span class="token keyword">ADD</span> . /app</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /app</span>
<span class="token instruction"><span class="token keyword">RUN</span> [<span class="token string">"dnu"</span>, <span class="token string">"restore"</span>]</span>
<span class="token instruction"><span class="token keyword">EXPOSE</span> 5004</span>
<span class="token instruction"><span class="token keyword">ENTRYPOINT</span> [<span class="token string">"dnx"</span>, <span class="token string">"./src/HelloMvc6"</span>, <span class="token string">"kestrel"</span>]</span></code></pre>
<p></end of update> [2015-05-02]</p>
<p>After that, we need to build or application image:</p>
<p><code>sudo docker build -t aspnet5beta4dnx</code></p>
<p>With that done, we can run our container:</p>
<p><code>sudo docker run -t -d -p 80:5004 aspnet5beta4dnx</code></p>
<p>If erverything went well, you should be able to browse http://localhost/api/values on your host.</p>
<p>The source for the HelloMvc6 application comes here:</p>
<p>project.json:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"webroot"</span><span class="token operator">:</span> <span class="token string">"wwwroot"</span><span class="token punctuation">,</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0-*"</span><span class="token punctuation">,</span>
<span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"Kestrel"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Mvc"</span><span class="token operator">:</span> <span class="token string">"6.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.IIS"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.Server.WebListener"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span><span class="token punctuation">,</span>
<span class="token property">"Microsoft.AspNet.StaticFiles"</span><span class="token operator">:</span> <span class="token string">"1.0.0-beta4"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"commands"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"web"</span><span class="token operator">:</span> <span class="token string">"Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"</span><span class="token punctuation">,</span>
<span class="token property">"kestrel"</span><span class="token operator">:</span> <span class="token string">"Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5004"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"frameworks"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"dnx451"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"dnxcore50"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"exclude"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"wwwroot"</span><span class="token punctuation">,</span>
<span class="token string">"node_modules"</span><span class="token punctuation">,</span>
<span class="token string">"bower_components"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"publishExclude"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"node_modules"</span><span class="token punctuation">,</span>
<span class="token string">"bower_components"</span><span class="token punctuation">,</span>
<span class="token string">"**.xproj"</span><span class="token punctuation">,</span>
<span class="token string">"**.user"</span><span class="token punctuation">,</span>
<span class="token string">"**.vspscc"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Startup.cs:</p>
<pre class="language-csharp"><code class="language-csharp">
<span class="token keyword">using</span> <span class="token namespace">System</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Collections<span class="token punctuation">.</span>Generic</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Linq</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Threading<span class="token punctuation">.</span>Tasks</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Builder</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Hosting</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Http</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Routing</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>Framework<span class="token punctuation">.</span>DependencyInjection</span><span class="token punctuation">;</span>
<span class="token keyword">namespace</span> <span class="token namespace">HelloMvc6</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Startup</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token function">Startup</span><span class="token punctuation">(</span><span class="token class-name">IHostingEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">// This method gets called by a runtime.</span>
<span class="token comment">// Use this method to add services to the container</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">ConfigureServices</span><span class="token punctuation">(</span><span class="token class-name">IServiceCollection</span> services<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
services<span class="token punctuation">.</span><span class="token function">AddMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers.</span>
<span class="token comment">// You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json.</span>
<span class="token comment">// services.AddWebApiConventions();</span>
<span class="token punctuation">}</span>
<span class="token comment">// Configure is called after ConfigureServices is called.</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Configure</span><span class="token punctuation">(</span><span class="token class-name">IApplicationBuilder</span> app<span class="token punctuation">,</span> <span class="token class-name">IHostingEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment">// Configure the HTTP request pipeline.</span>
app<span class="token punctuation">.</span><span class="token function">UseStaticFiles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Add MVC to the request pipeline.</span>
app<span class="token punctuation">.</span><span class="token function">UseMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Add the following route for porting Web API 2 controllers.</span>
<span class="token comment">// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>ValuesController.cs:</p>
<pre class="language-csharp"><code class="language-csharp"><span class="token keyword">using</span> <span class="token namespace">System</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Collections<span class="token punctuation">.</span>Generic</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Linq</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">System<span class="token punctuation">.</span>Threading<span class="token punctuation">.</span>Tasks</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token namespace">Microsoft<span class="token punctuation">.</span>AspNet<span class="token punctuation">.</span>Mvc</span><span class="token punctuation">;</span>
<span class="token keyword">namespace</span> <span class="token namespace">HelloMvc6<span class="token punctuation">.</span>Controllers</span>
<span class="token punctuation">{</span>
<span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Route</span><span class="token attribute-arguments"><span class="token punctuation">(</span><span class="token string">"api/[controller]"</span><span class="token punctuation">)</span></span></span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ValuesController</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">Controller</span></span>
<span class="token punctuation">{</span>
<span class="token comment">// GET: api/values</span>
<span class="token punctuation">[</span>HttpGet<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name">IEnumerable<span class="token punctuation"><</span><span class="token keyword">string</span><span class="token punctuation">></span></span> <span class="token function">Get</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name"><span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span></span> <span class="token punctuation">{</span> <span class="token string">"value1"</span><span class="token punctuation">,</span> <span class="token string">"value2"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// GET api/values/5</span>
<span class="token punctuation">[</span><span class="token function">HttpGet</span><span class="token punctuation">(</span><span class="token string">"{id}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">string</span></span> <span class="token function">Get</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> id<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token string">"value"</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// POST api/values</span>
<span class="token punctuation">[</span>HttpPost<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Post</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">FromBody</span></span><span class="token punctuation">]</span><span class="token class-name"><span class="token keyword">string</span></span> <span class="token keyword">value</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">// PUT api/values/5</span>
<span class="token punctuation">[</span><span class="token function">HttpPut</span><span class="token punctuation">(</span><span class="token string">"{id}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Put</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> id<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">FromBody</span></span><span class="token punctuation">]</span><span class="token class-name"><span class="token keyword">string</span></span> <span class="token keyword">value</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">// DELETE api/values/5</span>
<span class="token punctuation">[</span><span class="token function">HttpDelete</span><span class="token punctuation">(</span><span class="token string">"{id}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">Delete</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> id<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
Stop complaining or improve yourself2015-02-14T14:00:00Zhttps://alexanderzeitler.com/articles/stop-complaining-or-improve-yourself/<p>Working in the IT sector never has offered more choices than nowadays.
You can pick from a wide range of hardware platforms supporting an even broader range of operating systems. On top of it, you can pick from a myriad of development platforms and languages.</p>
<div class="more"></div>
<p>On the other hand, more and more people working in the IT sector (I'll focus on software development mostly in this rant) start complaining about so much things in their daily use with their hardware, OS or their development environment / programming language (yes, I also come through this hollow alley). If you start complaining publicly on twitter or Facebook et al. you can even get retweets and likes if you show some creativity when doing it.</p>
<p>Let's face the sad truth: it won't change anything until you start acting.</p>
<p>If you don't like<sup><a href="https://alexanderzeitler.com/articles/stop-complaining-or-improve-yourself/#dontlike">(*)</a></sup> JavaScript, stop complaining and try ES6. If you still don't like<sup><a href="https://alexanderzeitler.com/articles/stop-complaining-or-improve-yourself/#dontlike">(*)</a></sup> it: ditch it and try something different. Maybe you should focus on HTML5 and CSS3 only. If you don't like<sup><a href="https://alexanderzeitler.com/articles/stop-complaining-or-improve-yourself/#dontlike">(*)</a></sup> that either, stop doing web development as a whole. You're not forced to do web development at all.</p>
<p>If you don't like<sup><a href="https://alexanderzeitler.com/articles/stop-complaining-or-improve-yourself/#dontlike">(*)</a></sup> WPF (like me), don't do it, maybe AngularJS might be your frontend framework of choice.
But if you're doing the switch, please don't start complaining about it again. If you don't like<sup><a href="https://alexanderzeitler.com/articles/stop-complaining-or-improve-yourself/#dontlike">(*)</a></sup> it's behavior, try ReactJS or start contributing to AngularJS to improve it.</p>
<p>If you think running JavaScript on the server is plain wrong, simply don't use Node.js/io.js.</p>
<p>If giving up referential integrity is blasphemy for you, don't use (most of the) NoSQL databases.</p>
<p>If you don't like<sup><a href="https://alexanderzeitler.com/articles/stop-complaining-or-improve-yourself/#dontlike">(*)</a></sup> Windows (any longer - like me), try Linux, OS X or build your own operating system.
You may find it hard to change things, but it's up to you to understand and learn things like bash scripting, VIM and all that stuff. If you start experiencing doing better is hard, you might understand why existing things are as they are.</p>
<p>It's your choice to improve yourself or stick with your habbits.
But if you stick with them, please do me a favour and stop complaining about them - they have been your own choice.</p>
<p><a name="dontlike"></a>
* <strong>Update June 17th 2016:</strong></p>
<p>As Daniel pointed in his Tweet, the usage of the term "Don't like..." might be misleading for some people who don't know me:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/alexzeitler_">@alexzeitler_</a> I think you wrote a good article in general. Though I disagree with the abuse of "I don't like" // <a href="https://twitter.com/StefanLieser">@StefanLieser</a></p>— Daniel Marbach (@danielmarbach) <a href="https://twitter.com/danielmarbach/status/743168006344347648">June 15, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>So here's a little explanation what "Don't like" means:
If I wrote "Don't like" in the text above, somebody might get the impression (as Daniel pointed out) "I'm basing all my decisions on taste".</p>
<p>But when I wrote "Don't like" it means: "I tried to solve something with stack x but it didn't work for me in a elegant manner so I tried some other thing which did work much better for that particular problem".</p>
<p>So nothing is based on taste or disgust against a particular stack or company.
It's all based on evaluation without prejudices.</p>
<p>Thanks Daniel, for reading and pointing me at this issue.</p>
<p>And as a baseline for this post I would like to add the following:</p>
<p>Everything we're complaining about everyday - especially on social media - is the work of other people.</p>
<p>And even it doesn't look perfect or work in the expected way for us: Others might have worked hard to get it to this point and we should pay respect for their effort - how often did we build something that wasn't perfect at all? 😉</p>
#NodeJS / #ExpressJS: Adding routes dynamically at runtime2015-02-02T01:00:00Zhttps://alexanderzeitler.com/articles/expressjs-dynamic-runtime-routing/<p>When running a ExpressJS based API or Website, you may want to add new routes without restarting your ExpressJS host.</p>
<p>Actually, adding routes to ExpressJS at runtime is pretty simple.</p>
<p>First, lets create some "design time" routes for an API endpoint as well as some static content to be served by our ExpressJS application:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// development time route</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/api/hello'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span>response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string-property property">"hello"</span><span class="token operator">:</span><span class="token string">"world"</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// static file handling</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>express<span class="token punctuation">.</span><span class="token function">static</span><span class="token punctuation">(</span>__dirname <span class="token operator">+</span> <span class="token string">'/client/app'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>A call to <code>http://localhost:3000/api/hello</code> will return this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><span class="token property">"hello"</span><span class="token operator">:</span><span class="token string">"world"</span><span class="token punctuation">}</span></code></pre>
<p>And the call to <code>http://localhost:3000</code> will render us some HTML:</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Hello World!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hello World<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Nothing special here, move along :)</p>
<p>Now, lets assume we're dropping a new ExpressJS controller into our <code>./controllers</code> folder at runtime:</p>
<pre class="language-javascript"><code class="language-javascript">module<span class="token punctuation">.</span>exports<span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">init</span> <span class="token operator">:</span> init
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token parameter">app</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/api/myruntimeroute'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span>res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string-property property">"runtime"</span> <span class="token operator">:</span> <span class="token string">"route"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>When calling it's route <code>http://localhost:3000/api/myruntimeroute</code> we'll get a 404. Sad panda.</p>
<p>Now lets drop this piece of code into our app.js before the <code>app.listen(3000)</code> line (this needs to be added before the host is started, of course):</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// hook to initialize the dynamic route at runtime</span>
app<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/api/dynamic'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span>res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">var</span> dynamicController <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./controllers/RuntimeController'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
dynamicController<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span><span class="token punctuation">;</span>
res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>When sending a POST (<code>curl -X POST http://localhost:3000/api/dynamic</code>) to that endpoint, our controller dropped in at runtime, now gets initalizied and can register it's routes.</p>
<p>Calling <code>http://localhost:3000/api/myruntimeroute</code> now GETs us some fancy JSON:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><span class="token property">"runtime"</span> <span class="token operator">:</span> <span class="token string">"route"</span><span class="token punctuation">}</span></code></pre>
<p>Looking back to the hookup code, you can see that the name of the controller is already known which of course is just for the sake of simplicity of this sample.</p>
<p>In a real world implementation you might have some other hook up like a user action in your administration which looks for new controllers in a specific folder. Or you might monitor a specfic folder for new controller files. You get the point.</p>
<p>Another approach for dynamic runtime routing could be to have catch some/all route definition which hooks in when all other routes fail.</p>
<p>A small example:</p>
<pre class="language-javascript"><code class="language-javascript">app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/api/:dynamicroute'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span>res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string-property property">"param"</span> <span class="token operator">:</span> req<span class="token punctuation">.</span>params<span class="token punctuation">.</span>dynamicroute<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In this snippet, the part behind the <code>/api/</code> path is dynamic which means you can have some logic which distributes the requests based on decisions made at runtime.</p>
<p>You can find the source code for this sample in <a href="https://github.com/AlexZeitler/expressjs-dynamic-runtime-routing">this</a> GitHub repository.</p>
mongoose: Referencing schema in properties or arrays2015-02-01T01:00:00Zhttps://alexanderzeitler.com/articles/mongoose-referencing-schema-in-properties-and-arrays/<p>When using a NoSQL database like MongoDb, most of the time you'll have documents that contain all properties by itself.
But there are also scenarios where you might encounter the need for a more relational approach and need to reference other documents by the ObjectIds.</p>
<p>This post will show you how to deal with these references using Node.js and the <a href="http://mongoosejs.com/">mongoose ODM</a>.</p>
<p>Lets consider we'll have a users collection and a posts collection, thus we'll have a <code>UserSchema</code> as well as a <code>PostSchema</code>.
Posts can be written by users and the can by commented by users.</p>
<p>In this example, well reference the users in posts and comments by their ObjectId reference.</p>
<p>The <code>UserSchema</code> is implemented straight forward and looks like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> UserSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">name</span><span class="token operator">:</span> String
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">"User"</span><span class="token punctuation">,</span> UserSchema<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Beside the title property, the <code>PostSchema</code> also defines the reference by ObjectId for the <code>postedBy</code> property of the PostSchema as well as the <code>postedBy</code> property of the comments inside the <code>comments</code> array property:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> PostSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">title</span><span class="token operator">:</span> String<span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">type</span><span class="token operator">:</span> mongoose<span class="token punctuation">.</span>Schema<span class="token punctuation">.</span>Types<span class="token punctuation">.</span>ObjectId<span class="token punctuation">,</span>
<span class="token literal-property property">ref</span><span class="token operator">:</span> <span class="token string">'User'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">comments</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> String<span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">type</span><span class="token operator">:</span> mongoose<span class="token punctuation">.</span>Schema<span class="token punctuation">.</span>Types<span class="token punctuation">.</span>ObjectId<span class="token punctuation">,</span>
<span class="token literal-property property">ref</span><span class="token operator">:</span> <span class="token string">'User'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">"Post"</span><span class="token punctuation">,</span> PostSchema<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Now lets create two users:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./database"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> User <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./User'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
Post <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./Post'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> alex <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Alex"</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> joe <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Joe"</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
alex<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
joe<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The interesting part of course is the creation and even more the query for posts.
The post is created with the ObjectId references to the users.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> post <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"Hello World"</span><span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> alex<span class="token punctuation">.</span>_id<span class="token punctuation">,</span>
<span class="token literal-property property">comments</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">"Nice post!"</span><span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> joe<span class="token punctuation">.</span>_id
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">"Thanks :)"</span><span class="token punctuation">,</span>
<span class="token literal-property property">postedBy</span><span class="token operator">:</span> alex<span class="token punctuation">.</span>_id
<span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Now lets save the Post and after it got created, query for all existing Posts.</p>
<pre class="language-javascript"><code class="language-javascript">post<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Post<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">populate</span><span class="token punctuation">(</span><span class="token string">'postedBy'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">populate</span><span class="token punctuation">(</span><span class="token string">'comments.postedBy'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> posts</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>posts<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">"\t"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>As you can see, the we're using the <a href="http://mongoosejs.com/docs/populate.html"><code>populate</code></a> function of mongoose to join the documents when querying for Posts.
The first call to <code>populate</code> joins the Users for the <code>postedBy</code> property of the posts whereas the second one joins the Users for the comments.</p>
<p>The Post document in the database looks like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"_id"</span> <span class="token operator">:</span> ObjectId(<span class="token string">"54cd6669d3e0fb1b302e54e6"</span>)<span class="token punctuation">,</span>
<span class="token property">"title"</span> <span class="token operator">:</span> <span class="token string">"Hello World"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span> <span class="token operator">:</span> ObjectId(<span class="token string">"54cd6669d3e0fb1b302e54e4"</span>)<span class="token punctuation">,</span>
<span class="token property">"comments"</span> <span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"text"</span> <span class="token operator">:</span> <span class="token string">"Nice post!"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span> <span class="token operator">:</span> ObjectId(<span class="token string">"54cd6669d3e0fb1b302e54e5"</span>)<span class="token punctuation">,</span>
<span class="token property">"_id"</span> <span class="token operator">:</span> ObjectId(<span class="token string">"54cd6669d3e0fb1b302e54e8"</span>)
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"text"</span> <span class="token operator">:</span> <span class="token string">"Thanks :)"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span> <span class="token operator">:</span> ObjectId(<span class="token string">"54cd6669d3e0fb1b302e54e4"</span>)<span class="token punctuation">,</span>
<span class="token property">"_id"</span> <span class="token operator">:</span> ObjectId(<span class="token string">"54cd6669d3e0fb1b302e54e7"</span>)
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span> <span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span></code></pre>
<p>In contrast, the query result is a full document containing all User references for the Posts.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"54cd6669d3e0fb1b302e54e6"</span><span class="token punctuation">,</span>
<span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Hello World"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"54cd6669d3e0fb1b302e54e4"</span><span class="token punctuation">,</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Alex"</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
<span class="token property">"comments"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"Nice post!"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"54cd6669d3e0fb1b302e54e5"</span><span class="token punctuation">,</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Joe"</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"54cd6669d3e0fb1b302e54e8"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"Thanks :)"</span><span class="token punctuation">,</span>
<span class="token property">"postedBy"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"54cd6669d3e0fb1b302e54e4"</span><span class="token punctuation">,</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Alex"</span><span class="token punctuation">,</span>
<span class="token property">"__v"</span><span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"54cd6669d3e0fb1b302e54e7"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
</code></pre>
<p>You can find the source code for this sample in <a href="https://github.com/AlexZeitler/mongoose-schema-reference-sample">this</a> GitHub repository.</p>
MongoDB development environment journal size management using mongoctl2015-01-26T21:00:00Zhttps://alexanderzeitler.com/articles/MongoDb-development-environment-and-journalsize-management-using-mongoctl/<p>On a developer machine you might have to install multiple versions of MongoDB or you might want to run multiple MongoDB server instances.
Both can be done easily using <a href="https://github.com/mongolab/mongoctl">mongoctl</a>.</p>
<p>To install mongoctl on Ubuntu, just run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> pip <span class="token function">install</span> mongoctl</code></pre>
<p>In case you don't have installed pip, just run this before the above command:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> clone https://github.com/pypa/pip.git
<span class="token builtin class-name">cd</span> pip
python setup.py <span class="token function">install</span> <span class="token comment">#might need sudo / root</span></code></pre>
<p>To install the latest stable MongoDB release just run:</p>
<pre class="language-bash"><code class="language-bash">mongoctl install-mongodb</code></pre>
<p>Beside installing MongoDB this also creates the config file for your first MongoDB server name "MyServer".
You can simply start it using</p>
<pre class="language-bash"><code class="language-bash">mongoctl start MyServer</code></pre>
<p>Stopping is as easy as starting it:</p>
<pre class="language-bash"><code class="language-bash">mongoctl stop MyServer</code></pre>
<p>By default, <a href="http://docs.mongodb.org/manual/core/journaling/">journaling</a> is enabled for MongoDB and may eat your hard disk if you're running many MongoDB servers.
Having installed MongoDB using mongoctl, the journal of "MyServer" can be found here:</p>
<pre class="language-bash"><code class="language-bash">~/mongodb-data/my-server/journal</code></pre>
<p>Listing the directory content shows us, that our instance uses 3GB of hard disk space.</p>
<pre class="language-bash"><code class="language-bash">drwxrwxr-x <span class="token number">2</span> alexzeitler alexzeitler <span class="token number">4</span>,0K Jan <span class="token number">25</span> 00:09 <span class="token builtin class-name">.</span>
drwxrwxr-x <span class="token number">10</span> alexzeitler alexzeitler <span class="token number">4</span>,0K Jan <span class="token number">24</span> <span class="token number">23</span>:15 <span class="token punctuation">..</span>
-rw------- <span class="token number">1</span> alexzeitler alexzeitler <span class="token number">1</span>,0G Jan <span class="token number">25</span> 00:09 prealloc.0
-rw------- <span class="token number">1</span> alexzeitler alexzeitler <span class="token number">1</span>,0G Nov <span class="token number">25</span> <span class="token number">13</span>:04 prealloc.1
-rw------- <span class="token number">1</span> alexzeitler alexzeitler <span class="token number">1</span>,0G Nov <span class="token number">25</span> <span class="token number">13</span>:04 prealloc.2</code></pre>
<p>While (until the 2.0 release) disabling the journal is a bad idea for production, it is ok for a development environment.</p>
<p>To disable the journaling for "MyServer", head over to <code>~/.mongoctl</code> and edit the <code>servers.config</code> file.</p>
<p>No find the "MyServer" configuration, which should be the first entry and look like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"MyServer"</span><span class="token punctuation">,</span>
<span class="token property">"description"</span> <span class="token operator">:</span> <span class="token string">"My server (single mongod)"</span><span class="token punctuation">,</span>
<span class="token property">"cmdOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"port"</span><span class="token operator">:</span> <span class="token number">27017</span><span class="token punctuation">,</span>
<span class="token property">"dbpath"</span><span class="token operator">:</span> <span class="token string">"~/mongodb-data/my-server"</span><span class="token punctuation">,</span>
<span class="token property">"directoryperdb"</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>In order to disable MongoDB journaling, add the <code>nojournal</code> property to <code>cmdOptions</code>:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"_id"</span><span class="token operator">:</span> <span class="token string">"MyServer"</span><span class="token punctuation">,</span>
<span class="token property">"description"</span> <span class="token operator">:</span> <span class="token string">"My server (single mongod)"</span><span class="token punctuation">,</span>
<span class="token property">"cmdOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"port"</span><span class="token operator">:</span> <span class="token number">27017</span><span class="token punctuation">,</span>
<span class="token property">"dbpath"</span><span class="token operator">:</span> <span class="token string">"~/mongodb-data/my-server"</span><span class="token punctuation">,</span>
<span class="token property">"directoryperdb"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token property">"nojournal"</span> <span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>No, stop your "MyServer" instance and delete the files from the <code>~/mongodb-data/my-server/journal</code> (you're doing this at your own risk, of course - and as said not in a production environment!) directory.</p>
<p>After starting "MyServer" again, the journal folder should stay empty. That's it!</p>
<p>You can also limit the journal file size to 128MB by setting the <code>smallfiles</code> property to <code>true</code>.</p>
Farewell...2015-01-17T22:00:00Zhttps://alexanderzeitler.com/articles/Farewell/<p>The title of this post might sound a bit scenic, but 2014 indeed has been a year of goodbys for me - at least regarding software development.
As almost every leave also is a fresh start, lots of things have changed.</p>
<p>So lets say farewell to...</p>
<h3>... .NET only software development</h3>
<p>While contributing to "Pro ASP.NET Web API", doing talks and projects on ASP.NET Web API in 2012 and 2013 by persuasion, 2014 has become the year where I decided to gain broader knowledge by practice in the non-Microsoft space.</p>
<p>This means digging into Linux, Node.js and MongoDb and all that things like Gulp, Yeoman, Docker and ...you name it.</p>
<p>I also nosed around Scala, Go and Erlang, but in the end, I stick with Node.js and it's friends.</p>
<p>Things that felt bumpy (especcially because of Windows, e.g. CMD vs bash, Docker vs. ...?) in the .NET world, worked like a charm on the new stack and allowed me to purge my whole software development and deployment process.</p>
<p>Also the huge open source momentum in the Node.js ecosystem deeply impressed me.</p>
<p>On the other hand, in 2014, the .NET stack also has moved forward and Node.js not always is the promised land.
The biggest gain for ages has been open sourcing the .NET framework and making it a real cross platform development environment.</p>
<p>As a .NET developer since the early days, I surely won't leave the .NET ecosystem in the near future, but there may be requirements where I or my customers will prefer the Node.js environment in the future.</p>
<h3>... blog.alexonasp.net</h3>
<p>When you're reading these lines, my old blog "Alex on ASP.NET" which has been around since 2003, is gone.
As aforementioned, I'm no longer developing solely in the .NET space and thus my blog needs to be as open as I am.</p>
<h3>... BlogEngine.NET</h3>
<p>"Alex on ASP.NET" in it's early days has been running on "dasBlog" which has been replaced by SubText later on. Since 2006 it has been running on BlogEngine.NET.</p>
<p>As blog posts after being published, almost never are modified, running a database backed blog engine doesn't make sence to me any longer.</p>
<p>Because of this, I migrated my old blog to Wintersmith, a static site generator based on Node.js.
It is now hosted on the DigitalOcean cloud platform and deployment is done using git.</p>
<p>I'll cover this in a upcoming post for everyone who's interested in that topic.</p>