<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Robert Vandenberg Huang</title>
    <description>The latest articles on DEV Community by Robert Vandenberg Huang (@rvhuang).</description>
    <link>https://dev.to/rvhuang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F48721%2F4ebbcb7c-a733-4c02-90b3-91392b8a603a.jpeg</url>
      <title>DEV Community: Robert Vandenberg Huang</title>
      <link>https://dev.to/rvhuang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rvhuang"/>
    <language>en</language>
    <item>
      <title>Be careful of optional parameters in your APIs</title>
      <dc:creator>Robert Vandenberg Huang</dc:creator>
      <pubDate>Sun, 11 Jul 2021 08:52:59 +0000</pubDate>
      <link>https://dev.to/rvhuang/be-careful-of-optional-parameters-in-your-apis-2mb5</link>
      <guid>https://dev.to/rvhuang/be-careful-of-optional-parameters-in-your-apis-2mb5</guid>
      <description>&lt;p&gt;Recently, a team in my company has been facing a chaotic situation caused by bad API designs from a payment service, giving me an idea to share the story here. Most of the details in the article have been modified due to sensitivities but the big picture is not affected. &lt;/p&gt;

&lt;p&gt;The background, to put it roughly, is that the payment service provides a web API that supports offline transactions. After a customer has completed an offline transaction (for example, using a credit/debit card to transfer money), the customer can enter its reference ID on the merchant's website. The website then calls the offline transaction API to verify the reference ID. If the ID is valid, the website increases the user's balance.&lt;/p&gt;

&lt;p&gt;Everything was perfect until one day the payment service added two parameters to the API without releasing change notes to callers. Simply put, the new parameters allow its callers to specify the ID of a merchant's subsidiary (if any) so the verification will fail if the reference ID belongs to another subsidiary. In order to maintain backward compatibility, both parameters are optional.&lt;/p&gt;

&lt;p&gt;This was where the catastrophe started. For some reason, after the two optional parameters were added, malicious users found that &lt;strong&gt;a reference ID of the transaction that does not belong to the merchant can pass the verification&lt;/strong&gt;. It is like the reference ID of an offline transaction to Steam can be used to increase the balance on Epic Games Store. This caused a huge loss to victim merchants because they actually did not receive the money, yet the API misled them to increase a malicious user's balance. &lt;/p&gt;

&lt;p&gt;After a great effort of investigation and lengthy communication with the provider, the team found that the provider's API works if the two parameters are present. However, the damage has been done.  &lt;/p&gt;

&lt;p&gt;So two morals of the story:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When adding new optional parameters to an old API, &lt;strong&gt;testing the new parameters working as expected is equally important as they are omitted&lt;/strong&gt;. And yet the latter is usually not covered. &lt;/li&gt;
&lt;li&gt;Sometimes, a breaking change is acceptable when maintaining backward compatibility becomes expensive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thank you for reading this.&lt;/p&gt;

</description>
      <category>api</category>
    </item>
    <item>
      <title>Fun interview question: How to update Saint Quartz stones with one line SQL statement in FGO?</title>
      <dc:creator>Robert Vandenberg Huang</dc:creator>
      <pubDate>Sat, 22 Feb 2020 16:37:17 +0000</pubDate>
      <link>https://dev.to/rvhuang/fun-interview-question-how-to-update-saint-quartz-stones-with-one-line-sql-statement-in-fgo-5e13</link>
      <guid>https://dev.to/rvhuang/fun-interview-question-how-to-update-saint-quartz-stones-with-one-line-sql-statement-in-fgo-5e13</guid>
      <description>&lt;p&gt;So recently I have been obsessed with the mobile game &lt;a href="https://fate-go.us/"&gt;Fate/Grand Zero&lt;/a&gt;(FGO) and spent lots of time probing into every detail of the game. One particular detail that jumps out right in front of me is how the &lt;em&gt;Summoning&lt;/em&gt; works. It also becomes one of my fun interview questions that challenge candidates' SQL skills. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F9fnUgKZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tpmztuljygl03ciwr33e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F9fnUgKZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tpmztuljygl03ciwr33e.png" alt="Time to summon a spirit!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In FGO, a player holds two types of "Saint Quartz stones". The first type is Free Saint Quartz which is usually awarded for achievements such as finishing certain levels or daily logging-in to the game. The second type is Paid Saint Quartz which is, as the name has implied, purchased by the player. The player can either pays 3 stones to summon one time or 30 stones, for higher probabilities of getting rare characters, to summon 10 times. The rules of how they are spent are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Free Saint Quartz stones will be spent first. &lt;/li&gt;
&lt;li&gt;If Free Saint Quartz stones are not enough for summoning, Paid Saint Quartz stones will be spent then for the remaining part.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, if the player holds 40 Free Saint Quartz stones and 20 Paid Saint Quartz stones, after 10-time summoning, the numbers of stones are 10 and 20 respectively. And if the player holds 15 free and 20 paid stones, the results are 0 and 5.&lt;/p&gt;

&lt;p&gt;The interview question is: given that a table &lt;code&gt;player&lt;/code&gt; contains two columns &lt;code&gt;free_sant_quartz&lt;/code&gt; and &lt;code&gt;paid_sant_quartz&lt;/code&gt;, both in integer, &lt;strong&gt;write a SQL statement according to the rules with only one &lt;code&gt;UPDATE&lt;/code&gt; statement&lt;/strong&gt;. (Assuming that the DB system is MySQL or MariaDB)&lt;/p&gt;

&lt;p&gt;Before we get to the real &lt;code&gt;UPDATE&lt;/code&gt; answer, let us take a look at the &lt;code&gt;SELECT&lt;/code&gt; statement that allows the player to preview how many remaining stones there would be after 10-time summoning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
CREATE TABLE IF NOT EXISTS players (
  id int(6) NOT NULL,
  free_sant_quartz int(3) NOT NULL,
  paid_sant_quartz int(3) NOT NULL,
  PRIMARY KEY (id)
);
*/&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; 
&lt;span class="n"&gt;GREATEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;free_sant_quartz&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;free_sant_quartz_remaining&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="n"&gt;LEAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;free_sant_quartz&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;paid_sant_quartz&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;paid_sant_quartz_remaining&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- The WHERE clause is omitted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;GREATEST()&lt;/code&gt; and &lt;code&gt;LEAST()&lt;/code&gt; are two very useful functions to solve the problem as they simulate the "priorities" between &lt;code&gt;free_sant_quartz&lt;/code&gt; and &lt;code&gt;paid_sant_quartz&lt;/code&gt;. If the player holds 30 Free Sant Quartz stones or more, the result of &lt;code&gt;GREATEST(free_sant_quartz - 30, 0)&lt;/code&gt; will simply be the remaining Free Sant Quartz stones and the clause &lt;code&gt;LEAST(free_sant_quartz - 30, 0)&lt;/code&gt; produces zero. And if the player holds less than 30, the former produces zero and the latter produces a negative number so &lt;code&gt;paid_sant_quartz&lt;/code&gt; gets deducted for the remaining part.  &lt;/p&gt;

&lt;p&gt;Once we get the &lt;code&gt;SELECT&lt;/code&gt; statement, it becomes easy to come up with an &lt;code&gt;UPDATE&lt;/code&gt; statement for the problem. The only catch is that we need a temporary variable for the deducted &lt;code&gt;free_sant_quartz&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; 
&lt;span class="n"&gt;free_sant_quartz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GREATEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;free_sant_quartz&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;paid_sant_quartz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LEAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;paid_sant_quartz&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;free_sant_quartz&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;paid_sant_quartz&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;player_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- The player_id is omitted. &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Case closed.&lt;/p&gt;

&lt;p&gt;You can play with the complete example at &lt;a href="https://www.db-fiddle.com/f/szE1s5yyUVMsBsQvqU7cvU/0"&gt;DB Fiddle&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>mysql</category>
      <category>interview</category>
    </item>
  </channel>
</rss>
