<?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: Jesus Gonzalez</title>
    <description>The latest articles on DEV Community by Jesus Gonzalez (@gonzalezlrjesus).</description>
    <link>https://dev.to/gonzalezlrjesus</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%2F348237%2Fa46a9345-5e67-40bd-977b-9e49c504e28a.jpeg</url>
      <title>DEV Community: Jesus Gonzalez</title>
      <link>https://dev.to/gonzalezlrjesus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gonzalezlrjesus"/>
    <language>en</language>
    <item>
      <title>Create OHLC Candles with Python (Pandas + Plotly)</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Fri, 10 Oct 2025 15:35:48 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/create-ohlc-candles-with-python-pandas-plotly-kal</link>
      <guid>https://dev.to/gonzalezlrjesus/create-ohlc-candles-with-python-pandas-plotly-kal</guid>
      <description>&lt;p&gt;Many market datasets come as raw trade ticks (timestamp, price, size). To analyze trends we first convert ticks into OHLC candles. This tiny Python script builds 1‑minute candles with Pandas and plots them with Plotly.&lt;/p&gt;

&lt;p&gt;How it works&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate trade ticks at 1‑second resolution&lt;/li&gt;
&lt;li&gt;Resample to a 1‑minute interval and compute open, high, low, close, and volume&lt;/li&gt;
&lt;li&gt;Plot a candlestick chart&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open in Colab (public): paste the code below into &lt;a href="https://colab.new" rel="noopener noreferrer"&gt;https://colab.new&lt;/a&gt; and run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plotly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph_objects&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;

&lt;span class="n"&gt;FREQ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="c1"&gt;# interval and number of candles
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2025-01-01 10:00:00&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UTC&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;periods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MINS&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UTC&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&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="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;cumsum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;qty&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;qty&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;candles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FREQ&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FREQ&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FREQ&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;close&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FREQ&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vol&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;qty&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FREQ&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;dropna&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;fig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Candlestick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;close&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plotly_dark&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;420&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MINS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;m Candles (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FREQ&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>web3</category>
      <category>data</category>
      <category>finance</category>
    </item>
    <item>
      <title>Exploring Random Number Generation with Different Distributions in Python</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Sat, 28 Oct 2023 07:44:18 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/exploring-random-number-generation-with-different-distributions-in-python-29op</link>
      <guid>https://dev.to/gonzalezlrjesus/exploring-random-number-generation-with-different-distributions-in-python-29op</guid>
      <description>&lt;p&gt;Generating random numbers with different distributions is a fascinating topic that has applications in many areas, from computer simulations to machine learning.&lt;br&gt;
 &lt;br&gt;
&lt;strong&gt;What are distributions?&lt;/strong&gt;&lt;br&gt;
In statistics, a distribution describes the likelihood of an event occurring. In the context of random numbers, a distribution describes how likely each number is to be selected.&lt;/p&gt;

&lt;p&gt;For this article i generated random numbers with 3 different distributions: Uniform, Normal and Exponential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uniform Distribution&lt;/strong&gt;&lt;br&gt;
The uniform distribution is the simplest of all distributions. In a uniform distribution, all numbers within a specific range have the same likelihood of being selected. It is useful when you want all possible outcomes to be equally likely.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;np.random.uniform(low=1, high=1000, size=10000)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In real life, the uniform distribution is often used in computer simulations (e.g. &lt;a href="https://en.wikipedia.org/wiki/Monte_Carlo_method" rel="noopener noreferrer"&gt;Monte Carlo method&lt;/a&gt;) and games where you want all outcomes to be equally likely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normal Distribution&lt;/strong&gt;&lt;br&gt;
Also known as the Gaussian distribution, is probably the most well-known of all distributions. In a normal distribution, numbers near the middle of the range have a higher likelihood of being selected, while numbers at the ends of the range have a lower likelihood.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;random.normal(loc=500, scale=100, size=10000)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In real life, the normal distribution is used in a wide variety of fields including statistics, physics and engineering. It’s useful when you’re working with data that clusters around an average.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exponential Distribution&lt;/strong&gt;&lt;br&gt;
The exponential distribution is a bit different. In an exponential distribution, smaller numbers have a higher likelihood of being selected and this likelihood decreases exponentially as the numbers increase.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;random.exponential(scale=100, size=10000)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In real life, the exponential distribution is often used in queueing theory and in modeling the lifetime of certain products and materials.&lt;/p&gt;

&lt;p&gt;This graphs represent the results and allow see which numbers are more likehood to be selected in the different distributions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdpd2u0dzwvrzb9t4799.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdpd2u0dzwvrzb9t4799.png" alt="Results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Uniform Distribution shows an almost constant bar across the full range (0-1000), indicating that each number within this range has roughly equal probability of being selected. &lt;/p&gt;

&lt;p&gt;The Normal Distribution shows a “bell curve”, indicating that numbers near the middle of the range (around 500) have a higher probability of being selected while numbers at the ends of the range have a lower probability.. &lt;/p&gt;

&lt;p&gt;The Exponential Distribution shows a rapidly decreasing curve, indicating that smaller numbers have a higher probability of being selected and this probability decreases exponentially as the numbers increase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import numpy as np
import matplotlib.pyplot as plt

# Define the number of samples and the range of the random numbers
num_samples = 10000
low, high = 1, 1000

# Generate random numbers with different distributions
uniform_dist = np.random.uniform(low=low, high=high, size=num_samples)
normal_dist = np.random.normal(loc=high/2, scale=(high-low)/10, size=num_samples)
exponential_dist = np.random.exponential(scale=(high-low)/10, size=num_samples) 

plt_uniform_dist = np.clip(uniform_dist, low, high)
plt_normal_dist = np.clip(normal_dist, low, high)
plt_exponential_dist = np.clip(exponential_dist, low, high)

plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
plt.hist(plt_uniform_dist, bins=50, color='c')
plt.title('Uniform Distribution')

plt.subplot(1, 3, 2)
plt.hist(plt_normal_dist, bins=50, color='m')
plt.title('Normal Distribution')

plt.subplot(1, 3, 3)
plt.hist(plt_exponential_dist, bins=50, color='y')
plt.title('Exponential Distribution')

plt.tight_layout()
plt.show()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>computerscience</category>
      <category>math</category>
      <category>statistics</category>
    </item>
    <item>
      <title>Reverse the elements of a Singly Linked List using a Stack</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Mon, 01 Nov 2021 15:49:38 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/reverse-the-elements-of-a-singly-linked-list-using-a-stack-58ne</link>
      <guid>https://dev.to/gonzalezlrjesus/reverse-the-elements-of-a-singly-linked-list-using-a-stack-58ne</guid>
      <description>&lt;p&gt;Reverse the elements of a Singly Linked List using a Stack as an approach&lt;/p&gt;

&lt;p&gt;It is not the most efficient way to solve this problem but it is good to know other ways to solve it by helping us with other data structures&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.golang.org/p/Obbq73zUrD2"&gt;https://play.golang.org/p/Obbq73zUrD2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zb81-8MK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/psl1ft3kifjucgpduor4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zb81-8MK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/psl1ft3kifjucgpduor4.png" alt="Image description" width="791" height="2234"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>The Go Playground - standard input</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Tue, 18 May 2021 11:15:42 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/the-go-playground-standard-input-50ff</link>
      <guid>https://dev.to/gonzalezlrjesus/the-go-playground-standard-input-50ff</guid>
      <description>&lt;p&gt;📌 When you use The Go Playground, you cannot make interactive programs like this one because it does not allow the input of values by standard input interactively:&lt;br&gt;
&lt;a href="https://play.golang.org/p/lvkXx7aB-XV"&gt;https://play.golang.org/p/lvkXx7aB-XV&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the program will terminate and will not allow you to enter any value.&lt;/p&gt;

&lt;p&gt;📌 Go playground can write on standard output and standard error. Anyway can still receive a value from standard input not interactively.&lt;br&gt;
&lt;a href="https://play.golang.org/p/r-uvWSb8UiV"&gt;https://play.golang.org/p/r-uvWSb8UiV&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📌 it operates on an in-memory file system, which will allow assigning a value that stores it in memory as standard input. &lt;a href="https://github.com/orbs-network/go-nacl-experiment"&gt;https://github.com/orbs-network/go-nacl-experiment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📝 Playground documentation: Inside the Go Playground &lt;a href="https://blog.golang.org/playground"&gt;https://blog.golang.org/playground&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Line filter program that removes digits</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Fri, 14 May 2021 15:13:53 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/line-filter-program-that-removes-digits-3005</link>
      <guid>https://dev.to/gonzalezlrjesus/line-filter-program-that-removes-digits-3005</guid>
      <description>&lt;p&gt;📌 A filter is a program that gets data from standard input, performs some manipulation of the data, and then writes its results to standard output.&lt;br&gt;
Auxiliary input may come from command line flags or configuration files, while auxiliary output may go to standard error.&lt;/p&gt;

&lt;p&gt;📌 Most common functions:&lt;/p&gt;

&lt;p&gt;▶️ Convert data to a different format&lt;br&gt;
 ▶️ Add printer instructions to the beginning of each document&lt;br&gt;
 ▶️ Add, delete, or modify data in a document&lt;/p&gt;

&lt;p&gt;📌 Unix has many filter programs, most common are: cat, cut, grep,&lt;/p&gt;

&lt;p&gt;Go Playground: &lt;a href="https://play.golang.org/p/s8FwQRkZXdr"&gt;https://play.golang.org/p/s8FwQRkZXdr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VkqzKUMV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kaoyx8djfnvg4x8y7jov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VkqzKUMV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kaoyx8djfnvg4x8y7jov.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>linux</category>
      <category>unix</category>
    </item>
    <item>
      <title>Enviar correos por GMAIL utilizando el paquete SMTP de GO</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Mon, 29 Jun 2020 23:56:56 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/enviar-correos-por-gmail-utilizando-el-paquete-smtp-de-go-1cho</link>
      <guid>https://dev.to/gonzalezlrjesus/enviar-correos-por-gmail-utilizando-el-paquete-smtp-de-go-1cho</guid>
      <description>&lt;p&gt;El paquete implementa &lt;a href="https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol"&gt;SMTP&lt;/a&gt; (Simple Mail Transfer Protocol), el cual es un protocolo de comunicación para transmisión&lt;br&gt;
de correos electrónicos. En su documentación se encuentra un &lt;a href="https://play.golang.org/p/SQ12fOj5FfI"&gt;ejemplo&lt;/a&gt;, que se adaptará a un formato listo para producción, aplicando testing &lt;em&gt;unitarios&lt;/em&gt;, &lt;em&gt;integración&lt;/em&gt; que comprobarán los componentes en conjunto y de &lt;em&gt;cobertura&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Lo primero es crearse un directorio, con 2 archivos de extensión &lt;strong&gt;.go&lt;/strong&gt;, uno para el código normal de GO, otro para los testings. Debería quedar como la imagen:&lt;/p&gt;

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

&lt;p&gt;El nombre del archivo de testing debe cumplir el patrón especifico exigido por el lenguaje, de esta manera se ejecutaran solamente aquellos dedicados a esa función, el cual es el siguiente: &lt;strong&gt;&lt;strong&gt;tunombrepreferido_test.go&lt;/strong&gt;&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;Se empezará el desarrollo con un primer test a lo mas resaltante del articulo: &lt;strong&gt;Enviar Correos Electrónicos&lt;/strong&gt; .&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    package email

    import (
            "fmt"
            "net/smtp"
            "reflect"
            "strings"
            "testing"
    )

    func TestSendEmail(t *testing.T) {
            assertThat := func(assumption, addr string, auth smtp.Auth, from string, to []string, msg []byte, expectedOutput string) {
                    actualOutput := sendEmail(addr, auth, from, to, msg)
                    if actualOutput != expectedOutput {
                            t.Errorf("Error! Outputs are not equal Actual %v Expected %v", actualOutput, expectedOutput)
                    }
            }
            addr := "smtp.gmail.com:587"

            auth := smtp.PlainAuth("", "your-email@gmail.com", "your-password", "smtp.gmail.com")

            from := "your-email@gmail.com"

            to := []string{"to-email-1@gmail.com", "to-email-2@hotmail.com"}
            concatenate := strings.Join(to, ", ")
            toMsg := fmt.Sprintf("To: %v\r\n", concatenate)

            subject := "Test Subject"
            subjectMsg := fmt.Sprintf("Subject: %v\r\n", subject)

            body := "Example body."
            bodyMsg := fmt.Sprintf("%v\r\n", body)

            msg := []byte(toMsg + subjectMsg + "\r\n" + bodyMsg)
            assertThat("Should send email from user's GMAIL account to any email and return a message of successful sent", addr, auth, from, to, msg, "email sent successfully")
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Las funciones de igual manera se rigen por un respectivo patrón &lt;strong&gt;TestXxx( t *testing.T)&lt;/strong&gt; donde &lt;em&gt;Xxx&lt;/em&gt; es el nombre de cada una.&lt;/p&gt;

&lt;p&gt;El testing de arriba se aprobará si el correo fue enviado y retorna el mensaje de exito esperado, si devuelve un mensaje diferente indicara que ocurrio un error y no se aprobó, la funcion a evaluar es &lt;strong&gt;sendEmail()&lt;/strong&gt;, que contendra la misión de enviar los correos electrónicos y se agregara en el archivo &lt;strong&gt;email.go&lt;/strong&gt;::&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func sendEmail(addr string, auth smtp.Auth, from string, to []string, message []byte) string {
            err := smtp.SendMail(addr, auth, from, to, message)
            if err != nil {
                    log.Printf("Error!: %s", err)
                    return "Error! sending mail"
            }
            return "email sent successfully"
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Necesita de unos parámetros para realizar el objetivo, como se le esta evaluando a ella solamente se aplicara la tecnica &lt;a href="https://en.wikipedia.org/wiki/Test_double"&gt;test double&lt;/a&gt;, se inicializaran objetos falsos simulando a valores reales para poder correr el testing con el comando: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Existen muchas probabilidades que falle al primer intento, debido a que no se ha configurado la cuenta a utilizar de GMAIL. Se necesitara modificar los siguientes aspectos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Desactivar el mecanismo de seguridad de autentificacion &lt;strong&gt;2-step&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Activar la opcion de &lt;strong&gt;permitir acceso a aplicaciones menos seguras&lt;/strong&gt;, se esta trabajando localmente, y no se tiene el certificado TLS, GMAIL rechazará la conexion si no se realiza este paso.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si ya se hicieron los cambios, los posibles errores pueden ser que las credenciales esten erroneas (el correo o la contraseña).&lt;/p&gt;

&lt;p&gt;Se ejecuta nuevamente y deberia indicar que la prueba fue aprobada y el correo en la bandeja de entrada a quien fue dirigido.&lt;br&gt;
Recuerda que GMAIL tiene un limite diario para el envio de correos, si se sobrepasa no se podria enviar mas o entrarian en la bandeja de SPAM(&lt;a href="https://support.google.com/a/answer/166852?hl=en"&gt;Limites&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A continuacion crearan tests a los objectos falsos. Los primeros evaluaran a las funciones que obtendran la address y el email desde donde se enviaran los correos, se creara un archivo &lt;strong&gt;.env&lt;/strong&gt; para almacenar variables importantes o sensibles, se podra acceder a el con la libreria &lt;a href="https://github.com/joho/godotenv"&gt;&lt;strong&gt;godotenv&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func TestGetAddressSMTP(t *testing.T) {
            assertThat := func(assumption, expectedAddress string) {
                    actualAddress := getAddressSMTP()
                    if actualAddress != expectedAddress {
                            t.Errorf("Error! Outputs are not equal Actual %v Expected %v", actualAddress, expectedAddress)
                    }
            }
            assertThat("Should get from a environment variable the smtp address", "smtp.gmail.com:587")
    }

    func TestGetEmailFrom(t *testing.T) {
            assertThat := func(assumption, expectedEmail string) {
                    actualEmail := getEmailFrom()
                    if actualEmail != expectedEmail {
                            t.Errorf("Error! Outputs are not equal Actual %v Expected %v", actualEmail, expectedEmail)
                    }
            }
            assertThat("Should return the email from where the messages will be sent.", "your-email@gmail.com")
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Las funciones en el &lt;strong&gt;email.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func getAddressSMTP() string {
            return os.Getenv("address")
    }

    func getEmailFrom() string {
            return os.Getenv("username")
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Y variables en &lt;strong&gt;.env&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    address=smtp.gmail.com:587
    username=your-email@gmail.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Se ejecutara el comando nuevamente:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Los nuevos test deberian ser aprobados porque los valores esperados son iguales a los obtenidos.&lt;/p&gt;

&lt;p&gt;Ahora se evaluara la funcion encargada de la autentificacion de las credenciales de la cuenta a utilizar:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func TestPlainAuth(t *testing.T) {
            assertThat := func(assumption string, expectedAuth smtp.Auth) {
                    actualAuth := plainAuth()
                    if !reflect.DeepEqual(actualAuth, expectedAuth) {
                            t.Errorf("Error! Outputs are not equal Actual %v Expected %v", actualAuth, expectedAuth)
                    }
            }
            expectedAuth := smtp.PlainAuth("", "your-email@gmail.com", "your-password", "smtp.gmail.com")
            assertThat("Should returns an Auth that implements the PLAIN authentication mechanism", expectedAuth)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;El paquete de SMTP contiene 2 metodos de autenficacion el utilizado en este articulo &lt;strong&gt;PlainAuth&lt;/strong&gt; y &lt;strong&gt;CRAMMD5Auth&lt;/strong&gt;, el ultimo es un poco mas seguro porque implementa un mecanismo de seguridad pero lamentablemente no es soportado por GMAIL.&lt;/p&gt;

&lt;p&gt;En el archivo &lt;strong&gt;email.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func plainAuth() smtp.Auth {
            return smtp.PlainAuth(os.Getenv("identity"), os.Getenv("username"), os.Getenv("password"), os.Getenv("host"))
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Utiliza mas variables almacenadas en &lt;strong&gt;.env&lt;/strong&gt;, aqui sobresale lo importante que es tener datos tan sensibles como la contraseña del correo en un archivo que no debe subirse al repositorio y como una constante en el código directamente.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    address=smtp.gmail.com:587
    identity=""
    username=your-email@gmail.com
    password=your-password
    host=smtp.gmail.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Se vuelve a ejecutar el comando para asegurarse que la funcion retorna un objeto Auth como el esperado.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;De los ultimos objetos falsos que se crearon fue el de &lt;strong&gt;msg&lt;/strong&gt;, el cual contiene los correos a quienes va dirigido, el tema y el cuerpo del mensaje.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func TestGenerateMessage(t *testing.T) {
            assertThat := func(assumption string, emailList []string, subject string, body string, expectedMsg []byte) {
                    actualMsg := joinMessageStructure(emailList, subject, body)
                    if !reflect.DeepEqual(actualMsg, expectedMsg) {
                            t.Errorf("Error! Outputs are not equal Actual %v Expected %v", actualMsg, expectedMsg)
                    }
            }
            to := []string{"to-email-1@gmail.com", "to-email-2@hotmail.com"}
            concatenate := strings.Join(to, ", ")
            toMsg := fmt.Sprintf("To: %v\r\n", concatenate)

            subject := "Test Subject"
            subjectMsg := fmt.Sprintf("Subject: %v\r\n", subject)

            body := "Example body."
            bodyMsg := fmt.Sprintf("%v\r\n", body)

            expectedMsg := []byte(toMsg + subjectMsg + "\r\n" + bodyMsg)
            assertThat("Should receive the emails, subject, body and should return an array of bytes being the message", to, subject, body, expectedMsg)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;En el archivo &lt;strong&gt;email.go&lt;/strong&gt; la funcion a evaluar unira todos los datos, retornando un array de bytes que representa la estructura.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func joinMessageStructure(emailList []string, subject string, body string) []byte {
            concatenate := strings.Join(emailList, ", ")
            toMsg := fmt.Sprintf("To: %v\r\n", concatenate)

            subjectMsg := fmt.Sprintf("Subject: %v\r\n", subject)

            bodyMsg := fmt.Sprintf("%v\r\n", body)

            return []byte(toMsg + subjectMsg + "\r\n" + bodyMsg)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Se ejecuta:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ahora se evaluaran a los componentes en conjunto con un test de integracion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    func TestIntegrationSMTP(t *testing.T) {
            assertThat := func(assumption string, emailList []string, subject string, body string, expectedOutput string) {
                    address := getAddressSMTP()
                    auth := plainAuth()
                    emailFrom := getEmailFrom()
                    msg := joinMessageStructure(emailList, subject, body)
                    actualOutput := sendEmail(address, auth, emailFrom, emailList, msg)
                    if actualOutput != expectedOutput {
                            t.Errorf("Error! Outputs are not equal Actual %v Expected %v", actualOutput, expectedOutput)
                    }
            }
            to := []string{"to-email-1@gmail.com", "to-email-2@hotmail.com"}
            subject := "Test Subject"
            body := "Example body."
            assertThat("Should get data from different units and send email", to, subject, body, "email sent successfully")
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Todavia se necesita de objetos que son falsos, pero ellos en un caso real pueden ser obtenidos por funciones encargadas de capturar esos datos dependiendo de la aplicacion que se esta desarrolando Ejemplo: pueden venir de una peticion que le hicieron a una API o por input de una aplicacion CLI, etc. El proposito es que el correo sea enviado y retorne el mensaje esperado para aprobar el test.&lt;/p&gt;

&lt;p&gt;Por ultimo se hara un test coverage (cobertura de pruebas). Nos indicara el porcentaje que se cubren en el código con los tests. Se realiza con el comando:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go test -cover
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Arrojara un resultado de:&lt;/p&gt;

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

&lt;p&gt;No se cubre el 100% del código, se esta cubriendo un 81.2%. El anterior comando no nos indica cuales son las partes que no se cubren, en cambio los siguientes generaran un archivo &lt;em&gt;HTML&lt;/em&gt; con el código evaluado y coloreado:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go test -coverprofile=coverage.out
    $ go tool cover -func=coverage.out
    $ go tool cover -html=coverage.out
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MEW-fDxD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/735rbtpi407pw5qnlkdu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MEW-fDxD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/735rbtpi407pw5qnlkdu.png" alt="Cover"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Las partes en rojo, son zonas que no estan siendo evaluadas por los testing, existe un debate donde hay un grupo que sostienen que se deben cubrir el 100% del código y el otro donde solo se deben cubrir los casos necesarios, En el código del articulo los casos que faltan por cubrir son los casos se generan errores en las funciones, no son necesarios porque ya tienen su mecanismo de devolver un mensaje de error en caso que haya ocurrido algo diferente a su proposito.&lt;/p&gt;

&lt;p&gt;El código final esta mejor preparado para ir a produccion, porque cada funcion tiene una sola responsabilidad, se instauro un archivo especial para las variables sensibles por seguridad y fue testeada cada funcion, asi que ya se conoce como sera su comportamiento. &lt;/p&gt;

&lt;p&gt;Si tienen una sugerencia o encuentran un error en el código por favor hagamelo saber.&lt;/p&gt;

&lt;p&gt;Espero que les haya gustado.&lt;/p&gt;

&lt;p&gt;NOTA : Debes reemplazar la configuracion con su correo, contraseña en los distintos archivos para que funcione correctamente. &lt;/p&gt;
&lt;h4&gt;
  
  
  Link del repositorio del archivo con Go Modules para el manejo de dependencias 👇
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/gonzalezlrjesus/email-smtp"&gt;https://github.com/gonzalezlrjesus/email-smtp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Archivo final &lt;strong&gt;email.go&lt;/strong&gt;: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Archivo de testings &lt;strong&gt;email_test.go&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Archivo de variables  &lt;strong&gt;.env&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
      <category>go</category>
      <category>smtp</category>
      <category>email</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Como subir una API desarrollada con el lenguaje GO a Heroku</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Mon, 23 Mar 2020 16:15:33 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/como-subir-una-api-desarrollada-con-el-lenguaje-go-a-heroku-1dba</link>
      <guid>https://dev.to/gonzalezlrjesus/como-subir-una-api-desarrollada-con-el-lenguaje-go-a-heroku-1dba</guid>
      <description>&lt;p&gt;Cuando se está en el proceso de desarrollo de una API, se realizan pruebas internas para verificar el funcionamiento de los distintos componentes de la misma. Luego que la fase de pruebas haya concluido se debe subir a un servidor, desde donde los usuarios para la cual fue desarrollada puedan consumir sus servicios. Existen muchas empresas las cuales ofrecen alojar la API, pero cada una ofrece diferentes paquetes de funcionabilidades, dependiendo de los requerimientos se decidirá cual empresa se adapta mejor. En este tutorial se indicaran los pasos necesarios para subir la API a Heroku. &lt;/p&gt;

&lt;p&gt;La API constara de una única función, debido a que el objetivo de este tutorial  es indicar  como debe configurarse un proyecto localmente para que esté en condiciones de ser aceptado por el servicio heroku.&lt;/p&gt;

&lt;p&gt;Para el proyecto se utilizó el lenguaje &lt;a href="https://golang.org/"&gt;GO&lt;/a&gt;  versión: &lt;strong&gt;&lt;em&gt;go1.13.7&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6PRcpXcf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xeuo72nfvamg60nqqyll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6PRcpXcf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xeuo72nfvamg60nqqyll.png" alt="version-go"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;p&gt;La función &lt;strong&gt;&lt;em&gt;ListenAndServe&lt;/em&gt;&lt;/strong&gt; inicia un servidor HTTP con la dirección &lt;strong&gt;&lt;em&gt;:8000&lt;/em&gt;&lt;/strong&gt; cuando se trabaja localmente se utiliza el puerto 8000, pero cuando ya se encuentre alojada en el servicio el puerto lo decidirá Heroku por ende tiene esa validación, un único endpoint de acceso &lt;strong&gt;&lt;em&gt;/hello&lt;/em&gt;&lt;/strong&gt; por el cual se pueden hacer peticiones el cual devolverá el mensaje: &lt;strong&gt;&lt;em&gt;“Hello, World!”&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A pesar de que el proyecto no posee ninguna dependencia como: &lt;a href="https://www.gorillatoolkit.org/"&gt;Gorilla&lt;/a&gt;, &lt;a href="https://pkg.go.dev/golang.org/x/crypto/bcrypt?tab=doc"&gt;bcrypt&lt;/a&gt; , entre otras. Lo normal es que los servicios web  tengan dependencias para propósitos específicos. Para su manejo es necesario instalar una herramienta que las administrara, algunas de ellas son &lt;a href="https://github.com/tools/godep"&gt;Godep&lt;/a&gt;, &lt;a href="https://github.com/golang/dep"&gt;Dep&lt;/a&gt; y  &lt;a href="https://blog.golang.org/using-go-modules"&gt;Go Modules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El manejador seleccionado a utilizar es &lt;strong&gt;&lt;em&gt;Godep&lt;/em&gt;&lt;/strong&gt;. Para su instalación se debe cumplir los siguientes pasos:&lt;/p&gt;

&lt;p&gt;Dentro de la carpeta del proyecto se deberá abrir la consola y ejecutar el siguiente comando:&lt;br&gt;
 &lt;strong&gt;&lt;em&gt;go get -u github.com/tools/godep&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El cual lo descargara e instalara, para validar su instalación se ejecuta: &lt;strong&gt;&lt;em&gt;godep version&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Debería mostrar la versión que se instaló.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EIi1Z0Cf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3nm7xvdlw4en91vcdjt9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EIi1Z0Cf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3nm7xvdlw4en91vcdjt9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;De lo contrario mostrara un error como el siguiente: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LWc3YaT1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/03qcgt2tcrpgh1cosh7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LWc3YaT1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/03qcgt2tcrpgh1cosh7d.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para corregir ese error se necesita agregar la dirección de la carpeta donde se almacenan los archive BIN a la variable PATH de Windows.&lt;/p&gt;

&lt;p&gt;Ahora se ejecutara Godep que organizara las dependencias.&lt;br&gt;
&lt;strong&gt;&lt;em&gt;godep save ./...&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;De no mostrar ningún mensaje de error, se generaran dos carpetas: Godeps y Vendor, la API no cuenta con dependencias por lo cual no se generara la carpeta Vendor. En el caso de la carpeta Godeps se creara un archivo JSON automáticamente donde se almacenara la lista de las dependencias que se utilizan en la aplicación.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dxkN-yRK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cjl11ty56atzk993rjv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dxkN-yRK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cjl11ty56atzk993rjv2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Las nuevas carpetas deben ser almacenadas en un repositorio &lt;a href="https://git-scm.com/downloads"&gt;GIT&lt;/a&gt;. Se iniciara con el comando : &lt;strong&gt;&lt;em&gt;git init&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R4xr9Jvq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3wesnsxf2aq25gdwpnu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R4xr9Jvq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3wesnsxf2aq25gdwpnu2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se debe hacer commit a los cambios, primero el comando &lt;strong&gt;&lt;em&gt;git add&lt;/em&gt;&lt;/strong&gt; . , luego  &lt;strong&gt;&lt;em&gt;git commit –m “Agregando dependencias”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Lo siguiente es crearse una cuenta en heroku a través de este link: &lt;a href="https://signup.heroku.com/"&gt;Registrarse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y luego descargar e instalar el CLI con el cual se podrá subir el código a la recién creada cuenta de Heroku: &lt;a href="https://devcenter.heroku.com/articles/heroku-cli#download-and-install"&gt;Descargar CLI&lt;/a&gt;.&lt;br&gt;
Para verificar que la instalación del CLI fue exitosa se ingresa el comando: &lt;strong&gt;&lt;em&gt;heroku –versión&lt;/em&gt;&lt;/strong&gt; se deberá mostrar la versión de heroku instalada en la computadora.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mSdm96IC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8jheu5jgm6lqgn27jtbu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mSdm96IC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8jheu5jgm6lqgn27jtbu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para continuar con el proceso se debe iniciar sesión sincronizando el proyecto con la cuenta. La sincronización se hace con el comando  &lt;strong&gt;&lt;em&gt;heroku login&lt;/em&gt;&lt;/strong&gt;  el cual abrirá una ventana en un navegador para que se inicie sesión  en la cuenta de heroku.&lt;/p&gt;

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

&lt;p&gt;Si desea conocer si la autenticación fue exitosa se ejecuta el comando &lt;br&gt;
&lt;strong&gt;&lt;em&gt;heroku auth:token&lt;/em&gt;&lt;/strong&gt; le deberá mostrar el token asignado.&lt;/p&gt;

&lt;p&gt;Para este siguiente paso se debe crear una aplicación en Heroku la cual será la que recibirá el código y se generara automáticamente un repositorio GIT específico para la nueva aplicación en heroku. &lt;strong&gt;&lt;em&gt;heroku create [nombre-app]&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Se puede observar la recién creada aplicación en el panel de la cuenta de heroku. &lt;/p&gt;

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

&lt;p&gt;Y el nuevo repositorio GIT con &lt;strong&gt;&lt;em&gt;git remote –v&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Ahora para finalizar se debe  enviar el código a la aplicación en heroku: &lt;br&gt;
&lt;strong&gt;&lt;em&gt;git push heroku master&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Si todo fue enviado correctamente a partir desde este momento  los usuarios pueden hacer uso de la API.&lt;/p&gt;

&lt;p&gt;Nota: Ese mismo comando se debe ejecutar cada vez que se  realice un cambio en el código localmente y se desee enviar a la aplicación en heroku.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--44TkANR3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1b40ohut9jnhajo0or3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--44TkANR3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1b40ohut9jnhajo0or3a.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si desea conocer cuántas versiones tiene la aplicación en heroku, se puede observar con &lt;strong&gt;&lt;em&gt;heroku releases&lt;/em&gt;&lt;/strong&gt; y para conocer con más detalle a una específica versión se utiliza &lt;strong&gt;&lt;em&gt;heroku releases:info [versión-release]&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qYUJKW70--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3sta89nrtf4z3ev7arqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qYUJKW70--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3sta89nrtf4z3ev7arqy.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Heroku implementa una medida de protección de sus servicios, limita la cantidad de envíos de código a la aplicación que se encuentra en sus servicios de un número de 75 peticiones por usuario, por aplicación y por hora. Si desea conocer más acerca de una implementación de la medida en el lenguaje GO lo puede leer aquí:&lt;br&gt;
&lt;a href="https://dev.to/gonzalezlrjesus/como-limitar-las-peticiones-a-una-api-750"&gt;Como limitar las peticiones a una api&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Si usted tiene alguna pregunta, sugerencia o corrección, ¡estaría encantado conocerla!&lt;/p&gt;

</description>
      <category>go</category>
      <category>heroku</category>
      <category>api</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Como limitar las peticiones a una API</title>
      <dc:creator>Jesus Gonzalez</dc:creator>
      <pubDate>Mon, 09 Mar 2020 18:13:48 +0000</pubDate>
      <link>https://dev.to/gonzalezlrjesus/como-limitar-las-peticiones-a-una-api-750</link>
      <guid>https://dev.to/gonzalezlrjesus/como-limitar-las-peticiones-a-una-api-750</guid>
      <description>&lt;p&gt;Cuando se desarrolla una API es importante que en todo momento esté disponible para ser utilizada por los usuarios, pero la verdad es que cuando una API sale a producción siempre aparecen factores que alteran su estabilidad.&lt;/p&gt;

&lt;p&gt;Uno de esos factores son los ataques &lt;a href="https://es.wikipedia.org/wiki/Ataque_de_denegaci%C3%B3n_de_servicio"&gt;DDoS&lt;/a&gt;, los cuales muchas veces son producidos por errores en aplicaciones que utilizan los servicios ofrecidos por la API o usuarios particulares, que realizan demasiadas solicitudes de información sobrecargando los recursos de los servidores (CPU, Discos Duros, Memoria).&lt;/p&gt;

&lt;p&gt;Para estos casos es necesario agregarle algunas medidas de defensa a la API.&lt;br&gt;
Una medida es aplicarle un limitante a las peticiones, el cual permite un número determinado de solicitudes que puedan realizar los usuarios a la API durante un rango específico de tiempo.&lt;/p&gt;

&lt;p&gt;Esta limitante puede ser aplicada en varias formas a las solicitudes, dependiendo de lo que se desea lograr.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usuario: Limitar la cantidad de solicitudes por token asignados a usuarios o por IP de dispositivos.&lt;/li&gt;
&lt;li&gt;Geográficamente: Limitar solicitudes que provengan de regiones (países, estados, etc.) en particular.&lt;/li&gt;
&lt;li&gt;Horario: Algunos servicios ofrecidos por la API solo pueden utilizarse por los usuarios en un horario específico.&lt;/li&gt;
&lt;li&gt; Servidor: Disminuir las solicitudes de un servidor para otro servidor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existen varios algoritmos utilizados para implementar una limitante:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;a href="https://es.wikipedia.org/wiki/Token_bucket"&gt;Token Bucket&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://es.wikipedia.org/wiki/Algoritmo_de_cubeta_con_goteo"&gt;Leaky Bucket&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Sliding Window&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para la implementación utilice la librería &lt;a href="https://github.com/didip/tollbooth"&gt;Tollbooth&lt;/a&gt; la cual usa el algoritmo Leaky Bucket para limitar las solicitudes.&lt;/p&gt;

&lt;p&gt;Desarrolle un unico endpoint de una API devolverá un array de Usuarios en formato JSON. Ya teniendo la API funcional y testeada podemos pasar a la parte de integrar el limitante que permitirá como máximo 1 petición por segundo. Lo que quiere decir es que dentro de un tiempo de 1 segundo,  solamente una petición podrá ser atendida por la API, las demás peticiones serán rechazadas y se devolverá una respuesta con el código &lt;strong&gt;HTTP 429&lt;/strong&gt; según la &lt;a href="https://tools.ietf.org/html/rfc6585#section-4"&gt;RFC 6585&lt;/a&gt; lo define como “Too many Request” y un mensaje indicando el error que ocurrió.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2FCThtXT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1k908pts08p2va02yauh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2FCThtXT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1k908pts08p2va02yauh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Con el limitante podemos asegurar que la API nos entregara un flujo de datos sin sobrecargar el servidor y evitar costes adicionales por un aumento de consumo de recursos por proveedores de hosting que cobran por uso.&lt;br&gt;
Para una mejor confiabilidad es recomendable también aplicar medidas de control en las aplicaciones del lado de los clientes.&lt;/p&gt;

</description>
      <category>api</category>
      <category>spanish</category>
      <category>go</category>
      <category>security</category>
    </item>
  </channel>
</rss>
