<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Code Corner</title>
	<atom:link href="http://codecorner.galanter.net/feed/" rel="self" type="application/rss+xml" />
	<link>https://codecorner.galanter.net</link>
	<description>ASP.NET, XML, SQL and Javascript tips and tricks</description>
	<lastBuildDate>Sat, 20 Jul 2024 22:24:54 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.5</generator>
<site xmlns="com-wordpress:feed-additions:1">16734420</site>	<item>
		<title>Fire Tablet setup: How to skip Amazon registration</title>
		<link>https://codecorner.galanter.net/2024/07/20/fire-tablet-setup-how-to-skip-amazon-registration/</link>
					<comments>https://codecorner.galanter.net/2024/07/20/fire-tablet-setup-how-to-skip-amazon-registration/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Sat, 20 Jul 2024 22:24:54 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Rant]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[fire]]></category>
		<category><![CDATA[tablet]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3607</guid>

					<description><![CDATA[Fire tablets are great cheap Android tablets, but they&#8217;re tied to Amazon ecosystem, which sucks. You used to be able to skip Amazon registration during setup, but on newer devices you can&#8217;t anymore. Except you still can. During setup you&#8217;re presented with &#8220;Register your fire&#8221; screen, and &#8220;Continue&#8221; button, and no way to bypass it. [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Fire tablets are great cheap Android tablets, but they&#8217;re tied to Amazon ecosystem, which sucks. You used to be able to skip Amazon registration during setup, but on newer devices you can&#8217;t anymore.</p>
<p>Except you still can.</p>
<p>During setup you&#8217;re presented with &#8220;Register your fire&#8221; screen,  and &#8220;Continue&#8221; button, and no way to bypass it. Follow these steps:</p>
<ol>
<li>Tap &#8220;New to Amazon? Start here&#8221; link.</li>
<li>&#8220;Choose Country or Region&#8221; screen appears &#8211; select any country</li>
<li>&#8220;Create an Amazon Account&#8221; screen appears. Instead of entering name, email, and password just tap &#8220;Close&#8221; button</li>
</ol>
<p>You&#8217;re back in the original &#8220;Register your fire&#8221; screen, but now in addition to &#8220;Continue&#8221; button you will see &#8220;Not now&#8221; that lets you skip the registration.</p>
<p>Now you&#8217;re free to install an appstore of your choice e.g. <a href="https://f-droid.org/">F-Droid </a> or <a href="https://liliputing.com/hack-your-amazon-fire-tablet-with-fire-toolbox-v10/">Google Play Store</a> and use it as a regular Android tablet.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2024/07/20/fire-tablet-setup-how-to-skip-amazon-registration/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3607</post-id>	</item>
		<item>
		<title>Bluesky now supports embedded posts</title>
		<link>https://codecorner.galanter.net/2024/04/15/bluesky-now-supports-embedded-posts/</link>
					<comments>https://codecorner.galanter.net/2024/04/15/bluesky-now-supports-embedded-posts/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Mon, 15 Apr 2024 19:30:44 +0000</pubDate>
				<category><![CDATA[Bluesky]]></category>
		<category><![CDATA[Mastodon]]></category>
		<category><![CDATA[New Stuff]]></category>
		<category><![CDATA[Rant]]></category>
		<category><![CDATA[Social]]></category>
		<category><![CDATA[social]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3601</guid>

					<description><![CDATA[&#34;I am a Trekkie because Star Trek isn&#x27;t woke&#34; Star Trek: [image or embed] &#8212; Comfortably Numb (@ygalanter.bsky.social) Jan 4, 2024 at 6:40 PM October 29, 2023: FiveThirtyEight became self-aware. [image or embed] &#8212; Comfortably Numb (@ygalanter.bsky.social) Oct 29, 2023 at 12:48 PM So Musk removed news headlines from Twitter across all devices, Web and [&#8230;]]]></description>
										<content:encoded><![CDATA[<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:pmyqirafcp3jqdhrl7crpq7t/app.bsky.feed.post/3ki6x2w5l3i2h" data-bluesky-cid="bafyreifikcfjjbjfaqaiblnbb5et2nhmrqfrfkuqnzltmfohfwn5h3njoi">
<p lang="en">&quot;I am a Trekkie because Star Trek isn&#x27;t woke&quot;</p>
<p>Star Trek:</p>
<p><a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3ki6x2w5l3i2h?ref_src=embed">[image or embed]</a></p>
<p>&mdash; Comfortably Numb (<a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t?ref_src=embed">@ygalanter.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3ki6x2w5l3i2h?ref_src=embed">Jan 4, 2024 at 6:40 PM</a></p></blockquote>
<p><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script><br />
<span id="more-3601"></span></p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:pmyqirafcp3jqdhrl7crpq7t/app.bsky.feed.post/3kcvqt6ezpj2r" data-bluesky-cid="bafyreictdtmknwjytl4v2oy2yxg7pnt3plpuzbbubk53hx4u35lkpns5s4">
<p lang="en">October 29, 2023: FiveThirtyEight became self-aware.</p>
<p><a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kcvqt6ezpj2r?ref_src=embed">[image or embed]</a></p>
<p>&mdash; Comfortably Numb (<a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t?ref_src=embed">@ygalanter.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kcvqt6ezpj2r?ref_src=embed">Oct 29, 2023 at 12:48 PM</a></p></blockquote>
<p><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script></p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:pmyqirafcp3jqdhrl7crpq7t/app.bsky.feed.post/3kaynorcjgp25" data-bluesky-cid="bafyreico5x456go5hks7w7w6ekkx7ljavfjr4ilqnamvjiy2v7ibnqk42i">
<p lang="en">So Musk removed news headlines from Twitter across all devices, Web and mobile. Ok then.</p>
<p><a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kaynorcjgp25?ref_src=embed">[image or embed]</a></p>
<p>&mdash; Comfortably Numb (<a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t?ref_src=embed">@ygalanter.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kaynorcjgp25?ref_src=embed">Oct 5, 2023 at 5:40 AM</a></p></blockquote>
<p><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script></p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:pmyqirafcp3jqdhrl7crpq7t/app.bsky.feed.post/3khpq3yjau72a" data-bluesky-cid="bafyreierymifzthhprnbrmrffho2rcd3rh3hvyxhv4i2upqmpsmhfymmkm">
<p lang="en">These people are so close</p>
<p><a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3khpq3yjau72a?ref_src=embed">[image or embed]</a></p>
<p>&mdash; Comfortably Numb (<a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t?ref_src=embed">@ygalanter.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3khpq3yjau72a?ref_src=embed">Dec 29, 2023 at 5:25 PM</a></p></blockquote>
<p><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script></p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:pmyqirafcp3jqdhrl7crpq7t/app.bsky.feed.post/3kduo34kewu2e" data-bluesky-cid="bafyreieef7ad7fo2p3h4s3zygqawyofqrgodu4ycfnd3bxrpm4tbmem3ui">
<p lang="en">
<p><a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kduo34kewu2e?ref_src=embed">[image or embed]</a></p>
<p>&mdash; Comfortably Numb (<a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t?ref_src=embed">@ygalanter.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kduo34kewu2e?ref_src=embed">Nov 10, 2023 at 6:52 PM</a></p></blockquote>
<p><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script></p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:pmyqirafcp3jqdhrl7crpq7t/app.bsky.feed.post/3klvbncqbml2d" data-bluesky-cid="bafyreicandey26ah3bmbtg46qixodniva6ie3fxzfrswcojxjltk4g4jbu">
<p lang="en">ACAB includes fortune cookies</p>
<p><a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3klvbncqbml2d?ref_src=embed">[image or embed]</a></p>
<p>&mdash; Comfortably Numb (<a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t?ref_src=embed">@ygalanter.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3klvbncqbml2d?ref_src=embed">Feb 20, 2024 at 8:03 PM</a></p></blockquote>
<p><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script></p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:pmyqirafcp3jqdhrl7crpq7t/app.bsky.feed.post/3kldmi5e2ck2x" data-bluesky-cid="bafyreihw5srfk2bi4pcmzievawjlhcijpoavm4rxmduhyyvwsm4kqbzt3q">
<p lang="en">Every now and then I wonder why I don&#x27;t use Mastodon anymore, but then remember this experience, ah that&#x27;s why. bsky.app/profile/ygal&#8230;</p>
<p><a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kldmi5e2ck2x?ref_src=embed">[image or embed]</a></p>
<p>&mdash; Comfortably Numb (<a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t?ref_src=embed">@ygalanter.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:pmyqirafcp3jqdhrl7crpq7t/post/3kldmi5e2ck2x?ref_src=embed">Feb 13, 2024 at 7:29 PM</a></p></blockquote>
<p><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script></p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2024/04/15/bluesky-now-supports-embedded-posts/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3601</post-id>	</item>
		<item>
		<title>Force add file permission in git.</title>
		<link>https://codecorner.galanter.net/2024/04/05/force-add-file-permission-in-git/</link>
					<comments>https://codecorner.galanter.net/2024/04/05/force-add-file-permission-in-git/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Fri, 05 Apr 2024 17:50:44 +0000</pubDate>
				<category><![CDATA[New Stuff]]></category>
		<category><![CDATA[Rant]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[permissions]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3593</guid>

					<description><![CDATA[I was working on a Windows laptop with a repo originally created on Mac, and git arbitrary changed bash script file permission from 755 to 644. I was able to change it back by updating git index But I couldn&#8217;t commit this change, because as far as git was concerned nothing in the file changed. [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I was working on a Windows laptop with a repo originally created on Mac, and git arbitrary changed bash script file permission from <code>755</code>  to <code>644</code>.</p>
<p>I was able to change it back by updating git index</p>
<pre class="brush: bash; title: ; notranslate">git update-index --chmod=+x script.sh</pre>
<p>But I couldn&#8217;t commit this change, because as far as git was concerned nothing in the file changed. But I was able to force-add the change</p>
<pre class="brush: bash; title: ; notranslate">git add --chmod=+x -- script.sh</pre>
<p>After that I was able to commit and push the change.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2024/04/05/force-add-file-permission-in-git/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3593</post-id>	</item>
		<item>
		<title>Just let TypeScript do its thing</title>
		<link>https://codecorner.galanter.net/2023/07/10/just-let-typescript-do-its-thing/</link>
					<comments>https://codecorner.galanter.net/2023/07/10/just-let-typescript-do-its-thing/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Mon, 10 Jul 2023 19:45:19 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[lambda]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3554</guid>

					<description><![CDATA[It is a well known fact that compile-time errors during development are easier and cheaper to catch than runtime ones in production. Unfortunately people keep forgetting that simple fact. Let me tell you a little story about AWS Lambda hiccup, and how it could easily be avoided. A developer wrote a simple Lambda function in [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>It is a well known fact that compile-time errors during development are easier and cheaper to catch than runtime ones in production. Unfortunately people keep forgetting that simple fact. Let me tell you a little story about AWS Lambda hiccup, and how it could easily be avoided.</p>
<p>A developer wrote a simple Lambda function in TypeScript that would receive a parameter of the following type:</p>
<pre class="brush: jscript; title: ; notranslate">interface Notification {
    slack?: {
        notify: boolean
    };
    github?: {
        notify: boolean
    };
}</pre>
<p>The logic is simple – if <code>slack.notify</code> is <code>true</code> – send slack notification. If <code>github.notify</code> is <code>true</code> – send github commit status notification. The function deployed without issues, and it does send slack notifications. There is one problem: often it would send the same slack notification 3 times in a row.<span id="more-3554"></span><br />
What could be the reason? Let’s take a look at actual function. It goes something like this:</p>
<pre class="brush: jscript; title: ; notranslate">function notify(notification: any) {
 
    if (notification.slack.notify) {
        // ...
    }
 
    if (notification.github.notify) {
        // ...
    }
}</pre>
<p>Notice anything wrong with it? Yes, that’s right – the type of the parameter is defined as <code>any</code>, which means no type checks is being done by TypeScript. Now, as you notice both <code>slack</code> and <code>github</code> properties are optional. What will happen if the function receives value like</p>
<pre class="brush: jscript; title: ; notranslate">{
   slack: {
      notify: true
   }
}</pre>
<p>The first <code>if</code> condition is satisfied, and the function sends slack notification. But the second one will throw an exception</p>
<p><code style="color:red;font-style:italic">Cannot read property "notify" of undefined</code></p>
<p>because <code>github</code> property is not present in this value. Function throws the exception, and Lambda invocation fails. By default a failed Lambda re-tries invocation 2 times. So the slack notification will be send 2 more times. Mystery solved.</p>
<p>How could this be avoided? By defining function parameter as strongly typed. If you do so – TypeScript won’t let you compile the code as is. Moreover in IDEs like VS Code you will get visual representation of the error:</p>
<p><img fetchpriority="high" decoding="async" src="https://codecorner.galanter.net/wp-content/uploads/sites/2/2023/07/typescript-error.png" alt="TypeScript error" width="511" height="240" class="aligncenter size-full wp-image-3558" srcset="https://codecorner.galanter.net/wp-content/uploads/sites/2/2023/07/typescript-error.png 511w, https://codecorner.galanter.net/wp-content/uploads/sites/2/2023/07/typescript-error-300x141.png 300w, https://codecorner.galanter.net/wp-content/uploads/sites/2/2023/07/typescript-error-200x94.png 200w" sizes="(max-width: 511px) 100vw, 511px" /></p>
<p>The only way TypeScript will let this thru if you specify properties as optional</p>
<pre class="brush: jscript; title: ; notranslate">function notify(notification: Notification) {
 
    if (notification.slack?.notify) {
        // ...
    }
 
    if (notification.github?.notify) {
        // ...
    }
}</pre>
<p>With this version, in the above scenario if <code>github</code> property isn’t there – it won’t even bother to check for <code>.notify</code> value, the second if condition will fail before that.</p>
<p>Moral of this story: Let TypeScript do its thing, the types are there for a reason, and often the reason is to prevent you from shooting yourself in the foot.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2023/07/10/just-let-typescript-do-its-thing/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3554</post-id>	</item>
		<item>
		<title>Scan standalone CDK constructs for misconfiguration</title>
		<link>https://codecorner.galanter.net/2023/06/07/scan-standalone-cdk-constructs-for-misconfiguration/</link>
					<comments>https://codecorner.galanter.net/2023/06/07/scan-standalone-cdk-constructs-for-misconfiguration/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Wed, 07 Jun 2023 20:03:20 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[CDK]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3560</guid>

					<description><![CDATA[Vulnerability scanners like checkov have become a de facto standard for scanning IaC for possible misconfigurations. They can scan terraform configs, AWS CloudFormation templates, even – in a sense – CDK stacks, because they synthesize into CloudFormation templates that can be scanned. In order to produce synthesized templates from stacks you have to have a [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Vulnerability scanners like <a href="https://www.checkov.io/" rel="noopener" target="_blank">checkov</a> have become a de facto standard for scanning IaC for possible misconfigurations. They can scan terraform configs, AWS CloudFormation templates, even – in a sense – CDK stacks, because they synthesize into CloudFormation templates that can be scanned.</p>
<p>In order to produce synthesized templates from stacks you have to have a full-blown CDK application. But what if you’re developing a standalone CDK construct as a library, to be used as a dependency in a CDK app? There is a way to scan it for vulnerabilities as well.<span id="more-3560"></span></p>
<p><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.assertions-readme.html" rel="noopener" target="_blank">aws-cdk-lib/assertions</a> module’s intended use is to write unit tests for CDK applications, and one of its features we can use is programmatically generate a CloudFormation template from a stack via <code>Template.fromStack()</code> method.</p>
<p>Take a look at the following code</p>
<pre class="brush: jscript; title: ; notranslate">import { MyCoolConstruct } from &#039;../src/index&#039;;
import { Stack } from &#039;aws-cdk-lib&#039;;
import { Template } from &#039;aws-cdk-lib/assertions&#039;;
import * as fs from &#039;fs&#039;;
 
let stack = new Stack();
new MyCoolConstruct(stack, &#039;MyCoolConstruct&#039;);
 
let template = JSON.stringify(Template.fromStack(stack));
fs.writeFileSync(&#039;my-cool-construct.json&#039;, template);</pre>
<p><em>Lines 01-04</em> import your construct code<br />
<em>Lines 06-07</em> create an empty stack and instantiate yout construct in it<br />
<em>Lines 09-10</em> create a CloudFormation template and save it as a file.</p>
<p>Now you can run this code to generate the template, and scan the resulting template with checkov</p>
<pre class="brush: bash; gutter: false; title: ; notranslate">npx ts-node -- get-templates.ts
checkov -f my-cool-construct.json</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2023/06/07/scan-standalone-cdk-constructs-for-misconfiguration/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3560</post-id>	</item>
		<item>
		<title>Override private methods in TypeScript</title>
		<link>https://codecorner.galanter.net/2023/04/08/override-private-methods-in-typescript/</link>
					<comments>https://codecorner.galanter.net/2023/04/08/override-private-methods-in-typescript/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Sat, 08 Apr 2023 23:19:32 +0000</pubDate>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3535</guid>

					<description><![CDATA[First things first: a word of caution – do this only if you absolutely have to. TypeScript has these checks for a reason, and probably in 99% of the cases you should respect and abide these restrictions. But if your scenario falls within the 1% where you must in a subclass override a private method [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>First things first: a word of caution – do this only if you absolutely have to. TypeScript has these checks for a reason, and probably in 99% of the cases you should respect and abide these restrictions. But if your scenario falls within the 1% where you must in a subclass override a private method of a superclass – this post shows a way to do this.</p>
<p>Let’s say we have a class that defines a generic starship, and what happens to it when it’s hit by a weapon:</p>
<pre class="brush: jscript; title: ; notranslate">type Hit = &#039;phaser&#039; | &#039;antimatter spread&#039; | &#039;photon torpedo&#039;
 
class Starship {
    private totalDamage: number;
 
    constructor(private shipName: string) {
        this.totalDamage = 0;
    }
 
    private addDamage(damage: number) {
        this.totalDamage += damage;
    }
 
    public isHitBy(hit: Hit) {
        console.log(`${this.shipName} is hit by ${hit}!`)
 
        switch(hit) {
            case &#039;phaser&#039;:
                this.addDamage(100);
                break;
            case &#039;antimatter spread&#039;:
                this.addDamage(300);
                break;
            case &#039;photon torpedo&#039;:
                this.addDamage(500);
                break
        }
    }
}</pre>
<p>You can create your own class of starship based on this generic class, then build a ship of the new class, and simulate it being hit by a weapon:<span id="more-3535"></span></p>
<pre class="brush: jscript; title: ; notranslate">class Galaxy extends Starship {
    constructor(name: string) {
        super(name);
    }
}
 
const enterprise = new Galaxy(&#039;Enterprise&#039;);
enterprise.isHitBy(&#039;photon torpedo&#039;);</pre>
<p>Output:</p>
<p><code>"Enterprise is hit by photon torpedo!"</code></p>
<p>This is fine, but as you’ve seen the base class has a secret feature to calculate damage inflicted by the weapon. It is secret, because it’s defined by a private method, and is not exposed to derived classes. But, like Kirk in Kobayashi Maru, you want to break the rules, and have this forbidden knowledge.</p>
<p>If you attempt to add method override in a regular way:</p>
<pre class="brush: jscript; title: ; notranslate">class Galaxy extends Starship {
    constructor(name: string) {
        super(name);
    }
  
    addDamage(damage: number) {
        super.addDamage(damage);
        console.log(`The damage is: ${damage}`)
    }
}</pre>
<p>You will be greeted with error message</p>
<p><code style="color:red;font-style:italic;">Property 'addDamage' is private in type 'Starship' but not in type 'Galaxy'.</code></p>
<p>If you try to change your version of the method to <code>private</code> – TypeScript will complain again:</p>
<p><code style="color:red;font-style:italic;">Types have separate declarations of a private property 'addDamage'.</code></p>
<p>In order to bypass TypeScript restrictions you can use square bracket notation instead of the dot:</p>
<pre class="brush: jscript; title: ; notranslate">class Galaxy extends Starship {
    constructor(name: string) {
        super(name);
 
        this&#x5B;&#039;addDamage&#039;] = (damage: number) =&gt; {
            super&#x5B;&#039;addDamage&#039;](damage);
            console.log(`The damage is: ${damage}`)
        }
    }
}</pre>
<p>Now let’s try instantiate the subclass, and try hit it again:</p>
<pre class="brush: jscript; title: ; notranslate">const enterprise = new Galaxy(&#039;Enterprise&#039;);
enterprise.isHitBy(&#039;photon torpedo&#039;);</pre>
<p>Now the output includes the info from our rule-breaking override:</p>
<p><code>"Enterprise is hit by photon torpedo!"<br />
"The damage is: 500"</code></p>
<p>Once again, not to sound like a broken record, but use this approach only if you have to (e.g. if you’re subclassing a class from a library you have no control over, but absolutely need the access to its inner workings).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2023/04/08/override-private-methods-in-typescript/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3535</post-id>	</item>
		<item>
		<title>Reverse values into variables in tmplr</title>
		<link>https://codecorner.galanter.net/2023/04/03/reverse-values-into-variables-in-tmplr/</link>
					<comments>https://codecorner.galanter.net/2023/04/03/reverse-values-into-variables-in-tmplr/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Mon, 03 Apr 2023 23:34:00 +0000</pubDate>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[javascript]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3539</guid>

					<description><![CDATA[tmplr is a versatile templating tool that lets you quickly create a repository from a template customized for your needs. By running a recipe it can substitute placeholder variables with your own values making the repository truly your own. For example it can update package.json with dynamic values from git context. It can do much [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://github.com/loreanvictor/tmplr" rel="noopener" target="_blank">tmplr</a> is a versatile templating tool that lets you quickly create a repository from a template customized for your needs. By running <a href="https://github.com/loreanvictor/tmplr#template-recipes" rel="noopener" target="_blank">a recipe</a> it can substitute placeholder variables with your own values making the repository truly your own. For example it can update package.json with dynamic values from git context. It can do much more, put for the purpose of this post we will concentrate on literal values in code files.</p>
<p>Let’s say you have a JavaScript code that performs some AWS SDK/CDK actions, and it requires AWS account IDs – for dev and prod deployment. In a regular repo you would define <code>config.js</code> something like this:</p>
<pre class="brush: jscript; title: ; notranslate">const awsAccounts = {
   dev: &#039;1234567890&#039;,
   prod: &#039;0987654321&#039;
}</pre>
<p>But if we’re building a template repo – we don’t want to hardcode those values. Instead we could use <code>tmplr</code> placeholder variables:</p>
<pre class="brush: jscript; title: ; notranslate">const awsAccounts = {
   dev: &#039;{{ tmplr.dev_account_id }}&#039;,
   prod: &#039;{{ tmplr.prod_account_id }}&#039;
}</pre>
<p>Save this file as <code>config.tmplr.js</code> – it becomes our template. A user of your template then can create a <code>tmplr</code> recipe:<span id="more-3539"></span></p>
<pre class="brush: xml; title: ; notranslate">steps:
  - read: dev_account_id
    eval: &#039;1234567890&#039;
  - read: prod_account_id
    eval: &#039;0987654321&#039;
  - copy: config.tmplr.js
    to: config.js</pre>
<p>then save this it into <code>.tmplr.yml</code> file into repo root, and run tmplr cli command – it will create <code>config.js</code> file from <code>config.tmplr.js</code> substituting variables for values from the recipe.</p>
<p>It works great, but there is a problem.</p>
<p>If you’re actively developing the template – live code file (in our case <code>config.js</code>) that your template is based on will be changing – code will be added/removed/refactored. This means that the <code>config.tmplr.js</code> template will become outdated, will be out of sync with <code>config.js</code>. We need a way to update our template with the latest code. Luckily there is a way to automate this, so you don’t have to manually copy pieces of code from the live file to the template.</p>
<p>Basically we need an opposite of what <code>tmplr</code> does – take the file that has literal values, replace them with placeholder variables, and save it as a template. As far as I know as of the time of this post <code>tmplr</code> doesn’t have this capability, so I put together a small JavaScript helper (you will need to install one dependency <code>'js-yaml'</code> to handle YAML files):</p>
<pre class="brush: jscript; title: ; notranslate">const yaml = require(&#039;js-yaml&#039;);
const fs = require(&#039;fs&#039;);
 
const tmplr = yaml.load(
      fs.readFileSync(`.tmplr.yml`, &#039;utf-8&#039;)).steps;
const files = tmplr.filter((step) =&gt; &#039;copy&#039; in step);
const variables = tmplr.filter((step) =&gt; &#039;read&#039; in step);
 
for (let file of files) {
  let fileContent = fs.readFileSync(file.to, &#039;utf-8&#039;);
 
  for (let variable of variables) {
    fileContent =
      fileContent.replaceAll(variable.eval, 
         `{{ tmplr.${variable.read} }}`);
  }
 
  fs.writeFileSync(
       file.copy, fileContent, { encoding: &#039;utf8&#039;, flag: &#039;w&#039; });
  console.log(`<img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Templated: &quot;${file.to}&quot; -&gt; &quot;${file.copy}&quot;`);
}</pre>
<p>This code uses the same <code>.tmplr.yml</code> file we created earlier for <code>tmplr</code> (<em>Lines 04-07</em> read and parse the YAML file, and extract variable/value, copy_from/copy_to pairs from it). It then loops thru the files, loading live code file in every iteration (Lines 09-10). For each file it loops thru variables/values, and if it finds literal value – replaces it with placeholder variable (<em>Lines 12-16</em>). And finally it writes resulting template into template file (<em>Lines 18-19</em>)</p>
<p>You can run this script manually, or – if you’re using <code>VS Code</code> using <a href="https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave" rel="noopener" target="_blank">Run On Save</a> extension, or even in a GitHub workflow on push of a changed file. In any case your template files will be always in sync with your code files.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2023/04/03/reverse-values-into-variables-in-tmplr/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3539</post-id>	</item>
		<item>
		<title>Synthesize only stacks you need in CDK</title>
		<link>https://codecorner.galanter.net/2023/03/30/synthesize-only-stacks-you-need-in-cdk/</link>
					<comments>https://codecorner.galanter.net/2023/03/30/synthesize-only-stacks-you-need-in-cdk/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Thu, 30 Mar 2023 23:47:49 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[CDK]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3546</guid>

					<description><![CDATA[CDK documentation states that if you supply stack name(s) to CLI commands like cdk synth, cdk list, or cdk deploy – it will synthesize only the stacks you requested. But in reality this is not the case, CDK will always synthesize all stacks – and it may lead to unintended consequences. Let’s say you have [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>CDK documentation states that if you supply stack name(s) to CLI commands like <code>cdk synth</code>, <code>cdk list</code>, or <code>cdk deploy</code> – it will synthesize only the stacks you requested. But in reality this is not the case, CDK will always synthesize all stacks – and it may lead to unintended consequences.</p>
<p>Let’s say you have following stack declarations in your <code>lib/my-stacks.ts</code> code:</p>
<pre class="brush: jscript; title: ; notranslate">import * as cdk from &#039;aws-cdk-lib&#039;;
 
export class Stack1 extends cdk.Stack {};
export class Stack2 extends cdk.Stack {};
export class AnotherStack extends cdk.Stack {};
export class YerAnotherStack extends cdk.Stack {};</pre>
<p>And in your app’s entry point <code>bin/my-stacks.ts</code> you instantiate those stacks:</p>
<pre class="brush: jscript; title: ; notranslate">import * as cdk from &#039;aws-cdk-lib&#039;;
import * as stacks from &#039;../lib/my-stacks&#039;
 
const app = new cdk.App();
 
new stacks.Stack1(app, &quot;Stack1&quot;);
new stacks.Stack2(app, &quot;Stack2&quot;);
new stacks.AnotherStack(app, &quot;AnotherStack&quot;);
new stacks.YerAnotherStack(app, &quot;YetAnotherStack&quot;);</pre>
<p>And then issue a CLI command to synthesize stacks, but you only want to synthesize “Stack1” and “AnotherStack”:<span id="more-3546"></span></p>
<pre class="brush: bash; gutter: false; title: ; notranslate">cdk synth Stack1 AnotherStack</pre>
<p>The command output would have you believe that only 2 stacks were synthesized:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">Successfully synthesized to ./cdk.out
Supply a stack id (AnotherStack, Stack1) to display its template.</pre>
<p>But if you actually look into cdk.out folder – you will see <em>all</em> stack templates were synthesized:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">AnotherStack.assets.json        Stack2.assets.json
AnotherStack.template.json      Stack2.template.json
Stack1.assets.json              YetAnotherStack.assets.json
Stack1.template.json            YetAnotherStack.template.json</pre>
<p>CDK will synthesize templates for all stacks you instantiate no matter what stack names you pass – the names will only be used to filter the results. In order to synthesize only the stacks you request – only classes for the stacks you request must be instantiated. We can pass the stack names to a CDK command using context via option <code>--context</code> or its shorter form <code>-c</code>:</p>
<pre class="brush: bash; gutter: false; title: ; notranslate">cdk synth -c stacks=Stack1,AnotherStack</pre>
<p>And this is how we can interpret this in our entry point code:</p>
<pre class="brush: jscript; title: ; notranslate">import * as cdk from &#039;aws-cdk-lib&#039;;
import * as stacks from &#039;../lib/my-stacks&#039;
 
const app = new cdk.App();
const myStacks: string = app.node.tryGetContext(&#039;stacks&#039;);
 
Object.entries(stacks).forEach((&#x5B;stackName, stack]) =&gt; {
  if (!myStacks || myStacks.split(&#039;,&#039;).includes(stackName)) {
    new stack(app, stackName)
  }
})</pre>
<p>Here on <em>Line 05</em> we read the context parameter we passed in the command line. And on <em>Lines 07-11</em> we loop thru imported stacks. If stack name matches one of the names we passed as a parameter – only then it will be instantiated. If the <code>-c stacks</code> parameter isn’t passed – the value will be undefined – and all stacks will be instantiated and therefore synthesized.</p>
<p>Now, if you issue the command (don’t forget to clear previous content of <code>cdk.out</code>):</p>
<pre class="brush: bash; gutter: false; title: ; notranslate">cdk synth -c stacks=Stack1,AnotherStack</pre>
<p>The output will be the same as before:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">Successfully synthesized to ./cdk.out
Supply a stack id (AnotherStack, Stack1) to display its template.</pre>
<p>But if you look into cdk.out directory – you will only see requested stacks synthesized:</p>
<pre class="brush: plain; title: ; notranslate">]AnotherStack.assets.json
AnotherStack.template.json
Stack1.assets.json
Stack1.template.json</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2023/03/30/synthesize-only-stacks-you-need-in-cdk/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3546</post-id>	</item>
		<item>
		<title>Enable ActivityPub Plugin on Bitnami WordPress instance with LetsEncrypt</title>
		<link>https://codecorner.galanter.net/2023/03/24/enable-activitypub-plugin-on-bitnami-wordpress-instance-with-letsencrypt/</link>
					<comments>https://codecorner.galanter.net/2023/03/24/enable-activitypub-plugin-on-bitnami-wordpress-instance-with-letsencrypt/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Fri, 24 Mar 2023 23:05:18 +0000</pubDate>
				<category><![CDATA[Mastodon]]></category>
		<category><![CDATA[New Stuff]]></category>
		<category><![CDATA[Social]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[Fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[wordpress]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3532</guid>

					<description><![CDATA[ActivityPub plugin is a very cool piece of WordPress addition. With this plugin installed users of Mastodon and other such federated platforms that support ActivityPub can follow your blog as if it were another instance on the Fediverse. Unfortunately if you run a Bitnami instance of WordPress (for example one provided by AWS LightSail with [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://wordpress.org/plugins/activitypub/" rel="noopener" target="_blank">ActivityPub plugin</a> is a very cool piece of <a href="https://wordpress.org/" rel="noopener" target="_blank">WordPress</a> addition. With this plugin installed users of Mastodon and other such federated platforms that support ActivityPub can follow your blog as if it were another instance on the Fediverse.</p>
<p>Unfortunately if you run a <a href="https://bitnami.com/" rel="noopener" target="_blank">Bitnami</a> instance of WordPress (for example one provided by <a href="https://aws.amazon.com/lightsail/" rel="noopener" target="_blank">AWS LightSail</a> with <a href="https://letsencrypt.org/" rel="noopener" target="_blank">LetsEncrypt</a> service providing a TLS certificate for you site – you may encounter an incompatibility issue. LetsEncrypt uses <code>/.well-known/acme-challenge</code> path on your site for certificate validation, but ActivityPub plugin uses <code>/.well-known/webfinger</code> path to return relevant profile information. It conflicts with LetsEncrypt and the WebFinger path returns “404 – not found”.</p>
<p>Fortunately there is an easy fix.<span id="more-3532"></span></p>
<p>SSH to your server, locate file <code>/opt/bitnami/apps/letsencrypt/conf/httpd-app.conf</code>, and add highlighted lines to it:</p>
<pre class="brush: xml; highlight: [12,13,14,15,16]; title: ; notranslate">&lt;Directory &quot;/opt/bitnami/apps/letsencrypt/.well-known&quot;&gt;
    Options +MultiViews
    AllowOverride None
    &lt;IfVersion &lt; 2.3 &gt;
        Order allow,deny
        Allow from all
    &lt;/IfVersion&gt;
    &lt;IfVersion &gt;= 2.3&gt;
        Require all granted
    &lt;/IfVersion&gt;
     
    RewriteEngine On
    RewriteBase /.well-known/
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /index.php?well_known_path=$1 &#x5B;QSA,L]

&lt;/Directory&gt;</pre>
<p>After that Webfinger starts returning correct profile information, and your site can be found on the Fediverse.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2023/03/24/enable-activitypub-plugin-on-bitnami-wordpress-instance-with-letsencrypt/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3532</post-id>	</item>
		<item>
		<title>Wait for AWS region to become available</title>
		<link>https://codecorner.galanter.net/2023/03/11/wait-for-aws-region-to-become-available/</link>
					<comments>https://codecorner.galanter.net/2023/03/11/wait-for-aws-region-to-become-available/#respond</comments>
		
		<dc:creator><![CDATA[Comfortably Numb]]></dc:creator>
		<pubDate>Sat, 11 Mar 2023 22:57:20 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[solution]]></category>
		<guid isPermaLink="false">https://codecorner.galanter.net/?p=3527</guid>

					<description><![CDATA[AWS CLI has a nifty useful command to enable/opt-in a region on your account – account enable-region e.g. aws account enable-region --region-name af-south-1 There is a caveat though – this command only begins to enable the region. The process could take a while, but the command exits right away. But what if you need do [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://aws.amazon.com/cli" rel="noopener" target="_blank">AWS CLI</a> has a nifty useful command to enable/opt-in a region on your account – <a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/account/enable-region.html" rel="noopener" target="_blank">account enable-region</a> e.g.</p>
<pre class="brush: bash; title: ; notranslate">aws account enable-region --region-name af-south-1</pre>
<p>There is a caveat though – this command only begins to enable the region. The process could take a while, but the command exits right away. But what if you need do something with the region when it becomes available? Say you’re running a script and you need to bootstrap the region when its enabled. You need some way to wait for the enablement to finish. Luckily there is another AWS CLI command that lets you check the status of the region – <a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/describe-regions.html" rel="noopener" target="_blank">ec2 describe-regions</a>, e.g.</p>
<pre class="brush: bash; title: ; notranslate">aws ec2 describe-regions --region-names af-south-1</pre>
<p>One of the properties it return is whether the region is enabled/opted-in or not. Combining this with a little of bash magic – we can come up with a waiting routine:</p>
<pre class="brush: bash; title: ; notranslate">aws account enable-region --region-name af-south-state=not-opted-in
until &#x5B; &quot;$state&quot; = &quot;opted-in&quot; ]
do
   echo Waiting for af-south-1...
   sleep 5
   state=$(aws ec2 describe-regions --region-names af-south-1 --query &quot;Regions&#x5B;0].OptInStatus&quot; --output text)
done</pre>
<p>Here on the <em>Line 1</em> we execute command that initiates enabling region. And the rest is a loop: wait 5 seconds, and check the status of the region. Loop exits when the status becomes “opted-in”. At this point we know that the region has been enabled, and can proceed with using it.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecorner.galanter.net/2023/03/11/wait-for-aws-region-to-become-available/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3527</post-id>	</item>
	</channel>
</rss>
