{"id":23,"date":"2021-10-22T04:05:44","date_gmt":"2021-10-22T04:05:44","guid":{"rendered":"https:\/\/blogs.oregonstate.edu\/sandfield\/?p=23"},"modified":"2021-10-22T04:05:44","modified_gmt":"2021-10-22T04:05:44","slug":"regret-regret-regret","status":"publish","type":"post","link":"https:\/\/blogs.oregonstate.edu\/sandfield\/2021\/10\/22\/regret-regret-regret\/","title":{"rendered":"Regret Regret Regret"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/image.halocdn.com\/?path=https:%2F%2Fcontent.halocdn.com%2Fmedia%2FDefault%2Fencyclopedia%2Fcharacters%2Fprophet-of-regret%2Fprophet-of-regret-hero-large-1920x675-4f22e2731b51436f8afa2347ab42a725.jpg&amp;leftCrop=560&amp;rightCrop=560&amp;hash=DyWhzXz%2FoyR0I0kVS7x21pVj3PUWIUrr0yKAOFDGb64%3D\" alt=\"Prophet of Regret | Characters | Universe | Halo - Official Site\" width=\"200\" height=\"169\" \/><\/figure><\/div>\n\n\n\n<p>Regret is a strong word. I have regrets. Monoliths have their advantages, and I am rapidly and repeatedly finding them as I implement a microservice ecosystem for the first time. This week I set out to define the gateway API and dummy routes in the user service to experiment with multi-layer API calls. Then I sat down and realized that in order to talk to each other, the services each needed to know where the others were. I <em>could<\/em> hardcode URLs and rely on everything working perfectly and never changing; you already know why that&#8217;s a bad idea. I <em>could<\/em> have the gateway service pull double duty and have <em>it&#8217;s<\/em> address hardcoded into all other services; this would disobey a core principle of microservice philosophy and not actually save any trouble vs doing it properly.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn.quotesgram.com\/img\/82\/92\/24959667-Winnie-the-Pooh-think-think-think.jpg\" alt=\"Pooh Bear Quotes About Thinking. QuotesGram\" width=\"339\" height=\"186\" \/><\/figure><\/div>\n\n\n\n<p>So I now have a new coordinator service which will have its address provided via config files in all the other microservices and will be queried to retrieve the URLs for services of interest. I also got to learn how to create and use a custom Node module in order to keep to DRY principles; if every microservice is going to need to talk to the coordinator, then there needs to be a standardized block of code that I don&#8217;t need to worry about fixing everywhere any time it needs a fix.<\/p>\n\n\n\n<p>Starting with the module, it turns out that the <a href=\"https:\/\/docs.npmjs.com\/creating-node-js-modules\">basic documentation<\/a> is really all that was needed to actually do the module-specific pieces, although what they don&#8217;t tell you is that the ability to publish a private module requires a premium subscription. The module code was also pretty simple; private variables to store the needed URLs, setters for the coordinator URL and other service URLs, and two layers of getters for a service URL &#8211; if it is already stored and by making a query to the coordinator if it isn&#8217;t. Testing is where the real fun was. I finally got to knock out some Nock, and I had to pull on a <a href=\"https:\/\/medium.com\/@bhegde.leena\/unit-testing-node-js-api-using-mocha-chai-sinon-and-nock-881eb8795679\">new tutorial<\/a> on top of what I&#8217;ve linked in the past. Setting up the dummy routes wasn&#8217;t actually so bad, but interfacing it with Chai and the request module proved to be a hassle. Request has been deprecated, so I needed to sift through a decade of now out of date information, the HTTPS just did not give me any compatible output, and so I settled on Axios as a module that I&#8217;ve used in the past. Even then, it took a couple hours of trial and error with the Chai documentation to get the async tests to validate correctly without timing out.<\/p>\n\n\n\n<p>Then there was the matter of the coordinator service itself. Again, easy to code but a pain to figure out correct testing. There&#8217;s just the GETter and SETer routes and supporting model functions but I still don&#8217;t have a handle on Chai. Several timeouts, errors, and false test passes later I got to here:<\/p>\n\n\n\n<p><code>describe('\/POST service', function() {<\/code><br><code>it('should return a 201 status with self address attached', function(done) {<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 chai.request(app)<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .post('\/services\/' + serviceName)<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .end((err, res) =&gt; {<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 res.should.have.status(201);<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 let address = res.body[serviceName];<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 address.substr(0, address.indexOf(\":\")).should.be.an.ip;<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 done();<\/code><br><code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 });<\/code><br><code>\u00a0 \u00a0 })<\/code>;<br><code>});<\/code><\/p>\n\n\n\n<p>The major lessons learned to make it work:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>You can&#8217;t use an arrow function (() =&gt; {}) with Chai and keep things stable due to binding.<\/li><li>Without a done() an async function will time out<\/li><li>An address with a port number does not register as an IP address in Chai<\/li><li>Anything can be solved with judicious use of string manipulation<\/li><\/ul>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/img.picturequotes.com\/2\/130\/129905\/if-it-looks-stupid-but-works-it-aint-stupid-quote-1.jpg\" alt=\"If it looks stupid but works.. it ain&#039;t stupid | Picture Quotes\" width=\"287\" height=\"400\" \/><\/figure><\/div>\n\n\n\n<p>Once there was a module and coordinator service to test with, it was time to make an experimental service to use the module and talk to the coordinator. Again with the testing woes. I ended up refactoring the module to export a class rather than a collection of functions, which also solved the issue of assuming globality &#8211; each router file can safely have its own copy of the URL completer without much overhead. Once that was done, I was able to pretty much copy over the (also refactored) tests from the module into my experimental service. As of now I haven&#8217;t actually done an integration test for the services, so it might all need to be fixed again once I try running them together.<\/p>\n\n\n\n<p>Just when I thought I was done with my re-figured week, I got to learn a whole new thing: working with SSH keys in GitHub actions. I&#8217;ll let you know how that all works out next week, but so far it&#8217;s been more &#8216;trying things and having them not work&#8217;.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img decoding=\"async\" src=\"http:\/\/2.bp.blogspot.com\/-if-zBdDW8EM\/TfdbP2RP8OI\/AAAAAAAAAgE\/BeFhmoFJ470\/s640\/Headondesk.png\" alt=\"Culture Connoisseur: A Severe Case of Headdesk\" \/><\/figure><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Regret is a strong word. I have regrets. Monoliths have their advantages, and I am rapidly and repeatedly finding them as I implement a microservice ecosystem for the first time. This week I set out to define the gateway API and dummy routes in the user service to experiment with multi-layer API calls. Then I [&hellip;]<\/p>\n","protected":false},"author":11634,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-23","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/posts\/23","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/users\/11634"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/comments?post=23"}],"version-history":[{"count":3,"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/posts\/23\/revisions"}],"predecessor-version":[{"id":27,"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/posts\/23\/revisions\/27"}],"wp:attachment":[{"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/media?parent=23"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/categories?post=23"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/sandfield\/wp-json\/wp\/v2\/tags?post=23"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}