<?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: Stevan Kostoski</title>
    <description>The latest articles on DEV Community by Stevan Kostoski (@stevcooo).</description>
    <link>https://dev.to/stevcooo</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%2F46067%2F9d0ce424-a5ef-4270-a564-865d54a0b793.jpg</url>
      <title>DEV Community: Stevan Kostoski</title>
      <link>https://dev.to/stevcooo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stevcooo"/>
    <language>en</language>
    <item>
      <title>The transition from full time in office to fully remote</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Wed, 02 Feb 2022 12:53:15 +0000</pubDate>
      <link>https://dev.to/stevcooo/from-full-time-in-office-to-fully-remote-4i8f</link>
      <guid>https://dev.to/stevcooo/from-full-time-in-office-to-fully-remote-4i8f</guid>
      <description>&lt;h1&gt;
  
  
  Previous thoughts about working from home
&lt;/h1&gt;

&lt;p&gt;Each of us knows somebody who works from home, еven with unusual working hours, typically for some foreign client. If you think about it, you feel like it's uncomfortable. You believe that your daily tasks and routines associated with your private life will not allow that. Also, you think that working from home will decrease your productivity because of all the distractions at home. Even the employers who do not permit working from home think the same. They are afraid that you will not sit in front of your computer all the time and you won't complete all the tasks because there is nobody to "control" or "watch" you. I was the same. I thought that I couldn't work from home. I will miss my colleagues, that I will have too many distractions, and that I already have too many meetings in person. I thought it was impossible to achieve that from home. &lt;br&gt;
Then COVID 19 happened.&lt;/p&gt;

&lt;h1&gt;
  
  
  COVID19 strikes in March
&lt;/h1&gt;

&lt;p&gt;We received the news about COVID 19 virus during January and February. But in Europe, there was not that much of a fuss until there was the first case in Italy. Then somehow, everything was different. It started with canceling events, closing kindergartens, and everything that followed. Our company told us that we should start working from home until the whole situation was settled. Nobody expected that it would continue for the next two years and counting.&lt;br&gt;
Now, for something I thought that was impossible, I was forced to do. I need to work from home because there is no other option. The building my company was renting was closed. We were allowed to go there and pick up some equipment, but that was it. It would help if you found someplace at your apartment that will act as your office. &lt;br&gt;
It's temporary, they said. &lt;br&gt;
The first two weeks were chaos, at least for my family and me. My daughter was 10months old, and besides all her needs, she was sleeping 2-3 times a day. I needed to find somewhere to work and focus, have meetings, and not distract her while sleeping. And naturally, her room became my office. &lt;br&gt;
It's temporary, they said. &lt;br&gt;
But after a while, it all evolved naturally. Now I was able to see all the perks of working from home. Now I don't need to commute for 30-45min and rush to get back home in time. Now I need to get up, do the bathroom routine, and I'm ready to start working. I don't need to worry if the rush hour will hit me when I pick up my child from kindergarten. All the collaboration tools help when it comes to the professional aspect. Speaking from the perspective of a technical person, my team never encounters any communication problems of a technical kind. It would be best if you had proper equipment and place in your home (which none of us had planned before), where you could enter your working zone, and nobody will disturb you, and more importantly, you won't disturb anyone. &lt;br&gt;
We had a situation when my wife and I worked from home. It's a home project to set up two working spaces in one apartment. It's not a small thing, to be honest.&lt;br&gt;
As time goes by, I see more benefits of working from home. I used to commute to work, and now it's my free time. Whenever I need to run some errands at home, I can do them because I can sit on my PC later and finish what has been left.&lt;/p&gt;

&lt;h1&gt;
  
  
  Moving to my hometown for a while
&lt;/h1&gt;

&lt;p&gt;My daughter is allergic to peanuts. So there was a birthday party in the kindergarten, and she ate some food with peanuts. It was a disaster for the entire following week. We gave her medicine for the next few days until everything got back to normal, but we needed to keep her at home for some time. When having both parents with a full-time job and a kid at home, the only way out was to go back to my hometown, where my parents could keep an eye on her while we worked. We packed, and we stayed there for a whole month. After working hours, I was able to meet my old friends. &lt;br&gt;
It feels nice to be back in your hometown. It's yours. &lt;br&gt;
Besides the whole situation, we see the benefit of working from home. You're no longer tied up to a geographic location, you need your laptop, and you're ready to work from any place. All you need is a good internet connection.&lt;/p&gt;

&lt;h1&gt;
  
  
  Taking a step ahead, try to compete on a world level.
&lt;/h1&gt;

&lt;p&gt;After one month of working from my hometown, we went on vacation for several days. I had plenty of time to think about the whole situation and the advantages and disadvantages. Now I'm in a situation where I find it suitable working from home. I've managed to organize my professional and my personal/private time. I found ways how can I connect with my friends and my colleagues. &lt;br&gt;
In the meantime, as with every other software engineer, I've got several offers on LinkedIn for full-time remote positions from different companies in Europe and one from New Zealand. At that point, I worked in my company for almost four years. But I've started considering taking another role in another company. Yet, this time fully remote, no middle management, no company in between that provides all the documentation and project communications. None of that, just direct collaboration with a company with its in-house project. Since all of the offers I received were from companies that act as a "middle man" by providing developers/engineers to companies that now need them, I decided to take a step ahead and try to find one.&lt;br&gt;
Currently, there are tons of websites where companies/employers seek employees. I've tried several of them, but I didn't find many matches since I had some criteria. Until, one day, I spotted a job offer on StackOverflow. It had a nice and clean job description, so I applied. Remember, now the pool of applicants is way more significant than the one in a single city. No matter which city, now everybody can apply since the job is fully remote and doesn't require any physical presence.&lt;br&gt;
We had several meetings. There was a technical assignment too. I liked the story they were telling of how they organized their work and what my day-to-day responsibilities would look like, and we reached an agreement. I was checking my email for their response first thing in the morning. When I got their email stating that they picked me over 200 applicants, I was over the moon. I was at the same time happy and excited. Now I need to resign from my current job and sign a contract. That's easy. It's not my first time. I've been in this industry for more than ten years, so I have done this part. But now, since the job is fully remote, that means I should take care of my taxes, insurances, etc. For that, I need to have a company and accountant and everything that follows. Now I need to start a company, find an accountant and a lawyer. There was plenty of info on internet forums about how you can do that, and honestly, it's pretty straightforward. But it takes some time and patience. That probably depends on the country you live in. As you read, you can notice that a whole lot of things are changing when you move from a full-time employee in a local company to a fully remote company. But it's new, it's exciting, and you're learning a new thing that you haven't even imagined previously.&lt;/p&gt;

&lt;h1&gt;
  
  
  Fully remote is not the same as freelancing.
&lt;/h1&gt;

&lt;p&gt;There is often confusion between this two. Freelancing is more like a gig, something you do in the short term for a specific task. It can grow in a long-term collaboration, of course, but that's not the initial idea. You can check the &lt;a href="https://www.dictionary.com/browse/freelance"&gt;definition &lt;/a&gt;. Fully remote describes a long-term collaboration intent. It understands that you will probably have onboarding and contract on a more extended period. You're not paid for how many tasks you complete or how much time you spend in front of your screens. There shouldn't be any time or click trackers. You should build trust and do what is expected in the correct timeframe, with no shortcuts. It should be a long-term commitment, which means you should take ownership of some parts of the solution as time goes by.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dare to exit your comfort zone
&lt;/h1&gt;

&lt;p&gt;Too much of anything is poison. The same applies to comfort, no matter which comfort you're referring to. In this context, I think of the comfort in your working environment. When you work on a project for a more extended period (I was on my last project for almost four years), you slowly get in your comfort zone. It's bliss in the beginning and as time goes by. You know the drill, the problems, the solutions, and the process, and you're comfortable. But if you stay there for a more extended period, you may enter the spiral where you're afraid to leave that comfort. You may start to get the feeling of missing out on technologies and modern solutions. The more you stay there, the more afraid you get and the less courage you will have to learn something new to get out of there. Let's get real to sharpen your portfolio and apply for a new job. You need to show some skills, usually some new technologies or technologies you haven't experienced, which never 100% match your current position. And that takes time and effort. The longer you stay in your comfort zone, the more time and effort it will require.&lt;/p&gt;

&lt;h1&gt;
  
  
  What once was a sporadic case that you heard of now is reality.
&lt;/h1&gt;

&lt;p&gt;Like I said in the begging, each of us knows somebody or heard about someone working from their hometown for a company abroad, and they did all the paperwork and contracts over the internet. Now, try to explain to your parents or elder ones that you're going to quit your current job where you're well paid, you don't have any stress or pressure, and you will sign some online agreement with people who you never met in person. As mentioned, it's about mutual trust. The risks are on both sides. They have never met you in person also. Numerous things can go wrong for them too. It's not a one-way street. In my opinion, try your best and choose carefully, no matter which side you are, then build trust and have patience and understanding. If something goes wrong, most of the time, it can be resolved if communicated well. In the other cases, you're not back to square one. You have learned something, both of you.&lt;/p&gt;

&lt;h1&gt;
  
  
  Experience so far
&lt;/h1&gt;

&lt;p&gt;I've been in this role for three months now. So far, everything is going smoothly. It looks like we aligned how we imagined the working day. After a few talks, it was clear that we share almost the same vision of how things should be done. We even admired the same product development philosophy. There was and still is a learning curve for me, but I hope to climb it slowly but with confidence. &lt;/p&gt;

&lt;p&gt;Please note that this is only my point of view. It shouldn't be considered as a general rule. I just wanted to share my story and the process of going from a local office to a fully remote one. I hope some of you will find this inspiring or detect some uncertainty pinpoints.&lt;/p&gt;

&lt;p&gt;About my fears of not working from an office, I was right for only one of them. I miss spending time with my colleagues, the "watercooler talk," and our lunches together.&lt;/p&gt;

&lt;p&gt;Thanks for reading! If you like you can see my other blog posts on &lt;a href="https://blog.kostoski.com/"&gt;https://blog.kostoski.com/&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>remote</category>
    </item>
    <item>
      <title>From Hardware to API, From Arduino To Cloud</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Mon, 09 Nov 2020 09:31:22 +0000</pubDate>
      <link>https://dev.to/stevcooo/from-hardware-to-api-from-arduino-to-cloud-22d2</link>
      <guid>https://dev.to/stevcooo/from-hardware-to-api-from-arduino-to-cloud-22d2</guid>
      <description>&lt;h1&gt;
  
  
  From Hardware to API, From Arduino To Cloud
&lt;/h1&gt;

&lt;h2&gt;
  
  
  This is a proof of concept that shows how can you read data from a sensor and write it to database trough API, and the show that data on Web.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;Please note that data from this sensor is not too relevant, it can be used as a guide, but the numbers are not too precise.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m36mKPlS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/SolutionDiagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m36mKPlS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/SolutionDiagram.png" alt="Wiring" width="880" height="583"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Solution diagram&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware
&lt;/h3&gt;

&lt;p&gt;The sensor that I'm using is Nova PM sensor SDS011Arduino board is NodeMCU ESP82667 Segment display is TM1637 LED Display Module.&lt;br&gt;&lt;br&gt;
This is the links of the components that I've been using (they are no referral links!)  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.aliexpress.com/item/32617788139.html?spm=a2g0s.9042311.0.0.27424c4dXRo748NodeMCU"&gt;Nova PM sensor SDS011&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aliexpress.com/item/32733851178.html?spm=a2g0s.9042311.0.0.27424c4dTgWALH"&gt;ESP8266&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aliexpress.com/item/1961805015.html?spm=a2g0s.9042311.0.0.27424c4dMfBZmF"&gt;TM1637 LED Display Module&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hardware Architecture
&lt;/h3&gt;

&lt;p&gt;ESP8266 is the central unit, it reads data from SDS011 and sends it to TM1637 LED Display and API.&lt;br&gt;&lt;br&gt;
If you go through the &lt;a href="https://github.com/stevcooo/PollutionBlog/blob/master/Arduino/pollution-sensor-wifi-post-and-two-displays/pollution-sensor-wifi-post-and-two-displays.ino"&gt;&lt;code&gt;Arduino code&lt;/code&gt;&lt;/a&gt;, you can clearly see how it works. Please note that you need to set your network name and password, to be able to connect the ESP8266 to your local network so it can send data to your API.  I've put some description comments in the code, it should be enough to do your setup.  &lt;/p&gt;

&lt;h4&gt;
  
  
  Connections
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Dust sensor:
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ESP8266&lt;/th&gt;
&lt;th&gt; SDS011&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;D2&lt;/td&gt;
&lt;td&gt;TX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D3 &lt;/td&gt;
&lt;td&gt;RX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VIN&lt;/td&gt;
&lt;td&gt;5V&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  PM 10 Display:
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ESP8266 &lt;/th&gt;
&lt;th&gt; TM1637 LED&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;D5&lt;/td&gt;
&lt;td&gt;CLK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D6&lt;/td&gt;
&lt;td&gt;DIO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VIN&lt;/td&gt;
&lt;td&gt;VCC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  PM 2.5 Display:
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ESP8266 &lt;/th&gt;
&lt;th&gt; TM1637 LED&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;D7&lt;/td&gt;
&lt;td&gt;CLK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D8&lt;/td&gt;
&lt;td&gt;DIO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VIN&lt;/td&gt;
&lt;td&gt;VCC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ujLg-cEC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Schema.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ujLg-cEC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Schema.png" alt="Wiring" width="880" height="658"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Wiring diagram&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h2&gt;
  
  
  Enclosing box
&lt;/h2&gt;

&lt;p&gt;To have something compact, we need a box where we will put all these components. Because this is a custom build project, finding a box that will be compact for our components it's difficult, the easiest solution is to design and 3d print one. For this purpose, I designed and printed one.&lt;br&gt;&lt;br&gt;
STL models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/3DModel/PollutionSkeleton-Box.stl"&gt;&lt;code&gt;Box&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/3DModel/PollutionSkeleton-Cover.stl"&gt;&lt;code&gt;Cover&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Designed model
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D0KkHQ24--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Box1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D0KkHQ24--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Box1.png" alt="Wiring" width="635" height="525"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;3D Design &lt;/center&gt;&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tf_NMfJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Box2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tf_NMfJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Box2.png" alt="Wiring" width="644" height="562"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;3D Design &lt;/center&gt;&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lOPJCRFq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Box3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lOPJCRFq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/Box3.png" alt="Wiring" width="639" height="560"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;3D Design &lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  3D Printed model
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/3DModel/PollutionSkeleton-Box.stl"&gt;&lt;code&gt;Box&lt;/code&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/3DModel/PollutionSkeleton-Cover.stl"&gt;&lt;code&gt;Cover&lt;/code&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wE89ixBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/final.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wE89ixBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/final.jpg" alt="Wiring" width="880" height="568"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;3D Printed box&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h2&gt;
  
  
  Software
&lt;/h2&gt;

&lt;p&gt;Because we want data received from the sensor to be sent/written in the database, we need some sort of endpoint where our ESP8266 can send data. For that purpose, we will create a web site where we can store or view our data. Please note that in this example we use unprotected API, so do not expose your endpoint or use some kind of authentication to avoid abuse of your resources (if some destructive person finds your endpoint then it can easily overflow with data).  Our web solution has two parts, one is where ESP8266 has access and post data (sensor data about PM 10 and PM 2.5), and the other is a page we can see segmented data. Also, because we can deal with a lot of data, here I'm using an SQL View that will prepare the required data on data level and then EF will pick it up as prepared and display in our scree, also here I implemented a caching mechanism, to avoid the constant call to the DB when the data is not changed.&lt;a href="https://github.com/stevcooo/PollutionBlog/blob/master/Web/PollutionSensor.v2/Controllers/EntriesApiController.cs"&gt;&lt;code&gt;EntriesApiController&lt;/code&gt;&lt;/a&gt; is the controller that has been called when ESP8266 tries to send data via the &lt;code&gt;API/Entries&lt;/code&gt; endpoint, this is a POST method. Also in this controller, we have GET method that will retrieve all records from the DB, you can use this for debugging.  Also, we have &lt;a href="https://github.com/stevcooo/PollutionBlog/blob/master/Web/PollutionSensor.v2/Controllers/StatisticsController.cs"&gt;&lt;code&gt;StatisticsController&lt;/code&gt;&lt;/a&gt;, where we call the &lt;code&gt;EntriesService&lt;/code&gt; to get statistics from DB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pepeare
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Software
&lt;/h4&gt;

&lt;p&gt;When you create a database, you need to get the ConnectionString and put it in &lt;a href="https://github.com/stevcooo/PollutionBlog/blob/master/Web/PollutionSensor.v2/appsettings.json"&gt;&lt;code&gt;appsettings.json&lt;/code&gt;&lt;/a&gt; instead of the value &lt;code&gt;YOUR_DATABASE_CONNECTION_HERE&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Also, when you create the database, you need to copy/paste this scripts:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/PollutionBlog/blob/master/Web/PollutionSensor.v2/SQL/StoredProcedure/dbo.sp_GetDailyAverage.sql"&gt;&lt;code&gt;dbo.sp_GetDailyAverage.sql&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/stevcooo/PollutionBlog/blob/master/Web/PollutionSensor.v2/SQL/StoredProcedure/dbo.sp_GetHourlyAverage.sql"&gt;&lt;code&gt;dbo.sp_GetHourlyAverage.sql&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will be enough to run the web application or host it if you like, you can also test your endpoints with Postman or something similar just to be sure that the software part of this project works fine.  &lt;/p&gt;

&lt;h4&gt;
  
  
  Hardware/Arduino
&lt;/h4&gt;

&lt;p&gt;When you're done with the web probject, now is time to put the values inside the &lt;a href="https://github.com/stevcooo/PollutionBlog/blob/master/Arduino/pollution-sensor-wifi-post-and-two-displays/pollution-sensor-wifi-post-and-two-displays.ino"&gt;&lt;code&gt;Arduino code&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Here &lt;code&gt;NAME_OF_YOUR_NETWORK&lt;/code&gt; should be the name of your local network, and also &lt;code&gt;NETWORK_PASSWORD&lt;/code&gt; should be the password of your network.&lt;br&gt;&lt;br&gt;
For the endpoint, here, the value of &lt;code&gt;PATH_OF_YOUR_ENDPOINT&lt;/code&gt; should be replaced with the address of your endpoint, for example, &lt;a href="http://mysensordatadomai.com/api/Entries"&gt;http://mysensordatadomai.com/api/Entries&lt;/a&gt; if you host your application on some web/cloud provider, or &lt;a href="http://localhost:27005/api/Entries"&gt;http://localhost:27005/api/Entries&lt;/a&gt; if the application is hosted on your machine (please check the port).&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;

&lt;p&gt;When you're done with these changes, you can use &lt;a href="https://www.arduino.cc/en/software"&gt;&lt;code&gt;Arduino App&lt;/code&gt;&lt;/a&gt; to deploy the code to your ESP8266. Here is a tutorial if you're unfamiliar with ESP8266, how to upload code to it:  &lt;a href="https://www.instructables.com/How-to-Program-NodeMCU-on-Arduino-IDE/"&gt;How to Program NodeMCU on Arduino IDE&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
You will need the driver:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers"&gt;CP210x USB to UART Bridge VCP Drivers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Then, you need to install the ESP8266 add-on for the Arduino IDE. For that, go to File &amp;gt; Preferences.&lt;/li&gt;
&lt;li&gt;Enter &lt;a href="http://arduino.esp8266.com/stable/package_esp8266com_index.json"&gt;http://arduino.esp8266.com/stable/package_esp8266com_index.json&lt;/a&gt; into the “Additional Board Manager URLs” field as shown in the figure below. Then, click the “OK” button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nfK1r1Ir--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/esp8266-arduino-ide-preferences.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nfK1r1Ir--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/esp8266-arduino-ide-preferences.png" alt="Board" width="722" height="607"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;&lt;center&gt;Install board&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;Because there are many options, here is my configuration, so you may find it usefull:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qeSkQ7Xs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Arduino/NodeMcuConfig_arduino.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qeSkQ7Xs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Arduino/NodeMcuConfig_arduino.png" alt="Config" width="486" height="596"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;&lt;center&gt;My selected configuration&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;You must select the right COM port, you can check that on DeviceManager, once you install the driver and connect the ESP8266 with a USB cable.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k2KOAV_E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/ComPort.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k2KOAV_E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/PollutionBlog/master/Images/ComPort.png" alt="Config" width="374" height="131"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;My com port&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h1&gt;
  
  
  Source
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/stevcooo/PollutionBlog"&gt;https://github.com/stevcooo/PollutionBlog&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  My Blog
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://blog.kostoski.com/blog/3"&gt;https://blog.kostoski.com/blog/3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>pollution</category>
      <category>sensor</category>
      <category>netcore</category>
    </item>
    <item>
      <title>Azure Functions, Queues and Table storage a.k.a Serverless architecture trough example</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Tue, 12 May 2020 09:15:19 +0000</pubDate>
      <link>https://dev.to/stevcooo/azure-functions-queues-and-table-storage-a-k-a-serverless-architecture-trough-example-490i</link>
      <guid>https://dev.to/stevcooo/azure-functions-queues-and-table-storage-a-k-a-serverless-architecture-trough-example-490i</guid>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: I wanted to buy a mouse wich was on 50% sale twice a year. I was too lazy to check when it's on sale, so I created a website that will do that for me and send me an email when there is a change on the price/availability using Azure serverless architecture and .net core framework. If you follow along, I'll teach you how to do that yourself or you can use my &lt;a href="https://product-scrape.azurewebsites.net"&gt;website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless buzz word&lt;/li&gt;
&lt;li&gt;Idea&lt;/li&gt;
&lt;li&gt;Azure functions&lt;/li&gt;
&lt;li&gt;Azure queues&lt;/li&gt;
&lt;li&gt;
Azure tables

&lt;ul&gt;
&lt;li&gt;Accessing table data&lt;/li&gt;
&lt;li&gt;Direct access&lt;/li&gt;
&lt;li&gt;Access using azure functions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Email sending&lt;/li&gt;
&lt;li&gt;ASP.NET Identity Core&lt;/li&gt;
&lt;li&gt;
Azure resources

&lt;ul&gt;
&lt;li&gt;Azure function&lt;/li&gt;
&lt;li&gt;Send grid&lt;/li&gt;
&lt;li&gt;App service&lt;/li&gt;
&lt;li&gt;Application Insights&lt;/li&gt;
&lt;li&gt;Resource group&lt;/li&gt;
&lt;li&gt;Cost Management and Billing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Product scraper solution

&lt;ul&gt;
&lt;li&gt;ProductScraper web&lt;/li&gt;
&lt;li&gt;ProductInfo table config&lt;/li&gt;
&lt;li&gt;ScrapeConfig table config&lt;/li&gt;
&lt;li&gt;UserProfile table config&lt;/li&gt;
&lt;li&gt;ProductScraper.Common&lt;/li&gt;
&lt;li&gt;ProductScraper.Functions&lt;/li&gt;
&lt;li&gt;Http triggered functions&lt;/li&gt;
&lt;li&gt;Queue triggered functions&lt;/li&gt;
&lt;li&gt;Timer triggered functions&lt;/li&gt;
&lt;li&gt;
Scraping

&lt;ul&gt;
&lt;li&gt;Automatic scraping&lt;/li&gt;
&lt;li&gt;Manual scraping&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;ProductScraper.Functions.Tests&lt;/li&gt;
&lt;li&gt;ProductScraper.Models&lt;/li&gt;
&lt;li&gt;ProductScraper.Services&lt;/li&gt;
&lt;li&gt;Access data using Azure functions&lt;/li&gt;
&lt;li&gt;Direct data access&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Code and test Azure Functions locally&lt;/li&gt;
&lt;li&gt;Azure Storage Explorer&lt;/li&gt;
&lt;li&gt;Running the solution&lt;/li&gt;
&lt;li&gt;Contact info&lt;/li&gt;
&lt;li&gt;Code GitHub repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Serverless buzz word
&lt;/h1&gt;

&lt;p&gt;To be clear, serverless != no server, there is no such thing as web architecture where no server is involved. In my opinion serverless is a poorly chosen word for architecture that doesn't require a whole server to be dedicated to doing a job of serving a small amount of processing, retrieving or writing data. Instead, there is one &lt;code&gt;Mega-Giga-Tera&lt;/code&gt; big server (usually some cloud provider such as Amazon, Azure, Google services or similar) that can be used as a place where we can deploy a very small/tiny methods (stub routines) that can deal with this tiny data processing requests.&lt;/p&gt;

&lt;h1&gt;
  
  
  Idea
&lt;/h1&gt;

&lt;p&gt;In this demo project, I'll try to cover a few areas of the Azure serverless architecture ecosystem and use them on a real application. My idea was to create a web page where I can create a list of products/items that are available on online shops and track their changes, and whenever there is a change on the price or availability an email with the updates will be sent to me. That way I'll save myself some time checking product prices to see when they are on sale or become available. To get details from some URL, we gonna use a &lt;a href="https://en.wikipedia.org/wiki/Web_scraping"&gt;scraper&lt;/a&gt; to collect data from other websites. Every website/webstore has a different layout and for that, we need to configure our scraper to be able to scrape different stores.&lt;/p&gt;

&lt;h1&gt;
  
  
  Azure functions
&lt;/h1&gt;

&lt;p&gt;As I said earlier, I used Azure functions to establish the server-side functionality. An azure function can be triggered/called in several ways, in this demo I'll be using &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=csharp"&gt;Http&lt;/a&gt;, &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-queue-trigger?tabs=csharp"&gt;Queue&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer?tabs=csharp"&gt;Timer&lt;/a&gt; triggered functions. For more details, you can check the full documentation on this &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Azure queues
&lt;/h1&gt;

&lt;p&gt;In my example, I'll be using queues as a channel for communication between my functions. For example, when the function for detecting product change has detected some change, it sends a message to the &lt;code&gt;email-queue&lt;/code&gt; and then, another function (queue triggered) for email sending is listening to that same queue, and whenever there is a new message it picks it up and sends the email accordingly. So, no direct call between the functions (methods), they communicate using the queues. That allows us to have an asynchronous workflow.  &lt;/p&gt;

&lt;p&gt;This is the official Microsoft definition for the queues:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Azure Queue Storage is a service for storing large numbers of messages. You access messages from anywhere in the world via authenticated calls using HTTP or HTTPS. A queue message can be up to 64 KB in size. A queue may contain millions of messages, up to the total capacity limit of a storage account. Queues are commonly used to create a backlog of work to process asynchronously.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more info about the queues, you can check this &lt;a href="https://docs.microsoft.com/en-us/azure/storage/queues/storage-queues-introduction"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Azure tables
&lt;/h1&gt;

&lt;p&gt;In most of the cases when you create a web application there is a need to store some data. Usually, we store it in tables or files. Depending on the nature of the application or our skills we can choose between SQL or NoSQL storage. In most of my projects, I've been using MSSQL and PostgreSQL, but in this demo, I will use Azure Table storage. &lt;/p&gt;

&lt;p&gt;This is Azure definition of Table storage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Azure Table storage stores large amounts of structured data. The service is a NoSQL datastore which accepts authenticated calls from inside and outside the Azure cloud. Azure tables are ideal for storing structured, non-relational data.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more details, you can check the full documentation on this &lt;a href="https://docs.microsoft.com/en-us/azure/cosmos-db/table-storage-overview"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing table data
&lt;/h2&gt;

&lt;p&gt;To store or retrieve data from tables, you need to have a way to access them. In my demo application I'm be using two different approaches to access the table data:&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct access
&lt;/h3&gt;

&lt;p&gt;You can use the Microsoft Azure Storage Libraries for .NET and have direct access to the storage tables. I used this in &lt;code&gt;IUserProfileService&lt;/code&gt; for the CRUD operations. This way cheaper because you will access the data directly, bypassing azure functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2CvUwJrI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/UserProfileServiceDiagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2CvUwJrI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/UserProfileServiceDiagram.png" alt="Direct data access" width="876" height="274"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;UserProfileService communication with Azure Storage&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Access using azure functions
&lt;/h3&gt;

&lt;p&gt;Another way to access your data is to create an Azure Function that will do that, so instead of writing directly to the table, you will pass data to the Azure function and then the function will store the data in the table. This maybe sounds like overkill, having a 'redundant' layer (function) in the middle, because you need to maintain the function and pay for the usage of the function, but it also has its benefits. You can bind to different queues and notify them; you can easily log everything in one place and many other benefits. I use this approach in &lt;code&gt;IScrapeConfigService&lt;/code&gt; implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PB3hsKh0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/ScrapeConfigServiceDiagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PB3hsKh0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/ScrapeConfigServiceDiagram.png" alt="Direct data access" width="880" height="299"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;ScrapeConfigsController communication with Azure Storage&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Email sending
&lt;/h1&gt;

&lt;p&gt;To send emails, I used SendGrid service integrated into Azure. It's really easy to set up an account and then using Azure Function you can easily send an email. You can bind an Azure function to &lt;code&gt;queue&lt;/code&gt; and whenever you send message to that queue SendGrid will send an email. Easy Peasy.&lt;br&gt;
In my demo, I've created a service that I will use to send emails whenever my app needs to send any. So, I created a Function that will accept email messages, and then it will populate all the required data if needed and send them to message queue. From that message queue, a function that is triggered on message queue will pick it and forward it to SendGrid.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PwjMJPWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/EmailDiagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PwjMJPWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/EmailDiagram.png" alt="Direct data access" width="880" height="185"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Email sending flow&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  ASP.NET Identity Core
&lt;/h1&gt;

&lt;p&gt;Whenever users are involved in a web application, there is always a need to be able to authenticate or authorize them. Usually, when we use .net core as a technology we tend to use Identity service. One thing that is bonded to the Identity service is that it requires MSSQL data provider, but that is now what is suitable for me in this demo since I choose to use Azure tables as storage, of course, we can make a hybrid app where identity will be stored in MSSQL and all other data in Azure tables, but I found a NuGet package that will implement all the Identity functionality but it will rely on Azure Tables, that's great. This is the package &lt;a href="https://dlmelendez.github.io/identityazuretable/#/"&gt;Identityazuretable&lt;/a&gt; that I'll be using in this project for the identity part.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hLQlH8co--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/IdentityDiagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hLQlH8co--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/IdentityDiagram.png" alt="Identity" width="880" height="261"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Identityazuretable architecture&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Azure resources
&lt;/h1&gt;

&lt;p&gt;I've mentioned a few Azure services. To be able to reproduce this solution you will need to use several Azure resources. &lt;/p&gt;
&lt;h2&gt;
  
  
  Azure function
&lt;/h2&gt;

&lt;p&gt;For the Azure functions, you'll need to create a &lt;a href="https://portal.azure.com/#blade/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites/kind/functionapp"&gt;Function App&lt;/a&gt;. In the process of creating the Function App, in the Hosting step, you will be asked to create a Storage Account if you don't have one created previously, just type the name and the wizard will create it for you. You can see mine in the screenshots below. Once created you can check your &lt;a href="https://portal.azure.com/#blade/HubsExtension/BrowseResource/resourceType/Microsoft.Storage%2FStorageAccounts"&gt;Storage account here&lt;/a&gt;. You need this Storage Account for the Tables and Queues that we will be using in this tutorial. For the PlanType, also on the Hosting tab, I've selected Consumption(serverless) because that was the cheapest one that will serve the needs of this application. Please be aware always before creating an Azure resource to check it's pricing because it maybe will be too expensive or will not be compatible with the needs of your product/application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gw-KV04K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/FunctionApp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gw-KV04K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/FunctionApp.png" alt="Function app" width="880" height="132"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Fuction app that I'm using in this demo&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HkP8jRNm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/StorageAccount.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HkP8jRNm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/StorageAccount.png" alt="Function app" width="880" height="148"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Storage account that I'm using in this demo&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Send grid
&lt;/h2&gt;

&lt;p&gt;Also, I mentioned SendGrid, to be able to send emails using the Azure functions, you will need a SendGrid account, you can create one via Azure resources or create one directly on their website. Once you have created the SendGrid account, you need to obtain an ApiKey that will associate the FunctionApp with your SendGrid account (if you have any questions on this please let me know). Once you have the SendGridApiKey you will need to add to the Azure Functions Configuration settings (check the screenshots).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NyWTXSkx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/AzureFunctionConfigurationLink.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NyWTXSkx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/AzureFunctionConfigurationLink.png" alt="Function app" width="880" height="469"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Navigate to Azure app configuration&lt;/center&gt;&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--487hT1JC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/SendGridApiKeyConfiguration.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--487hT1JC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/SendGridApiKeyConfiguration.png" alt="Function app" width="880" height="357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;SendGrid API key configuration&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;
&lt;h2&gt;
  
  
  App service
&lt;/h2&gt;

&lt;p&gt;In the end, you will need an App Service or a place where you can host your website that will interact with the Azure Functions. Of course, you can host your web application anywhere and still communicate with the Azure Functions, but in this example a tired to stick with Azure services, so I've created AppService and hosted my .net core application here. Creating an App service is pretty straight forward wizard, be careful with the pricing tiers (App service plan), I've chosen the free option, but of course, there are lot more other plans that you can use to scale your application.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---19C223k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/AppService.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---19C223k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/AppService.png" alt="Function app" width="847" height="964"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Sample of my settings for the App service&lt;/center&gt;&lt;/em&gt;    &lt;/p&gt;
&lt;h2&gt;
  
  
  Application Insights
&lt;/h2&gt;

&lt;p&gt;Application Insights it's a nice way to track the health of your Function App or App Service, here you can see a lot of useful info related to the Availablity, errors, Failed Request, Server response time, Server Request and many many more things. I've created two Application Insights services, one for the Azure Function and one for the App service.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--svJN6ylX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/ApplicationInsights.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--svJN6ylX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/ApplicationInsights.png" alt="Function app" width="880" height="386"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Data that is shown in Application Insights&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;
&lt;h2&gt;
  
  
  Resource group
&lt;/h2&gt;

&lt;p&gt;An Azure resource group is like a namespace or folder or a group for Azure services, whenever you create a service there is a filed where you can choose on which resource group that service should belong. This is a nice thing to be able to group all your services that are related to the same project. This is extremely useful when you have several Applications ecosystems, you can easily track them and see all services that are related. For this demo, I've used the same resource group for all services, and now I can easily see all the services that I'm using for this ProductScraper project.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jchXyvh3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/resourceGroup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jchXyvh3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/resourceGroup.png" alt="Function app" width="880" height="389"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Services in the resource group that I used for this demo&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;
&lt;h2&gt;
  
  
  Cost Management and Billing
&lt;/h2&gt;

&lt;p&gt;The very important thing is when using online service where you have a play Pay-as-you-go, &lt;br&gt;
like in this case, to check your bill every few days to avoid big or unexpected costs. &lt;br&gt;
I've done this mistake once, I've ended up with a huge bill because I selected some pricing plan that I wasn't aware how much it will cost &lt;br&gt;
(Always check &lt;a href="https://azure.microsoft.com/en-us/pricing/calculator/"&gt;Azure pricing&lt;/a&gt;, always! ). In this &lt;a href="https://portal.azure.com/#blade/Microsoft_Azure_GTM/ModernBillingMenuBlade/Overview"&gt;section&lt;/a&gt; you can check how much you have spent so far (if any) and witch service costs you how much.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6V7pNrhs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/Costs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6V7pNrhs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/Costs.png" alt="Function app" width="880" height="481"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Billing sample of my demo&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;
&lt;h1&gt;
  
  
  Product scraper solution
&lt;/h1&gt;

&lt;p&gt;As I mentioned in the first sentence my idea was to build a product scraper that will collect products info and send me an email whenever there is a change in them. In the following context, I'll try to explain the most important things in the application, if something is unclear or there is no enough info provided please let me know. You can see in the repository that my solution is divided into 5 projects. I'll try to explain their purpose and the most important code in them.&lt;/p&gt;
&lt;h2&gt;
  
  
  ProductScraper web
&lt;/h2&gt;

&lt;p&gt;This is a web project. Here I configure the Identity settings to use &lt;a href="https://www.nuget.org/packages/ElCamino.AspNetCore.Identity.AzureTable/"&gt;&lt;code&gt;ElCamino.AspNetCore.Identity.AzureTable&lt;/code&gt;&lt;/a&gt; NuGet package for user registration/authentication/authorization. &lt;br&gt;
You can see this configuration in &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Startup.cs"&gt;&lt;code&gt;Startup.cs&lt;/code&gt;&lt;/a&gt; file in &lt;code&gt;ConfigureServices&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDefaultIdentity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IdentityUser&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignIn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireConfirmedAccount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireUniqueEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireDigit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireLowercase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireUppercase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireNonAlphanumeric&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="c1"&gt;//ElCamino configuration&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAzureTableStores&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApplicationDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IdentityConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;IdentityConfiguration&lt;/span&gt; &lt;span class="n"&gt;idconfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;IdentityConfiguration&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;TablePrefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AzureTable:IdentityConfiguration:TablePrefix"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;StorageConnectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AzureTable:StorageConnectionString"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;LocationMode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AzureTable:IdentityConfiguration:LocationMode"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;IndexTableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdentityIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default: AspNetIndex&lt;/span&gt;
                &lt;span class="n"&gt;RoleTableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdentityRoles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// default: AspNetRoles&lt;/span&gt;
                &lt;span class="n"&gt;UserTableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdentityUsers&lt;/span&gt;   &lt;span class="c1"&gt;// default: AspNetUsers&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;idconfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDefaultTokenProviders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDefaultUI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateAzureTablesIfNotExists&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApplicationDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;//can remove after first run;&lt;/span&gt;

        &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more detailed info on how to set this configuration, you can check on the &lt;a href="https://dlmelendez.github.io/identityazuretable/#/"&gt;official project site&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Also in &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Startup.cs"&gt;&lt;code&gt;Startup.cs&lt;/code&gt;&lt;/a&gt; file in &lt;code&gt;ConfigureServices&lt;/code&gt; method there is configuration about the Azure table that I'm using. Here I add settings for each Azure table such as ConnectionString and TableName.&lt;/p&gt;

&lt;h3&gt;
  
  
  ProductInfo table config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAzureTableStorage&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProductInfo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AzureTableStorage&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProductInfo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AzureTableSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;storageConnectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AzureTable:StorageConnectionString"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductInfo&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ScrapeConfig table config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAzureTableStorage&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ScrapeConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AzureTableStorage&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ScrapeConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AzureTableSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;storageConnectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AzureTable:StorageConnectionString"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ScrapeConfig&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  UserProfile table config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAzureTableStorage&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AzureTableStorage&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AzureTableSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;storageConnectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AzureTable:StorageConnectionString"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, in this project, in the Identity section, when a new user is registered, I'm creating a record in UserPfofile table, in this table, we will keep all the additional info related to the users. You can see this in the &lt;br&gt;
&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Areas/Identity/Pages/Account/Register.cshtml.cs"&gt;&lt;code&gt;Register.cshtml.cs&lt;/code&gt;&lt;/a&gt; file in OnPostAsync method. Here is the code segment that is adding a record to the UserProfile table. You can note that Id value of IdentityUsers table record is used as Userid in UserProfile table, that's the link between these two tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;userProfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UserProfile&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;EnableEmailNotifications&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DaysBetweenEmailNotifications&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_userProfileService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, there is a need some user(s) to be an administrator on this application to be able to create/edit/delete &lt;code&gt;ScrapeConfigurations&lt;/code&gt;, so in that manner, were in this same method when a user is created I'm checking if that's the first user that creates a profile, if so, then I assign Admin claim to him.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_userProfileService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUsersCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Claim&lt;/span&gt; &lt;span class="n"&gt;claim&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClaimValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Admin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClaimValueTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_userManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddClaimAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to authorize with Claims you need to add Authorization policy in &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Startup.cs"&gt;&lt;code&gt;Startup.cs&lt;/code&gt;&lt;/a&gt; file in &lt;code&gt;ConfigureServices&lt;/code&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AdminOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequireClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClaimValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Admin&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you configure the policy for Admins, you can add &lt;code&gt;[Authorize(Policy = Policy.AdminOnly)]&lt;/code&gt; before the declaration of any controller or method in order to enforce this policy. I used this in &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Controllers/ScrapeConfigsController.cs"&gt;&lt;code&gt;ScrapeConfigsController.cs&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Or if you want to use this inside a method, you can use &lt;code&gt;User.IsInRole(ProductScraper.Common.Naming.ClaimValues.Admin)&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;Also, in this project I've created two controllers, &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Controllers/ProductsController.cs"&gt;&lt;code&gt;ProductsController.cs&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Controllers/ScrapeConfigsController.cs"&gt;&lt;code&gt;ScrapeConfigsController.cs&lt;/code&gt;&lt;/a&gt;, they using the Services created in &lt;code&gt;ProductScraper.Services&lt;/code&gt; project are interacting with the data, reading and writing to Azure tables or calling Azure functions.&lt;/p&gt;
&lt;h2&gt;
  
  
  ProductScraper.Common
&lt;/h2&gt;

&lt;p&gt;This is a small &lt;code&gt;netstandard2.0&lt;/code&gt; project. Here I'll store all the common things for all other projects. For example, to avoid using strings and magic numbers in the code, I place all of them here and then use them as a constant field. That way when I change the name of a function, or a queue, I don't need to check all code, I only change the name here in this project. When you're using functions and queues, it's easy to forget the change a queue name in all functions that are related to that queue, but this approach helps you to avoid that scenario. This is how I keep track of the table names:&lt;br&gt;
This is how I keep track of the table names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TableName&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ProductInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ProductInfo"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ScrapeConfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ScrapeConfig"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserProfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UserProfile"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;IdentityIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"IdentityIndex"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;IdentityRoles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"IdentityRoles"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;IdentityUsers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"IdentityUsers"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is how I use them later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FunctionName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ScrapeProduct&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthorizationLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ScrapeProduct&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/{userId}/{productId}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{userId}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;CloudTable&lt;/span&gt; &lt;span class="n"&gt;productInfoTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ScrapeConfig&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;CloudTable&lt;/span&gt; &lt;span class="n"&gt;scrapeConfigTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmailsToSend&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IAsyncCollector&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SendGridMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;emailMessageQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ProductScraper.Functions
&lt;/h2&gt;

&lt;p&gt;This is the project where Azure functions are developed. I've developed three types of Azure functions, Htpp, Queue and Timer triggered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Http triggered functions:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeFunctions/ScrapeProduct.cs"&gt;&lt;code&gt;ScrapeProduct.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/GetAllScrapeConfigs.cs"&gt;&lt;code&gt;GetAllScrapeConfigs.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/GetScrapeConfig.cs"&gt;&lt;code&gt;GetScrapeConfig.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/AddScrapeConfig.cs"&gt;&lt;code&gt;AddScrapeConfig.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/UpdateScrapeConfig.cs"&gt;&lt;code&gt;UpdateScrapeConfig.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/DeleteScrapeConfig.cs"&gt;&lt;code&gt;DeleteScrapeConfig.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/Common/EmailSender.cs"&gt;&lt;code&gt;EmailSender.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Queue triggered functions:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/Common/EmailToSendGrid.cs"&gt;&lt;code&gt;EmailToSendGrid.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/EmailNotificationsFunctions/GenerateProductUpdateEmail.cs"&gt;&lt;code&gt;GenerateProductUpdateEmail.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeFunctions/ScrapeUserProducts.cs"&gt;&lt;code&gt;ScrapeUserProducts.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Timer triggered functions:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/EmailNotificationsFunctions/CheckForUsersReadyForEmailNotification.cs"&gt;&lt;code&gt;CheckForUsersReadyForEmailNotification.cs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scraping
&lt;/h3&gt;

&lt;p&gt;The initial idea of this project was web scraping. Whole scraping logic is located in this project in &lt;code&gt;Scrape&lt;/code&gt; method in &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/Common/Utils.cs"&gt;&lt;code&gt;Utils.cs&lt;/code&gt;&lt;/a&gt; class. &lt;br&gt;
I'm using &lt;a href="https://html-agility-pack.net"&gt;&lt;code&gt;HtmlAgilityPack&lt;/code&gt;&lt;/a&gt; here to do all the web scraping. It's a really nice package with tons of features and handles the web scraping very well in my experience. In order to scrape info from the web site, we need to configure witch info is important for us, in this case, Name, Price and Availability. &lt;br&gt;
Because every website has it's own layout, we will create Config for each domain that we want to scrape info from. I've created a video where I show how to add configuration for a website.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=rCfHlkDJ_G8"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XkXBi_YP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.youtube.com/vi/rCfHlkDJ_G8/0.jpg" alt="IMAGE ALT TEXT HERE" width="480" height="360"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Add scrape configuration&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=7v3LVJwylCk"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lnspuhz---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.youtube.com/vi/7v3LVJwylCk/0.jpg" alt="IMAGE ALT TEXT HERE" width="480" height="360"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Add product to your list&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h4&gt;
  
  
  Automatic scraping
&lt;/h4&gt;

&lt;p&gt;Every time user adds a new product in his list of products, the scraping method is run in the background to collect all the data about that link. If currently there is no configuration for that website an email will be sent to the administrator to create a configuration for that website.&lt;br&gt;
Also, here we use the Time triggered function, witch occurs on constant intervals and checks for each user if scraping should be performed based on his schedule preferences, should it be every day, every month or other. If so, then for each of his products we run the &lt;code&gt;Scrape&lt;/code&gt; method to check if there is any change in the product details from the previous time.&lt;br&gt;&lt;br&gt;
You can see the flow in this chart:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g54o-jo6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/AutomaticScraping.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g54o-jo6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Diagrams/AutomaticScraping.png" alt="Function app" width="880" height="796"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Automatic scraping using Time triggered function&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h4&gt;
  
  
  Manual scraping
&lt;/h4&gt;

&lt;p&gt;User can also manually check for product changes when he opens the webpage, and in the list of the product, there is a &lt;code&gt;Check&lt;/code&gt; button, which invokes the Scrape function.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OTF4dUgc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/manualCheckButton.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OTF4dUgc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/manualCheckButton.png" alt="Function app" width="880" height="234"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Manual scraping&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h2&gt;
  
  
  ProductScraper.Functions.Tests
&lt;/h2&gt;

&lt;p&gt;This is projected where I put all the tests. There are not many tests, I need to update it. Basically what is use this project, for now, is for manual testing my methods and configurations. Using tests you can easily check if some part of your code behaves like it should or not. Here I created a few ScrapConfig configurations and I can easily check if they work or not. If I set this project as a step of continuous integration I can be sure that every configuration works without any problems, that saves a lot of pain and time.&lt;/p&gt;

&lt;h2&gt;
  
  
  ProductScraper.Models
&lt;/h2&gt;

&lt;p&gt;Here I store all EntityModels, objects that should be saved into the database, and all the ViewModels, objects that are used for data transfer between layers, but it's not stored anywhere. One important thing about the entity models is that they are all descendants of the &lt;code&gt;TableEntity&lt;/code&gt; class, that is required to be able to be saved in AzureStorage tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  ProductScraper.Services
&lt;/h2&gt;

&lt;p&gt;In these projects are all services that Web project is using to access the data. Every service has it's own &lt;code&gt;Interface&lt;/code&gt; and &lt;code&gt;Implementation&lt;/code&gt; they can be found in Interfaces and Implementations folders accordingly. As I mentioned above, here implemented two approaches of the accessing azure storage data, one using &lt;code&gt;Functions&lt;/code&gt; and the other one using direct access to Azure Table storages.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Access data using Azure functions
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Services/Implementations/ScrapeConfigService.cs"&gt;&lt;code&gt;ScrapeConfigService.cs&lt;/code&gt;&lt;/a&gt; you can see how i call Azure function in order to read/write data to Azure storage table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct data access
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Services/Implementations/ProductInfoService.cs"&gt;&lt;code&gt;ProductInfoService.cs&lt;/code&gt;&lt;/a&gt; you can see how i directly access Azure table using &lt;a href="https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Services/Implementations/AzureTableStorage.cs"&gt;&lt;code&gt;AzureTableStorage.cs&lt;/code&gt;&lt;/a&gt; implementation of repository pattern. Here in this service i pass &lt;code&gt;AzureTableSettings&lt;/code&gt; where all the table info is stored, in that way i can access Azure Storage Table.&lt;/p&gt;

&lt;h1&gt;
  
  
  Code and test Azure Functions locally
&lt;/h1&gt;

&lt;p&gt;If you prefer to skip creating Azure account, you can for sure test azure functions locally, just like you test your Web applications locally before you publish them. To be able to run Azure function locally please follow this link there detailed info is provided by Microsoft.&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-local"&gt;Code and test Azure Functions locally&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Azure Storage Explorer
&lt;/h1&gt;

&lt;p&gt;Is a very good tool which allows you to see what's inside your storage account, witch tables are created, witch queues are there and messages that are enqueued. It also offers you to review your local storage account. For more info please check the link below.&lt;br&gt;&lt;br&gt;
&lt;a href="https://azure.microsoft.com/en-us/features/storage-explorer/"&gt;Azure Storage Explorer&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Running the solution
&lt;/h1&gt;

&lt;p&gt;When you clone this repository you should be able to open the solution using VisualStudio. You can check the web project by setting &lt;code&gt;ProductScraper&lt;/code&gt; as a startup project, or you can test the functions using Postman or other API testing tool, by setting &lt;code&gt;ProductScraper.Functions&lt;/code&gt; project as a startup project.&lt;br&gt;
There is also an option to run them both and to be able to test the whole workflow. To do so, you need to do a right-click on the solution and choose &lt;code&gt;Set Startup Projects...&lt;/code&gt; and then, on the screen that appears, choose Multiple startup projects and then in the list, choose to Start as an option for &lt;code&gt;ProductScraper&lt;/code&gt; and &lt;code&gt;ProductScraper.Functions&lt;/code&gt;,. Now when you click Oon start or F5, both project will start and you can test the solution.  and &lt;code&gt;ProductScraper.Functions&lt;/code&gt;,. Now when you click Oon start or F5, both project will start and you can test the solution.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wrpoAiL1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/SetUpMultipleStartUpProjects.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wrpoAiL1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/SetUpMultipleStartUpProjects.png" alt="Function app" width="414" height="692"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Set startup projects&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fo8p0S0k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/MultipleStartUpProjects.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fo8p0S0k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/ProductScraper/master/Images/MultipleStartUpProjects.png" alt="Function app" width="784" height="542"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;center&gt;Choose which projects to start&lt;/center&gt;&lt;/em&gt;  &lt;/p&gt;

&lt;h1&gt;
  
  
  Contact info
&lt;/h1&gt;

&lt;p&gt;If you have any troubles or questions with the content of this blog, please feel free to contact me via email stevan[at]kostoski.com&lt;/p&gt;

&lt;h1&gt;
  
  
  Code GitHub repo
&lt;/h1&gt;

&lt;p&gt;Here is the GitHub repository &lt;a href="https://github.com/stevcooo/ProductScraper"&gt;https://github.com/stevcooo/ProductScraper&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Blog
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://blog.kostoski.com/blog/4"&gt;https://blog.kostoski.com/blog/4&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Happy coding!
&lt;/h1&gt;

</description>
      <category>azure</category>
      <category>serverless</category>
      <category>scraping</category>
      <category>netcore</category>
    </item>
    <item>
      <title>Random Rubik's cube solve</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Thu, 26 Mar 2020 09:11:44 +0000</pubDate>
      <link>https://dev.to/stevcooo/random-rubik-s-cube-solve-4k35</link>
      <guid>https://dev.to/stevcooo/random-rubik-s-cube-solve-4k35</guid>
      <description>&lt;h1&gt;
  
  
  Inspiration
&lt;/h1&gt;

&lt;p&gt;I'm sure that all of you have heard about the &lt;a href="https://en.wikipedia.org/wiki/Infinite_monkey_theorem"&gt;infinite monkey theorem&lt;/a&gt; states that a monkey hitting keys at random on a typewriter keyboard for an infinite amount of time will almost surely type any given text, such as the complete works of William Shakespeare.&lt;/p&gt;

&lt;h1&gt;
  
  
  Idea
&lt;/h1&gt;

&lt;p&gt;In this direction I was wondering what if a monkey finds an unsolved Rubiks cube and starts rotating it for an infinite number of times, will it eventually solve it? I have the algorithm for solving the Rubik's cube, I wrote it for my last project, now I can use it in a different way, I can simulate random rotation for an infinite number of times and apply them to unsolved Rubik's cube and calculate the percentage of completion. According to the theorem, it will solve it one day, the chance of it occurring during a period of time hundreds of thousands of orders of magnitude longer than the age of the universe is extremely low (but technically not zero).&lt;/p&gt;

&lt;p&gt;Please note that I'm not using any learning algorithm, it's just a random rotation, that's the basic idea, so the maximum completion level is increasing/decreasing all the time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Experiment
&lt;/h1&gt;

&lt;p&gt;In order to start the experiment, I've got a solved cube and apply several random rotations in order to get a randomized cube. After randomization, it was 14.58% completed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s_a6iEiG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/bmvodr6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s_a6iEiG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/bmvodr6.png" alt="StartingCubeRandomized"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After one day of running, in other words, after 1,808,200,376 (~2 billion) rotations, it gets to 66.67% completion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n4MJoOAu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/rRMkI2s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n4MJoOAu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/rRMkI2s.png" alt="MaxCompletedCubeDay1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a sample of the random rotation moves.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7UGl8c7i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/w5YTdMj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7UGl8c7i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/w5YTdMj.png" alt="RandomCubeRotationsDay1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Next steps
&lt;/h1&gt;

&lt;p&gt;I'll keep monitoring the progress and if it gets above the current maximum completion, I will post an update.&lt;/p&gt;

&lt;p&gt;I hope this was interesting to you and also inspire you to do some fun with your old code or project. In days of isolation brilliant ideas can come to your mind, please use them in a good way and share them with the community.&lt;/p&gt;

</description>
      <category>rubiks</category>
      <category>experiment</category>
      <category>probability</category>
      <category>random</category>
    </item>
    <item>
      <title>Do The Work</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Tue, 10 Dec 2019 09:46:56 +0000</pubDate>
      <link>https://dev.to/stevcooo/do-the-work-4012</link>
      <guid>https://dev.to/stevcooo/do-the-work-4012</guid>
      <description>&lt;p&gt;&lt;em&gt;A brief recap of the Do The Work, book by Steven Pressfield&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One month ago I stumbled upon a very interesting book, judging by its cover. Do the work was the title, with a small used pencil on the cover photo. It turned to be a very small and nice book that has packed in one place all the resistance points of you not getting the work done. It points directly that you're not alone, you're not the first person to "hit the wall" and no, you're not crazy. The whole book is about a project journey, no matter the nature of the project, if it's writing a book, developing a kick-ass software or building a house. All the drawback you're gonna go through and all the disappointment points you're gonna visit. It gives you a vision, a hope, that you can do the work no matter what if you are aware that those resistance points are part of the development process.&lt;/p&gt;

&lt;p&gt;In the middle of the book, the author tries to isolate the 7 principles of the resistance. According to me, they should be sanitized in one sentence each and hang up on the wall in every office. Here are they:&lt;/p&gt;




&lt;h2&gt;
  
  
  There is an enemy
&lt;/h2&gt;

&lt;p&gt;There is an enemy. There is an intelligent, active, malign force working against us. Step one is to recognize this. This recognition alone is enormously powerful. It saved my life, and it will save yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  This enemy is implacable
&lt;/h2&gt;

&lt;p&gt;"It will kill you. It will kill you like cancer". Its aim is not to obstruct or to hamper or to impede. Its aim is to kill.&lt;/p&gt;

&lt;h2&gt;
  
  
  This enemy is inside you
&lt;/h2&gt;

&lt;p&gt;It comes from us. The enemy is inside you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The enemy is inside you, but it is not you.
&lt;/h2&gt;

&lt;p&gt;What does that mean? It means you are not to blame for the voices of Resistance you head in your head. If you've got head, you've got a voice of Resistance inside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;strong&gt;real you&lt;/strong&gt; must duel the &lt;strong&gt;resistance you&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You're the knight, resistance is the dragon. There is no way to be nice with the dragon or to reason with it or negotiate with it or beam a white light around it and make it your friend. The only possible intercourse between the knight and the dragon is a battle. The contest is life-and-death, &lt;em&gt;mano a mano&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resistance arises second
&lt;/h2&gt;

&lt;p&gt;What comes first is the idea, the passion, the dream of the work we are so excited to create that scares the hell out of us. Resistance is the shadow cast by the innovative self's sun. It means that before the dragon of Resistance reared it's ugly head and breathed fire into out faces, there existed within us a force so potent and life-affirming that is summoned this beast into being, perversely to combat it. But the urge to claim came first. The urge is love. Love for the material, love for the work, love for our brothers and sisters to whom we will offer our work as a gift. The opposite of fear is love, love of the challenge, love of the work, the pure joyous passion to take a shot at our dream and see if we can pull it off.&lt;/p&gt;

&lt;h2&gt;
  
  
  The opposite of &lt;strong&gt;resistance&lt;/strong&gt; is &lt;strong&gt;assistance&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In every mythology, every knight/hero is assisted by some character. Sometimes it's a magic raven, sometimes it's a donkey or a star. Those characters represent the Assistance, they stand for a dream. The dream is your project, your vision, your symphony, your startup. Love is the passion and enthusiasm that fill your heart when you envision your project's completion.&lt;/p&gt;

&lt;p&gt;The seventh principle of Resistance is that we can align ourselves with these forces of assistance to make the unmanifest manifest and ride them into battle against the dragon.&lt;/p&gt;




&lt;p&gt;I honestly recommend this book to every student, professor, engineer&lt;br&gt;
and every developer. This book is a simple reminder that we are not&lt;br&gt;
alone and a guide through the painful process of making stuff. Do The&lt;br&gt;
Work.&lt;/p&gt;

&lt;p&gt;I bought the book from BookDepository, this is not an affiliate link:&lt;br&gt;
&lt;a href="https://www.bookdepository.com/Do-Work-Steven-Pressfield/9781936891375"&gt;https://www.bookdepository.com/Do-Work-Steven-Pressfield/9781936891375&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Blog
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://blog.kostoski.com/blog/6"&gt;https://blog.kostoski.com/blog/6&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>development</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Add items dynamically in list in .net core</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Tue, 05 Nov 2019 08:50:14 +0000</pubDate>
      <link>https://dev.to/stevcooo/add-items-dynamically-in-list-in-net-core-40i9</link>
      <guid>https://dev.to/stevcooo/add-items-dynamically-in-list-in-net-core-40i9</guid>
      <description>&lt;p&gt;Very often there is a need to add items in a collection of items. Usually we have some object that holds a list of complex objects, for example, one Order contains several OrderItems which are complex objects (has their properties). When creating a Controller and View for such object, Razor ignores these properties and doesn't create input fields for them. In this example, I can show you how I solve this problem without refreshing the page.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsyjmcte68mtrwyzbuts9.gif" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsyjmcte68mtrwyzbuts9.gif" alt="AddAnimation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the first thins that you should do is to create an Editor for the Objects that are in the list, in this example the order items. This editor is simple copy-paste from its Edit.cshtml file, but remember to remove the &lt;/p&gt; element, otherwise, the submit button will not work. Name of the editor must be the same as the name of the model that you're editing, in this case, OrderItem and place it under Views/Shared/EditorTemplates. If you don't have the EditorTemplates folder under Shared, create it.

&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FOrderItemTemplate.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FOrderItemTemplate.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we have the EditorTemplate we can use it in the Create.cshtml file to display the collection of the items. Usually, we place this part in a separate div element with specific id (in this case it's id="orderItemsContainer" so we can later change the content of this div with ajax.&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FUsingEditorInCreate.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FUsingEditorInCreate.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After displaying the items, we should create a button that can be pressed to add a new element to the list. Let's create the Add Button together with the javascript code, that will be used for the ajax call.&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FAddButonAndJavScript.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FAddButonAndJavScript.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To be able to serialize this form, we should also add an id to the form in the create file.&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FFormIdAndSerialize.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FFormIdAndSerialize.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you noted, we call a method AddOrderItem from Orders controller in the javascript ajax method, and this method we should also create in the controller and set up its binding. This method in the controller is adding a new element to the list and rendering the whole list as a partial view which is later shown in the HTML.&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FAddOrderItemMethod.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FAddOrderItemMethod.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the AddOrderItem method, we're returning PartialView where we render the list of the order items. Create a partial view in your Views/Order folder called OrderItems.cshtml. The Partial view is pretty much simple, it should only use the editor that we created previously.&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FPartialView.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FPartialView.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we have everything shown on the screen, now we should add the binding for the list in the create method of the controller and we are ready to use it (include Items in the binding list, because they are not included by default).&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FCreateMethod.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FCreateMethod.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that we changed the model, we added a default constructor that will initialize the list. Also, I've added a property that will give us the total number of items in the list, usually this logic should be in ViewModel, but for the sake of simplicity, I put it in the model.&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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FOrderModel.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%2Fgithub.com%2Fstevcooo%2FAddItemsDynamically%2Fraw%2Fmaster%2FAddItemsDynamically%2FMedia%2FOrderModel.png"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dynamically</category>
      <category>netcore</category>
      <category>ajax</category>
      <category>razor</category>
    </item>
    <item>
      <title>From zero to Rubik’s cube solving robot</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Tue, 22 Jan 2019 10:27:34 +0000</pubDate>
      <link>https://dev.to/stevcooo/from-zero-to-rubiks-cube-solving-robot-2ccj</link>
      <guid>https://dev.to/stevcooo/from-zero-to-rubiks-cube-solving-robot-2ccj</guid>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: I’ve built a robot that solves the Rubik's cube without any previous&lt;br&gt;
knowledge or experience with electronics or Raspberry Pi, Arduino, Python in&lt;br&gt;
less than a year as a pet project.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  How it all started
&lt;/h4&gt;

&lt;p&gt;It all started when I bought a Rubik’s cube just for fun. I had several of them&lt;br&gt;
when I was a kid, but I could never solve any of them. I even remember that I had some papers explaining how to solve it, but I didn’t understand any of it at the time. So, when I got my cube, one year ago, I put my mind to solve it and was only able to solve one side and maybe set the matching corners, but that was it, I couldn’t solve the next phase (the second layer). Then I found a tutorial on the internet “The beginner’s method”. It was a step by step tutorial how to solve any Rubik’s cube. In the beginning, it was a little bit frustrating, until I learned the notations and was able to understand the steps. Next phase was learning the order of the steps and their algorithms, and finally, I was able to solve my cube. This was my mental exercise for several days, maybe a week. After I mastered the “The beginner’s method”, I was able to solve the cube in ~2.5 minutes. After that the fun is gone, you know that you can solve the cube, and it’s not interesting anymore unless you need to kill some time.&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%2Fx969l3zb2bqesmyxycw5.jpeg" 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%2Fx969l3zb2bqesmyxycw5.jpeg" alt="My unsolved cubes"&gt;&lt;/a&gt;&lt;/p&gt;
My unsolved cubes


&lt;h4&gt;
  
  
  Algorithm challenge — represent the cube in code
&lt;/h4&gt;

&lt;p&gt;Next phase was to make a program that can solve the Rubik’s cube, not that weren’t any of them developed before, but I thought that it will be a nice exercise for me. Since the “Beginners method” is a simple algorithm that should be applied to the cube in a given order, it should be easy to develop a program that will do that for me. The first challenge was to make a good representation of the cube using Objects in OOP. I chose to use Test Driven Development (TDD), and I didn’t regret that at any moment. Writing a complicated software needs a lot of attention on details and requires keeping track of every algorithm, and that is impossible to be done without TDD, especially when you are not developing it every day, because it’s a pet project and you spend only a fraction of your free time on it. Without the test, you will be lost after 2 weeks development for sure, and you’ll never know if any of your changes break something until you apply all the steps to the cube, which is time-consuming and exhausting.&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%2F5o9angq1hw2tjb9uyfc9.jpeg" 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%2F5o9angq1hw2tjb9uyfc9.jpeg" alt="Mapping cube rotations"&gt;&lt;/a&gt;&lt;/p&gt;
Mapping cube rotations



&lt;p&gt;After several unit tests and objects, I was able to make first console application where I could visually see how my application was progressing, from scrambling to solving cube variations. When I was done with the algorithm and it passed all unit test, the application was able to define a Solved cube, scramble it, and solve it using the “Beginners method”, it was all done in ~200 steps. I was so amazed and proud, not that I did something that wasn’t done before, but because I did it completely by myself and my pet project was up and running.&lt;/p&gt;
&lt;h4&gt;
  
  
  Raspberry Pi
&lt;/h4&gt;

&lt;p&gt;Having an algorithm that solves the cube, and only works as a console&lt;br&gt;
application is not much fun. It is so abstract, it solves a real problem in the&lt;br&gt;
abstract environment. You can’t see/feel the actual solving, you can only define a matrix that represents a cube formation/combination and the algorithm will give you a set of rotations that you should perform to solve the cube. Next phase for me was to build a robot that will solve the cube. The only thing that it should do is to apply the steps of the algorithm output to a physical cube. I have never ever built or programmed a robot, I didn’t have any idea where I should start from. My idea was to have a motor that will do the rotations on the cube, that was the simplest model that came to my mind. After researching a little bit on the internet, I found out about step motors.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A stepper motor is a type of DC motor that works in discrete steps. It is a&lt;br&gt;
synchronous brushless motor where a full rotation is divided into several steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perfect, this was what I needed. I needed something that can make a 90 degrees rotation. But wait, to be able to control a step motor you need Arduino or Raspberry PI. I had zero experience in this area. I had never ever touched Arduino or Raspberry Pi, it was a completely mystic world for me. So, I decided to buy Raspberry PI and Arduino, just to explore them and see if I can do anything. After a month, it was delivered in my home and I started a whole new chapter in my computer experience.&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%2Fg860x97yn0ndt4fc9t2u.jpeg" 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%2Fg860x97yn0ndt4fc9t2u.jpeg" alt="Playing around with Arduino Nano and Raspberry Pi for the first time"&gt;&lt;/a&gt;&lt;/p&gt;
Playing around with Arduino Nano and Raspberry Pi for the first time



&lt;p&gt;After a while, I decided to continue with Raspberry Pi. It was easier for me because it was working on a higher level than the Arduino, and it has an operating system that you can use. I have never used a Raspberry PI, it was a completely unexplored world for me. So, my journey with Raspberry Pi had just stared. I watched several tutorials on YouTube, to get some basic knowledge of the domain. For the first time in my life, I wrote a script on Python. Apparently, you can do a lot of stuff using Python, there are a lot of libraries that will help you program Raspberry Pi pins. I wrote a sample Python script that will turn off and on LED, I was happy as a little child. In my childhood I was playing with LED and batteries, it was fun of course, but now I’m able to control LED using code. Amazing. After that a lot of ideas are floating in your brain, you can automate or program a lot of your daily tasks/routines. But I was stuck with my first idea, to be able to control a step motor to make 90 degrees rotation in a given direction. As I searched, I found out that there are several types of step motors. I wasn’t sure which one I need, so I bought the cheapest one (28BYJ-48 5V 4 Phase DC Gear Stepper Motor). Just to test it. After a month it was delivered at my home and I started to explore it. Of course, you need a driver (ULN2003 Driver Board) that will process your input and accordingly control the step motor. I found a few scripts online that were easy to use and implement. After a few hours, I was able to control my step motor and to control the movement by programming the number and direction of the angles.&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%2F7pf72f176ngu22sdabla.jpeg" 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%2F7pf72f176ngu22sdabla.jpeg" alt="28BYJ-48 motor"&gt;&lt;/a&gt;&lt;/p&gt;
28BYJ-48 motor



&lt;p&gt;I was so happy. The motor seemed to be very strong and had a high torque. I was sure that if I had 6 motors, one for each side of the cube, I’ll be able to control/solve the cube since I have the algorithm that will calculate the needed steps. I ordered 6 new step motors and 6 drivers for them, after a month, they were delivered, and I started to connect them to my Raspberry Pi. To spare some pins I was experimenting with Arduino Nano. I programmed my Arduino Nano to act as a multiplexer. I had 6 motors that should be running, but only one of them will be active at the given moment.&lt;/p&gt;

&lt;p&gt;With that logic, I was able to use 3 pins from my Raspberry to control which one of the 8 outputs (I needed only 6) of the Arduino Nano will be active (2³=8). But after one day spent with this one, I gave up on this idea. Luckily for me, there were enough pins on the Raspberry to connect all 6 motors directly to it. I was connecting the step motor drivers to the Raspberry Pi. Each driver has 4 signal + 2 power pins. After playing around for some time, all the 6 motors were wired up and I was able to control which one of them will rotate, for how long and to which direction. Great! Now I could control 6 motors, one for each side of the cube. I just want to mention that I fired 2 or 3 motors while testing, I considered it as a part of the development process, no worries, except that you will need to wait for a month for the delivery of the new motor from AliExpress.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/fOVS4aL_0OQ"&gt;
&lt;/iframe&gt;Playing around with Arduino Nano and Raspberry Pi for the first time
&lt;/p&gt;
&lt;h4&gt;
  
  
  API and mapping
&lt;/h4&gt;

&lt;p&gt;Now I have all 6 motors connected to Raspberry Pi and I can control them. Great. Now I need some service that will provide me with a list of steps that should be applied to the cube. &lt;br&gt;
I developed a simple API service that will return a list of solving steps. Raspberry using Python should be able to connect to this API service that will return the list of the steps required to solve the cube. But to get some result from the API you should somehow pass the current state of the cube, so the algorithm can make the calculations and return them via the service. Now I need a web interface where I can map the current state of the cube. I developed a simple website using JavaScript where you can map your Rubik’s cube, and according to your state the algorithm will calculate the steps and store them, so when you call the API from Python it will return you the steps. The website now is enhanced, and it provides the required steps on the web to solve your cube at home. (some screenshots here about the website and how to use it). Now I have a website where I can map my cube and a service that will calculate the solving steps, also my API can return those steps in a list that can be used in Raspberry Python code to control the motors.&lt;/p&gt;
&lt;h4&gt;
  
  
  Wooden era
&lt;/h4&gt;

&lt;p&gt;Having all the 6 step motors wired up and running, the next thing was to construct a frame that will hold all the motors and the cube between them in order to rotate the cube sides accordingly. Simple idea, only I needed to find a way to connect the step motor shaft with the center of each side of the cube. See the screenshots of my first connection. I was using a screw and a type of epoxy compound to construct something that will connect the shaft and the cube center. Now what is left is to construct a frame. I made a wooden cube that will hold the motors and the cube in the center. I did it, I have my first robot construction, I wired it up and it was working. Not as it should, but yes, it’s something. It was able to do a rotation, but not as I wanted. Because the frame wasn’t precise I think, the cube did not hold in the absolute center position and the motors weren’t able to do an exact 90 degrees rotation. Sometimes they were, but in most cases, they weren’t. If one of the sides was rotated for 85 degrees, next, the other side was unable to rotate because the previous one stands on its way. I was disappointed a little bit, maybe more than a little bit, but still, never give up.&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%2Fgex7q1z3r316r64x876q.jpeg" 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%2Fgex7q1z3r316r64x876q.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;
The first wooden case part 1



&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%2Fj256bv01jefkz1o0c3sb.jpeg" 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%2Fj256bv01jefkz1o0c3sb.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;
The first wooden case part 2



&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%2F4ch547xlim0pktsvh2h5.jpeg" 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%2F4ch547xlim0pktsvh2h5.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;
The first wooden case part 3


&lt;h4&gt;
  
  
  Wooden Era ||
&lt;/h4&gt;

&lt;p&gt;Next stage was to find a better connection between the cube and the shaft of the step motor. I found something on AliExpress that will do the job. I ordered it immediately. 12 of them, just in case. Since I was changing the connection between the shaft and the cube side, I decided to make a new wooden frame, that will be more precise and way smaller. I did it too, I was getting better in wood craftsmanship (Don’t ask my wife about all the mess I did in our apartment).&lt;/p&gt;

&lt;p&gt;The connectors arrived and I connected them with the shaft and the cube center using the new wooden frame. This should be it. But it wasn’t. Same old, same old. It had a little improvement but not enough. Now the rotations are between 85 and 90 degrees, but because of the small torques (which, in the beginning, seems to be enough) of the step motor, you can’t predict what will happen. You don’t have any feedback from the motor, you only send it a command to do 90 degrees rotation, but you don’t know if it does 90 or 80 or even 0 degrees radius. I mean you can see it visually, but the raspberry doesn’t have any idea (maybe in future I can extend this project to use a camera as a sensor for feedback, but maybe).&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/tiNWicCCbQs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
Second wooden case



&lt;h4&gt;
  
  
  Plexiglass era
&lt;/h4&gt;

&lt;p&gt;Ok, now I have a new connection between the shaft and the cube, but my wooden frame is not precise enough. I need something that is more precise. I found out a local store in my town that has a laser cutting machine and can apply my design on Plexiglass. I installed Adobe Illustrator and started designing a new frame for my robot. After several hours it was designed and ready to be produced. I contacted the company, send them my design and they produced it in two days. I was so happy. I pick up my new plexiglass frame, attached my step motors and the cube. It’s testing time. I mapped my cube on my website, run the API call, it received ~200 steps, and started rotating the sides. It was great, but for only for the first 3–4 rotations, then I had the same problem. Rotations weren’t exactly 90 degrees. Now everything depends on the step motors rotation torque. I was a little bit disappointed and almost out of ideas and hope for my Rubik’s cube solving robot.&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%2Fzr92zdtxjp5jf03xyvyf.jpeg" 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%2Fzr92zdtxjp5jf03xyvyf.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;
plexiglass case img 1



&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%2F8t18gdzufzwjqspcy3za.jpeg" 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%2F8t18gdzufzwjqspcy3za.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;
plexiglass case img 2



&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%2F6ltb2rb85dsk8xdqdeve.jpeg" 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%2F6ltb2rb85dsk8xdqdeve.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;
plexiglass case img 3



&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/H_ACvtJAXy8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
Rubik’s cube in a plexiglass case



&lt;h4&gt;
  
  
  Nema17
&lt;/h4&gt;

&lt;p&gt;After researching again, I found hope for my robot. I found out that a lot of moving machines (CNC machines) are using NEMA standard motors. I found out that Nema17 step motor will do the job. I have seen this step motors previously, but they were expensive for me in the beginning. Now they seemed like the only hope for my project. I ordered one of them just to test it, of course, they use different drivers, so I bought a new driver (they were cheap). According to some YouTube videos, I need to use a capacitor between the power supply and the step motor driver, that was something that I didn’t consider previously. I bought several of them, they are cheap too. After a month, Nema17 step motor arrived and I tested it. I was surprised by how strong and powerful it is. Immediately I ordered 5 more, so I can attach one to every side of the cube. Again, after a month, I have 6 Nema17 step motors and 6 new drivers, and 6 capacitors. Now I need some power supply that will provide enough power. Previously I used a simple DC adapter, but it wasn’t enough. After talking with one colleague of mine, he suggested using a desktop computer power supply. It has enough power to supply 6 motors with no problem. I already had a spare power supply at my home, so I used it.&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%2Fqbitkr81mb5wk5bkxe53.jpeg" 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%2Fqbitkr81mb5wk5bkxe53.jpeg" alt="NEMA 17 Step motors"&gt;&lt;/a&gt;&lt;/p&gt;
NEMA 17 Step motors



&lt;p&gt;Now, with all 6 motors connected to the power supply, I needed a new frame (again), because the Nema17 motors have different dimensions that required a completely new frame. This time I didn’t even consider using a wooden frame, I immediately designed a new plexiglass print and ordered it. After a day or two, I had a new frame. I connected everything, and it was working!!! Finally! I was so happy; my cube was solved by the robot. Eureka! It’s 2 AM in the morning, I’m excited as a little child, I need to solve the problem with all the wires in my desk, but before I spoil anything, I decided to go to sleep, because first time after 10 months I see my robot solving the cube.&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%2F89deedbcau567v91z0u6.jpeg" 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%2F89deedbcau567v91z0u6.jpeg" alt="New plexiglass case with NEMA 17"&gt;&lt;/a&gt;&lt;/p&gt;
New plexiglass case with NEMA 17



&lt;h4&gt;
  
  
  Soldering and PCB design
&lt;/h4&gt;

&lt;p&gt;Since I had 6 drivers connected to my Raspberry Pi, each one of them with 3 control pins, 1 pin for direction, 1 pin for step, 1 pin for ground, 2 pins for plover, and 4 pins exiting from the driver that go to the Nema17 step motor, I had ~80 jumper wires on my protoboard and it was a mess! When I looked at it, the first thought that came to my mind was “if one of the jumper cables comes out it might burn out some driver or motor”, I didn’t want that. To avoid such a hazard, I bought a PCB protoboard and started soldering the elements on it. This cleaned the mess. Now I had a custom PCB board where everything is connected and works as it should.&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%2Fg91a7r4udwylgs371ksq.jpeg" 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%2Fg91a7r4udwylgs371ksq.jpeg" alt="From left to right, from ~80 wires to PCB board."&gt;&lt;/a&gt;&lt;/p&gt;
~80 wires in the protoboard



&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%2Fshp6e320x592b08anfbp.jpeg" 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%2Fshp6e320x592b08anfbp.jpeg" alt="From left to right, from ~80 wires to PCB board."&gt;&lt;/a&gt;To PCB board&lt;/p&gt;

&lt;h4&gt;
  
  
  Let’s give the users some feedback
&lt;/h4&gt;

&lt;p&gt;After I had a working robot which handles ~200 rotations to solve the cube, the user (observer) doesn’t have any idea when it’s going to end. It’s only a simple 90 degrees rotations until you see the solved cube. I had a small display that I used to display a step-down counter, so the observer has an idea of how many steps are left until the cube is solved.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/P-DITcE-pOw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
Solving the cube using the Beginners method with step down counter



&lt;h4&gt;
  
  
  Utilize the robot
&lt;/h4&gt;

&lt;p&gt;Solving the cube in ~200 steps is nice, but it’s not fast. The Beginners method is good for learning how to solve the cube, but it’s the slowest algorithm. Now I need something that will solve the cube much faster. After a little research on the internet, I found out about the F2L algorithm that can solve any cube in under 23 steps. That’s nice. The only thing I need is to implement that code in my application and use it. So, I did. I used Kociemba implementation &lt;a href="http://kociemba.org/cube.htm" rel="noopener noreferrer"&gt;http://kociemba.org/cube.htm&lt;/a&gt;. It works great, I just implemented it in C# and used it in my service. Now the cube is solved in 23–25 steps.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/hinvKkamiUI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
Full cube solve with Kociemba (F2L) algorithm



&lt;h4&gt;
  
  
  Lesson learned
&lt;/h4&gt;

&lt;p&gt;I’ve spent almost one year on this project, this is the longest pet project that I have been working on. In the past, I’ve had many pet projects, from simple websites to various full stack websites and several mobile applications. I have tried out a lot of technologies and development stacks, but nothing keeps my attention like this one. Maybe because in this project for the first time in my carrier I tried all layers of the development, from hardware to software, from the basic wirings and voltage adjustments to API server call. &lt;br&gt;
If you reach this part of the blog, you already noted that I had more failures than successes during the development process, but the small achievements/milestones during the process are what keeps your desire alive. I never give up, but to be able to do that, whatever your goal is split it to several milestones, that way you will more quickly get some satisfaction from something you achieved and that is what gives you boost for the next small goal. Any pet project is time-consuming but do it in a way that you’ll have fun during the development, share some of your achievements with your family or colleagues, they will give you some feedback and encouragement for your next steps. I had a&lt;br&gt;
few improvement ideas from feedback that I received from my close ones. The easiest way to learn something new is when you have fun doing it.&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%2Fqrcnj5815kld4wcie369.jpeg" 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%2Fqrcnj5815kld4wcie369.jpeg" alt="The making of"&gt;&lt;/a&gt;&lt;/p&gt;
The making of



&lt;p&gt;If you have any question or suggestion please don’t hesitate to contact me, I’m willing to help you in any way that I’m capable of. If you want some peace of code from my project or any help, please let me know. &lt;/p&gt;

&lt;p&gt;The website that I have developed that helps you to solve the Rubik’s cube can be found on the following link &lt;a href="http://rubiks.kostoski.com/" rel="noopener noreferrer"&gt;http://rubiks.kostoski.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lastly, if you found this post useful, please feel free to share + give it&lt;br&gt;
*👏&lt;/em&gt;️ so others can find it too!*&lt;/p&gt;

</description>
      <category>robot</category>
      <category>inspiration</category>
      <category>petproject</category>
      <category>raspberrypi</category>
    </item>
    <item>
      <title>Access your daily URL's using a shortcut</title>
      <dc:creator>Stevan Kostoski</dc:creator>
      <pubDate>Mon, 14 Jan 2019 08:06:45 +0000</pubDate>
      <link>https://dev.to/stevcooo/access-your-daily-urls-using-a-shortcut-48od</link>
      <guid>https://dev.to/stevcooo/access-your-daily-urls-using-a-shortcut-48od</guid>
      <description>&lt;p&gt;Hello,&lt;br&gt;
this is my first post here, I decided to post this here because I believe that most of the people that uses this site have the same problem as I had. I hope that this will ease your daily work and increase your productivity.&lt;/p&gt;

&lt;p&gt;I'm a developer for 6+ years, and no matter on which project I'm working on there is always a list of URL's that I'm opening every day. For example Jira stories. They all seem the same, but only the last part of the path is different. Since I'm using Google Chrome I developed an extension that will help me access these urls.&lt;/p&gt;

&lt;p&gt;You can install the extension from &lt;a href="https://chrome.google.com/webstore/detail/combine-url/lnmkibhfmgahenghonphjlepcdbdjpon"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, if you have a lot of tasks in Jira and all they have the same base URL but only the last few characters are different.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jira.yourcompany.com/browse/AX-3830"&gt;https://jira.yourcompany.com/browse/AX-3830&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jira.yourcompany.com/browse/AX-3822"&gt;https://jira.yourcompany.com/browse/AX-3822&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jira.yourcompany.com/browse/AX-2592"&gt;https://jira.yourcompany.com/browse/AX-2592&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can set up this extension as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tf1YLBL0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/urlcombine/master/img/sampleSetUP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tf1YLBL0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/stevcooo/urlcombine/master/img/sampleSetUP.png" alt="Sample set up" width="393" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And when you press CTR+SHIFT+F, a popup will appear where you only write the last 4 characters, for example 3830, and by pressing enter or Go button, the link will be opened in new tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e-8ckTLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/stevcooo/urlcombine/blob/master/img/taskUrl.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e-8ckTLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/stevcooo/urlcombine/blob/master/img/taskUrl.png%3Fraw%3Dtrue" alt="Sample link" width="396" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested on the source code you can find it here: &lt;a href="https://github.com/stevcooo/urlcombine"&gt;https://github.com/stevcooo/urlcombine&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Blog
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://blog.kostoski.com/blog/9"&gt;https://blog.kostoski.com/blog/9&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>jira</category>
      <category>chrome</category>
      <category>extension</category>
    </item>
  </channel>
</rss>
