{"id":825,"date":"2020-03-30T16:00:00","date_gmt":"2020-03-30T16:00:00","guid":{"rendered":"http:\/\/bullyrooks.com\/index.php\/2020\/04\/10\/simple-spring-boot-service-to-kubernetes-application-step-16-52691f97f46f\/"},"modified":"2021-02-04T02:08:03","modified_gmt":"2021-02-04T02:08:03","slug":"simple-spring-boot-service-to-kubernetes-application-step-16-52691f97f46f","status":"publish","type":"post","link":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-16-52691f97f46f\/","title":{"rendered":"Web UI with React"},"content":{"rendered":"\n<p class=\"graf graf--p graf-after--h3 graf--trailing\" id=\"3c06\">I have not written a front end application in almost 20 years. Lets try something new. I\u2019m going to be starting slow with a \u2018Hello World\u2019 application using typescript and node to deploy to our kubernetes cluster.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf--leading wp-block-heading\" id=\"b89d\">Setting Up the Environment<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"47cc\">I\u2019m still on windows (sigh) and was having trouble getting node\/npm\/parcel working on the command line, so I swapped over to an ubuntu distro running in a windows VM. I\u2019ll skip the pain, but there is a pretty good article about how to do that <a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/nodejs\/setup-on-wsl2\" target=\"_blank\" rel=\"noopener\" data-href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/nodejs\/setup-on-wsl2\">here<\/a>.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"79ef\">After that, its fairly straightforward to get your project moving forward. You can follow this simple example <a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/www.robinwieruch.de\/parcel-react-setup\" target=\"_blank\" rel=\"noopener\" data-href=\"https:\/\/www.robinwieruch.de\/parcel-react-setup\">here<\/a>.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p graf--trailing\" id=\"33f1\">I\u2019ve also been using <a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/code.visualstudio.com\/download\" target=\"_blank\" rel=\"noopener\" data-href=\"https:\/\/code.visualstudio.com\/download\">VSCode <\/a>which has a pretty slick integration with the ubuntu running in your virtual machine.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf--leading wp-block-heading\" id=\"f6d9\">Building the&nbsp;Service<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"71a4\">Create a new repository called <code class=\"markup--code markup--p-code\">medium-customer-manager<\/code>. Clone it locally and start a new branch <code class=\"markup--code markup--p-code\">first-service<\/code>.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"d100\">Make sure that you have node and npm available on your commandline<\/p>\n\n\n\n<pre id=\"0928\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>brian@Redoubt:~\/workspace\/medium-customer-manager$ node --version\nv13.12.0\nbrian@Redoubt:~\/workspace\/medium-customer-manager$ npm --version\n6.14.4<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"fa7b\">I\u2019m going to be roughly the tutorial <a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/pusher.com\/tutorials\/consume-restful-api-react\" target=\"_blank\" rel=\"noopener\" data-href=\"https:\/\/pusher.com\/tutorials\/consume-restful-api-react\">here<\/a>. Create an initial project layout with npm<\/p>\n\n\n\n<pre id=\"41d6\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>npm i create-react-app\nnpx create-react-app medium-customer-manager<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"53e5\">First make the \u201cDTO\u201d structure that will hold our customer object<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"a018\">in change <code class=\"markup--code markup--p-code\">src\/App.js<\/code> to look like this:<\/p>\n\n\n\n<pre id=\"25a1\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>import React, { Component } from 'react';\nimport Customers from '.\/components\/customers';\n\n    class App extends Component {\n      state = {\n        customers: &#91;]\n      }\n\n      componentDidMount() {\n        fetch(process.env.REACT_APP_CUSTOMER_HOST+'\/customer\/all', {\n          method: 'get',\n          headers: new Headers({\n            'Content-Type': 'application\/json'\n          })\n        })\n        .then(res =&gt; res.json())\n        .then((data) =&gt; {\n          this.setState({ customers: data })\n        })\n        .catch(console.log)\n      }\n\n      render() {\n        return (\n          &lt;Customers customers={this.state.customers} \/&gt;\n        )\n      }\n    }\n\n    export default App;<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote graf graf--blockquote graf-after--pre is-layout-flow wp-block-quote-is-layout-flow\" id=\"3d6e\"><p>This is our call to our service endpoint. We\u2019re using an environment variable for the host, so that we can change it out. I <em class=\"markup--em markup--blockquote-em\">think<\/em> that this only works at build time though, so its not ideal and is only for short term integration. In this logic we\u2019re grabbing the JSON from our list all customers endpoint and putting it in our state holder. Next we\u2019re calling into our Customers page passing in this data, so lets look at that.<\/p><\/blockquote>\n\n\n\n<p class=\"graf graf--p graf-after--blockquote\" id=\"ab57\">Create <code class=\"markup--code markup--p-code\">src\/components\/customers.js<\/code> and add this data:<\/p>\n\n\n\n<pre id=\"0574\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>import React from 'react'\n\nconst Customers = ({ customers }) =&gt; {\n    return (\n    &lt;div&gt;\n        &lt;center&gt;&lt;h1&gt;Customer List&lt;\/h1&gt;&lt;\/center&gt;\n        &lt;table id=\"dtBasicExample\" className=\"table table-striped table-bordered table-sm\" cellSpacing=\"0\" width=\"100%\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;th className=\"th-sm\"&gt;CustomerId\n\n                    &lt;\/th&gt;\n                    &lt;th className=\"th-sm\"&gt;Customer Name\n\n                    &lt;\/th&gt;\n                    &lt;th className=\"th-sm\"&gt;Customer Email\n\n                    &lt;\/th&gt;\n                    &lt;th className=\"th-sm\"&gt;Customer Phone\n\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n            {customers.map((customer) =&gt; (\n                &lt;tr key={customer.customerId}&gt;\n                    &lt;td&gt;{customer.customerId}&lt;\/td&gt;\n                    &lt;td&gt;{customer.firstName} {customer.lastName}&lt;\/td&gt;\n                    &lt;td&gt;{customer.email}&lt;\/td&gt;\n                    &lt;td&gt;{customer.phoneNumber}&lt;\/td&gt;\n                &lt;\/tr&gt;\n            ))}\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n    )\n};\n\nexport default Customers<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote graf graf--blockquote graf-after--pre is-layout-flow wp-block-quote-is-layout-flow\" id=\"64fb\"><p>Its a simple table, but the important part is that we\u2019re referring to this section as <code class=\"markup--code markup--blockquote-code\">Customers<\/code> which i believe helps react understand that its going to replace the <code class=\"markup--code markup--blockquote-code\">Customers <\/code>in the <code class=\"markup--code markup--blockquote-code\">App.js<\/code> with this html.<\/p><\/blockquote>\n\n\n\n<p class=\"graf graf--p graf-after--blockquote\" id=\"64dc\">Next we need to review <code class=\"markup--code markup--p-code\">src\/index.js<\/code>, which is the \u2018master\u2019 component. it should look like this:<\/p>\n\n\n\n<pre id=\"b36f\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from '.\/App';\nimport * as serviceWorker from '.\/serviceWorker';\n\nReactDOM.render(\n  &lt;React.StrictMode&gt;\n    &lt;App \/&gt;\n  &lt;\/React.StrictMode&gt;,\n  document.getElementById('root')\n);\n\n\/\/ If you want your app to work offline and load faster, you can change\n\/\/ unregister() to register() below. Note this comes with some pitfalls.\n\/\/ Learn more about service workers: https:\/\/bit.ly\/CRA-PWA\nserviceWorker.unregister();<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote graf graf--blockquote graf-after--pre is-layout-flow wp-block-quote-is-layout-flow\" id=\"d088\"><p>Here we\u2019re telling react to replace the root div element in index.html with the html rendered by App (which is generated by our App.js).<\/p><\/blockquote>\n\n\n\n<p class=\"graf graf--p graf-after--blockquote\" id=\"3c81\">Now let\u2019s take a look at <code class=\"markup--code markup--p-code\">public\/index.html<\/code><\/p>\n\n\n\n<pre id=\"ab50\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n  &lt;head&gt;\n    &lt;meta charset=\"utf-8\" \/&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/&gt;\n    &lt;meta\/&gt;\n    &lt;link rel=\"stylesheet\" href=\"https:\/\/stackpath.bootstrapcdn.com\/bootstrap\/4.1.3\/css\/bootstrap.min.css\" integrity=\"sha384-MCw98\/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO\" crossorigin=\"anonymous\"&gt;\n    &lt;!--\n      manifest.json provides metadata used when your web app is installed on a\n      user's mobile device or desktop. See https:\/\/developers.google.com\/web\/fundamentals\/web-app-manifest\/\n    --&gt;\n    &lt;link rel=\"manifest\" href=\"%PUBLIC_URL%\/manifest.json\" \/&gt;\n    &lt;title&gt;Customer Management&lt;\/title&gt;\n  &lt;\/head&gt;\n  &lt;body&gt;\n    &lt;noscript&gt;You need to enable JavaScript to run this app.&lt;\/noscript&gt;\n    &lt;div id=\"root\"&gt;&lt;\/div&gt;\n\n  &lt;\/body&gt;\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote graf graf--blockquote graf-after--pre is-layout-flow wp-block-quote-is-layout-flow\" id=\"ce63\"><p>Nothing crazy here\u2026 react is going to use index.js by default and follow our chain of logic down to the table render and replace the root div. I <em class=\"markup--em markup--blockquote-em\">am <\/em>using bootstrap here, just because I heard about it and thought it might be easier to write a multi-platform app with it.<\/p><\/blockquote>\n\n\n\n<p class=\"graf graf--p graf-after--blockquote\" id=\"847c\">I did remove a bunch of cruft that is automatically generated by create-react-app. I would probably use parcel in the future instead, but this is what got me going and I needed a quick win with the UI in order to build my confidence.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"8842\">Create an environment variable to point to our customer-manager instance (running in the IDE)<\/p>\n\n\n\n<pre id=\"4163\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>export REACT_APP_CUSTOMER_HOST=\"<a class=\"markup--anchor markup--pre-anchor\" href=\"http:\/\/localhost:10000\" target=\"_blank\" rel=\"nofollow noopener\" data-href=\"http:\/\/localhost:10000\">http:\/\/localhost:10000<\/a>\"<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"6889\">now npm install to make sure that we have all of our dependencies<\/p>\n\n\n\n<pre id=\"5390\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>npm install<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"c0df\">and start the app within the built in server<\/p>\n\n\n\n<pre id=\"4313\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>npm start<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"1b0d\">this should startup a browser window and you should see the customer list populated from any data that you may have previously saved.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/02\/1LrzwbhqqgYmXiPkFY-_BeA.png?w=960\" alt=\"\" data-recalc-dims=\"1\"\/><\/figure>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--figure wp-block-heading\" id=\"e531\">Build and&nbsp;Commit<\/h3>\n\n\n\n<pre id=\"3a96\" class=\"wp-block-code graf graf--pre graf-after--h3\"><code>git checkout first-service\ngit add .\ngit commit -m \"customer list page\"\ngit push\ngit checkout master\ngit merge first-service\ngit push<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<div class=\"entry-summary\">\nI have not written a front end application in almost 20 years.&hellip;\n<\/div>\n<div class=\"link-more\"><a href=\"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-16-52691f97f46f\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &ldquo;Web UI with React&rdquo;<\/span>&hellip;<\/a><\/div>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[99,29,98,96,100,97],"course":[40],"class_list":["post-825","post","type-post","status-publish","format-standard","hentry","category-software-development","tag-front-end","tag-git","tag-npm","tag-react","tag-ui","tag-webapp","course-spring-with-kubernetes","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":820,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-17-3867eb38d8f1\/","url_meta":{"origin":825,"position":0},"title":"Containerizing our UI","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"We\u2019ve got a simple page that calls one of our endpoints working. Let\u2019s go ahead an containerize it so that we can deploy it into our k8s environment in future steps. Build the Dockerfile I\u2019m following the process laid out here, with some minor differences. Create a Dockerfile # Stage\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":832,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-22-e37d26471e9e\/","url_meta":{"origin":825,"position":1},"title":"Conclusion and Review","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"Up to this point we\u2019ve been writing, testing, building, deploying and integrating services. Lets take a moment to reflect on what we have accomplished and why. Here we are at 22 articles in. That seems like a lot, but I wanted to write these articles in an easily ingested way\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":815,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-14-8e1ade0b7b84\/","url_meta":{"origin":825,"position":2},"title":"System Design","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"We\u2019ve successfully deployed a service and confirmed that its accessible. However, a single service doesn\u2019t make an app. We now need to fill out the rest of the application and introduce some best practices around service governance and system engineering. The Application Design We\u2019re going to be organizing the UI\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":833,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-18-9dff659cd334\/","url_meta":{"origin":825,"position":3},"title":"UI Build Pipeline","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"Simple Spring Boot Service to Kubernetes Application: Step\u00a018 Lets quickly update our build pipeline to push an image for our front end application into our repo. This opens the path to using helm to package and deploy our chart. Create a New\u00a0Pipeline Log into codefresh and go into pipelines. Choose\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":835,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-6-c52f5247ba20\/","url_meta":{"origin":825,"position":4},"title":"Documentation and Code Coverage","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"Before we move on to getting our service ready for deployment we need to do a little more housecleaning. Sometimes these tasks get de-prioritized so we might as well take care of it now, before we move on. Endpoint Documentation Using OpenAPI is a powerful tool for documenting our REST\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":818,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-19-944a76415384\/","url_meta":{"origin":825,"position":5},"title":"Put the UI in to Helm","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"Lets take the docker image we just created and wrap it with helm so that we have an easy way to deploy our application. Create Helm Chart Templates We\u2019ve created helm charts before, so lets create a helm directory at the frontend root. Then run helm create in that directory\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/825","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/comments?post=825"}],"version-history":[{"count":2,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/825\/revisions"}],"predecessor-version":[{"id":907,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/825\/revisions\/907"}],"wp:attachment":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/media?parent=825"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/categories?post=825"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/tags?post=825"},{"taxonomy":"course","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/course?post=825"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}