<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Cognizant Transmutation]]></title><description><![CDATA[Cognizant Transmutation]]></description><link>https://hashnode.ibd.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 07 Sep 2026 09:37:51 GMT</lastBuildDate><atom:link href="https://hashnode.ibd.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Use Amplify Studio Figma Connector with Clojurescript]]></title><description><![CDATA[Amplify Studio / Figma / Clojurescript / Reagent Tutorial
Implements the AWS Tutorial Build a Vacation Rental Site with Amplify Studio but instead of being Javascript based, uses Clojurescript for the project implementation. It does incorporate the J...]]></description><link>https://hashnode.ibd.com/how-to-use-amplify-studio-figma-connector-with-clojurescript</link><guid isPermaLink="true">https://hashnode.ibd.com/how-to-use-amplify-studio-figma-connector-with-clojurescript</guid><dc:creator><![CDATA[Robert J. Berger]]></dc:creator><pubDate>Sun, 09 Jan 2022 21:56:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313807225/Zeijwuvrt.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-amplify-studio-figma-clojurescript-reagent-tutorial">Amplify Studio / Figma / Clojurescript / Reagent Tutorial</h2>
<p>Implements the AWS Tutorial <a target="_blank" href="https://welearncode.com/studio-vacation-site/">Build a Vacation Rental Site with Amplify Studio</a> but instead of being Javascript based, uses Clojurescript for the project implementation. It does incorporate the Javascript output of Amplify Studio but all code to use it is in Clojurescript.</p>
<h3 id="heading-tooling-used-in-the-project">Tooling Used in the project:</h3>
<ul>
<li><a target="_blank" href="https://github.com/AutoScreencast/create-reagent-app">Create Reagent App</a> to create the project scaffold</li>
<li><a target="_blank" href="https://clojurescript.org">Clojurescript</a> (The whole point of this article!)</li>
<li><a target="_blank" href="http://shadow-cljs.org/">Shadow-CLJS</a> as the build tool / compiler</li>
<li><a target="_blank" href="https://webpack.js.org">Webpack</a> Key to preping JSX and JS Files from Amplify Studio and UI Components to be used with shadlow-cljs transpiled clojurescript.</li>
<li><a target="_blank" href="https://babeljs.io">Babel</a> Used by webpack to convert JSX to JS</li>
<li><a target="_blank" href="https://github.com/reagent-project/reagent">Reagent</a> (CLJS wrapper around <a target="_blank" href="https://reactjs.org/">React</a>) for building your user interface</li>
<li><a target="_blank" href="https://aws.amazon.com/amplify/studio/">Amplify Studio</a> and all the related <a target="_blank" href="https://aws.amazon.com/amplify/">AWS Amplify tooling</a></li>
<li><a target="_blank" href="https://www.figma.com/community/file/1047600760128127424">Figma AWS Amplify UI Kit</a></li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<h3 id="heading-setup-an-amplify-studio-project">Setup an Amplify Studio Project</h3>
<p>All the initial setup of the Amplify Studio Project on AWS and the associated Figma project is already described in the first part of the excellent <a target="_blank" href="https://welearncode.com/studio-vacation-site/">Build a Vacation Rental Site with Amplify Studio</a> so will not repeat it here.</p>
<p>That first part of the article will have you do all the following in the appropriate Web Consoles and services AWS and Figma). You won't be doing any CLI commands on your local dev computer:</p>
<ul>
<li>Setup an Amplify Studio project via the Amplify Sandbox</li>
<li>Create a basic data model in Amplify Studio</li>
<li>Deploy the start of the Amplify project to AWS</li>
<li>Create some sample data</li>
<li>Set up a Figma project using the Amplify UI Components and shows you how to modify it</li>
<li>Import the modified Figma project into the Amplify project</li>
<li>Link the data model and the UI Component in Amplify Studio</li>
<li>Create a collection view using Amplify Studio</li>
</ul>
<p><strong>Start by following the instructions from the original article, <a target="_blank" href="https://welearncode.com/studio-vacation-site/">Build a Vacation Rental Site with Amplify Studio</a>, up thru to the section: <code>Pull to Studio</code></strong>
Once you have completed that, come back to here and follow the rest of this post at this point.</p>
<h2 id="heading-creating-an-amplify-studio-app-with-clojurescript">Creating an Amplify Studio App with Clojurescript</h2>
<p>This is the actual instructions on how to create your Amplify Studio app in Clojurescript instead of Javascript. It replaces the remainder all the content after <code>Pull to Studio</code> from the original article <a target="_blank" href="https://welearncode.com/studio-vacation-site/">Build a Vacation Rental Site with Amplify Studio</a>.</p>
<h3 id="heading-create-a-git-repo-with-shadow-cljs-reagent-scaffolding">Create a git repo with shadow-cljs / reagent scaffolding</h3>
<p>Instead of using <code>create-react-app</code> that would have created a Javascript/React app, we’re going to use <a target="_blank" href="https://www.npmjs.com/package/create-reagent-app">create-reagent-app</a> to create the scaffolding of a shadow-cljs / reagent / react app repo.</p>
<p>In this tutorial, we will make this a git repo and snapshot the state at every stage so that if you make a mistake you can go back to an earlier step.</p>
<pre><code class="lang-bash">npx create-reagent-app  amplifystudio-cljs-tutorial
<span class="hljs-built_in">cd</span> amplifystudio-cljs-tutorial
git init
git add -A
git commit -m <span class="hljs-string">"Initial Commit after create-reagent-app"</span>
npm-install
</code></pre>
<h3 id="heading-add-webpack-and-related-dependencies">Add webpack and related dependencies</h3>
<p>Shadow-cljs can not directly consume JSX files that are the output of the Figma plugin and it needs some help to incorporate the AWS UI Components files that Amplify Studio injects into the project.</p>
<p>The use of Babel to prepare JSX files for Shadow-cljs is based on info from <a target="_blank" href="https://shadow-cljs.github.io/docs/UsersGuide.html#_javascript_dialects">Shadow CLJS User’s Guide - JavaScript Dialects</a>. This tutorial moves the babel management into webpack configuration as described later on.</p>
<p>The following dependencies are needed primarily to install webpack and its dependencies.</p>
<p><code>html-webpack-plugin</code> and <code>html-beautifier-webpack-plugin</code> are used to inject the proper JS include for the output of webpack into the index.html.</p>
<pre><code class="lang-bash">npm i -D @babel/cli @babel/core @babel/preset-react @babel/preset-env babel-loader html-webpack-plugin html-beautifier-webpack-plugin process webpack webpack-cli
</code></pre>
<p>Then update any dependencies to the latest versions
If you don’t already have it, install <a target="_blank" href="https://www.npmjs.com/package/npm-check-updates">npm-check-updates</a></p>
<pre><code>npm install <span class="hljs-operator">-</span>g npm<span class="hljs-operator">-</span>check<span class="hljs-operator">-</span>updates
</code></pre><p>And then run it to update any dependencies to the latest versions ignoring specified versions in the package.json.</p>
<p>I like to start projects with the latest versions of everything. But you could just make sure <code>shadow-cljs</code> is the latest version, best to stay latest with that.</p>
<p>If you run it without the <code>-u</code> it will just show you what it would update and you could manually update the ones you care about.</p>
<pre><code class="lang-bash">ncu -u
npm install
</code></pre>
<h3 id="heading-update-your-local-git-repo">Update your local git repo</h3>
<pre><code class="lang-bash">git add -A
git commit -m <span class="hljs-string">"Snapshot after adding webpack dependencies"</span>
</code></pre>
<p>If you want, you could push it to your own remote Github or other repository</p>
<h3 id="heading-add-aws-dependencies">Add AWS Dependencies</h3>
<ul>
<li>AWS Account<ul>
<li>If you don’t already have an AWS account, you’ll need to create one in order to follow the steps outlined in this tutorial. <a target="_blank" href="https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start">Create an AWS Account</a></li>
</ul>
</li>
<li>Amplify CLI
If you don’t already have the Amplify CLI installed you can install it with</li>
</ul>
<pre><code class="lang-bash">npm install -g @aws-amplify/cli
</code></pre>
<ul>
<li>Configure Account / IAM / CLI to work with Amplify
If you already have an AWS account you want to use and you have things setup in your workstation / Terminal to use AWS CLI via profiles in ~/.aws/credentials, you can just set your profile in your terminal for the profile to use</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> AWS_PROFILE=&lt;your profile&gt;
</code></pre>
<p>and you don’t need to do <code>amplify configure</code>.</p>
<p>If you haven’t set up aws amplify on your local dev machine before, follow the instructions at <a target="_blank" href="https://docs.amplify.aws/cli/start/install/#configure-the-amplify-cli">Configure the Amplify CLI</a></p>
<h3 id="heading-install-the-aws-amplify-libraries-in-your-project">Install the aws-amplify libraries in your project</h3>
<p>Still at the top of the <code>amplifystudio-cljs-tutorial</code> repo, install the libraries</p>
<pre><code class="lang-bash">npm i aws-amplify @aws-amplify/ui-react
</code></pre>
<p>You might want to commit the changes to git just as a snapshot in case the next step messes anything up.</p>
<pre><code class="lang-bash">git commit -a -m <span class="hljs-string">"After adding amplify deps"</span>
</code></pre>
<h3 id="heading-sync-repo-with-amplify-project">Sync repo with Amplify project</h3>
<p>Using the amplify CLI, pull the project info and ui-components into your repo.</p>
<p>You’ll get the command to do this from your Amplify Apps page that was created earlier.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313795370/rzi_a6UJ2.png" alt="Command for amplify pull" /></p>
<p>If you are using an AWS account via IAM, you should log in to your AWS Console on your default browser. The following command is going to open up your default browser to authenticate to AWS.</p>
<p>If you are not using AWS IAM for auth, but are using the Amplify Console that has its own username/password style login, you don’t need to do anything in advance.</p>
<p><strong>DON’T TYPE THIS EXACT LINE</strong>
Use the line from your environment as it has the appID for your application
The following line is just an example</p>
<pre><code class="lang-bash">amplify pull --appId dgt42342sdv765la --envName staging
</code></pre>
<p>This will eventually open a browser page to authenticate the process. As mentioned earlier, if you are using IAM for access, its easiest if you logged into the AWS Console with your browser first. If you forget to do this, you can still login now, and copy and past the link shown in the output of the CLI command and it will retry authenticating.</p>
<p>If you are using the Amplify Studio username/password, you will get that dialog on the browser and you can fill it in and click Yes</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313797509/HCO4ahtQR.png" alt="Login Success" /></p>
<p>It will then prompt you for a bunch of things to set up your amplify project in this repo</p>
<pre><code>Opening link: https:<span class="hljs-comment">//us-west-2.admin.amplifyapp.com/admin/dgt42342sdv765la/staging/verify/</span>
✔ Successfully received Amplify Studio tokens.
Amplify AppID found: dgtkqevv765la. Amplify App name <span class="hljs-keyword">is</span>: rental<span class="hljs-operator">-</span>cljs
Backend environment staging found in Amplify Console app: rental<span class="hljs-operator">-</span>cljs
? Choose your default editor:
  Android Studio
  Xcode (Mac OS only)
  Atom Editor
  Sublime Text
  IntelliJ IDEA
  Vim (via Terminal, Mac OS only)
❯ Emacs (via Terminal, Mac OS only)
(Move up and down to reveal more choices)
</code></pre><p>Of course the only choice that makes sense is Emacs 🤓
(Note even though it says via terminal, it works fine with GUI Emacs)</p>
<pre><code>? Choose the <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">of</span></span> app that you<span class="hljs-symbol">'re</span> building (Use arrow keys)
  android
  flutter
  ios
❯ javascript
</code></pre><p>Keep javascript</p>
<pre><code>? What javascript framework are you using (<span class="hljs-keyword">Use</span> arrow <span class="hljs-keyword">keys</span>)
  angular
  ember
  ionic
❯ react
  react-<span class="hljs-keyword">native</span>
  vue
  <span class="hljs-keyword">none</span>
</code></pre><p>Keep react</p>
<pre><code>? Source Directory <span class="hljs-type">Path</span>:  src/amplify
? Distribution Directory <span class="hljs-type">Path</span>: <span class="hljs-built_in">public</span>
? Build Command:  npm run-script build
? <span class="hljs-keyword">Start</span> Command: npm run-script <span class="hljs-keyword">start</span>
</code></pre><p>Enter <code>src/amplify</code>for <code>Source Directory Path</code>
<em>NOTE: we're going to send the amplify JSX/JS files to a directory that is NOT in the clojurescript classpath</em>
Enter <code>public</code> for <code>Distribution Directory Path</code>
This build puts everything in <code>public</code> but other scaffolding or cljs projects may use some other path. It should be the same as the directory above <code>js</code> in the <code>output-dir</code> parameter in <code>shadow-cljs.edn</code></p>
<p>You can keep the defaults for <code>Build Command</code> and <code>Start Command</code></p>
<p>The rest of the config inputs and outputs:</p>
<pre><code>✔ Synced UI components.
GraphQL schema compiled successfully.

Edit your schema at <span class="hljs-operator">/</span>Users<span class="hljs-operator">/</span>rberger<span class="hljs-operator">/</span>work<span class="hljs-operator">/</span>aws<span class="hljs-operator">/</span>amplifystudio<span class="hljs-operator">-</span>cljs<span class="hljs-operator">-</span>tutorial<span class="hljs-operator">/</span>amplify<span class="hljs-operator">/</span>backend<span class="hljs-operator">/</span>api<span class="hljs-operator">/</span>rentalcljs<span class="hljs-operator">/</span>schema.graphql or place .graphql files in a directory at <span class="hljs-operator">/</span>Users<span class="hljs-operator">/</span>rberger<span class="hljs-operator">/</span>work<span class="hljs-operator">/</span>aws<span class="hljs-operator">/</span>amplifystudio<span class="hljs-operator">-</span>cljs<span class="hljs-operator">-</span>tutorial<span class="hljs-operator">/</span>amplify<span class="hljs-operator">/</span>backend<span class="hljs-operator">/</span>api<span class="hljs-operator">/</span>rentalcljs<span class="hljs-operator">/</span>schema
Successfully generated models. Generated models can be found in <span class="hljs-operator">/</span>Users<span class="hljs-operator">/</span>rberger<span class="hljs-operator">/</span>work<span class="hljs-operator">/</span>aws<span class="hljs-operator">/</span>amplifystudio<span class="hljs-operator">-</span>cljs<span class="hljs-operator">-</span>tutorial<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>main
? Do you plan on modifying <span class="hljs-built_in">this</span> backend? (Y<span class="hljs-operator">/</span>n) Y
</code></pre><p>Say <code>Y</code> for <code>Do you plan on modifying this backend?</code></p>
<p>You might want to checkpoint your git repo again after this.</p>
<pre><code class="lang-bash">git add -A
git commit -m <span class="hljs-string">"After pulling Amplify Studio project"</span>
</code></pre>
<p>You can make sure the basic reagent setup is still working by doing:</p>
<pre><code class="lang-bash">npm start
</code></pre>
<p>The first time you run this, it will take a while to download all the Clojurescript / Clojure dependencies.</p>
<p>And see that the app is running at <code>http://localhost:3000</code>
You will just see <code>Create Reagent App</code> on the page as a header.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313799175/x5AVlEZDc.png" alt="Initial Create Reagent App success page" /></p>
<h2 id="heading-update-to-support-mixing-webpack-with-shadow-cljs">Update to support mixing webpack with shadow-cljs</h2>
<p>Based on David Vujic’s work <a target="_blank" href="https://davidvujic.blogspot.com/2021/08/hey-webpack-hey-clojurescript.html">Agile &amp; Coding: Hey Webpack, Hey ClojureScript</a> we’re going to add mechanisms to build the javascript code using webpack and the clojurescript code with shadow-cljs. This is necessary when using more recent versions of the AWS Amplify libraries.</p>
<h3 id="heading-make-sure-shadow-cljs-dependencies-are-up-to-date">Make sure Shadow-cljs dependencies are up to date</h3>
<p>In <code>shadow-cljs.edn</code> make sure that the dependencies are up to date (you can check for the latest versions at <a target="_blank" href="https://clojars.org/">Clojars</a>)</p>
<pre><code class="lang-clojure"> :dependencies
 [[reagent            "1.1.0"]
  [binaryage/devtools "1.0.4"]]
</code></pre>
<h3 id="heading-shadow-cljs-js-options">Shadow-cljs js-options</h3>
<p>Add the following lines to shadow-cljs.edn between the <code>:asset-path</code> and <code>:modules</code> stanzas in the <code>:app</code> section as per <a target="_blank" href="https://github.com/thheller">Thomas Heller</a>'s article
<a target="_blank" href="https://code.thheller.com/blog/shadow-cljs/2020/05/08/how-about-webpack-now.html">How about webpack now?</a></p>
<pre><code class="lang-clojure">   :js-options {:js-provider    :external
                :external-index "target/index.js"}
</code></pre>
<h3 id="heading-make-a-template-from-indexhtml">Make a template from index.html</h3>
<p>Webpack will be used to update index.html with the proper script include that points to the webpack bundle.</p>
<h4 id="heading-move-publicindexhtml-to-publicindexhtmltmpl">Move <code>public/index.html</code> to <code>public/index.html.tmpl</code></h4>
<pre><code class="lang-bash">mv public/index.html public/index.html.tmpl
</code></pre>
<h4 id="heading-edit-publicindexhtmltmpl">Edit <code>public/index.html.tmpl</code></h4>
<ul>
<li>Add <code>defer</code> to the main script tag</li>
</ul>
<p>Change:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/js/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>To:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/js/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-add-in-a-sytlesheet-for-the-fonts">Add in a sytlesheet for the fonts</h4>
<ul>
<li>Add the following line after the other <code>link</code> tags in <code>&lt;head&gt;</code></li>
</ul>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span>
  <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
  <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css?family=Inter:slnt,wght@-10..0,100..900&amp;display=swap"</span>
/&gt;</span>
</code></pre>
<h4 id="heading-copy-the-amplify-css-to-public">Copy the Amplify CSS to public</h4>
<p>Note that the source is <code>styles.css</code> (plural) and the destination is <code>style.css</code> (singular)</p>
<pre><code class="lang-bash">cp node_modules/@aws-amplify/ui/dist/styles.css public/css/style.css
</code></pre>
<h2 id="heading-update-the-scaffold-clojurescript-code-to-support-amplify">Update the scaffold Clojurescript code to support Amplify</h2>
<p>Here's where we actually get to the actually writing of some code to use the Amplify UI Components in an App.</p>
<p>Edit <code>src/main/amplifystudio_cljs_tutorial/app/core.cljs</code> with the following changes</p>
<h3 id="heading-add-the-dependencies-for-the-require">Add the dependencies for the <code>require</code></h3>
<p>Add the aws amplify and ui imports to the require so it looks like:</p>
<p>Note that the <code>amplify pull</code> will populate <code>src/amplify/ui-components</code> and the <code>webpack</code> execution described further on, will set things up so the <code>"ui-components/CardACollection"</code> require can be fulfilled. </p>
<p><code>src/amplify</code> should <em>NOT</em> be in the clojure[script] class path (usually set in shadow-cljs.edn <code>:source-paths</code> map)</p>
<pre><code class="lang-clojure">(ns amplifystudio-cljs-tutorial.app.core
  (:require [reagent.dom :as rdom]
            ["/aws-exports" :default ^js aws-exports]
            ["aws-amplify" :refer [Amplify] :as amplify]
            ["@aws-amplify/ui-react" :refer [AmplifyProvider]]
            ["ui-components/RentalCollection" :default RentalCollection]))
</code></pre>
<h3 id="heading-update-the-app-function">Update the <code>app</code> function</h3>
<p>This is the actual initial page code that is run by the render function. It is primarily <a target="_blank" href="https://github.com/reagent-project/reagent/blob/master/doc/UsingHiccupToDescribeHTML.md">hiccup</a> syntax.</p>
<blockquote>
<p>Hiccup describes HTML elements and user-defined components as a nested ClojureScript vector.</p>
<ul>
<li>The first element is either a keyword or a symbol<ul>
<li>If it is a keyword, the element is an HTML element where (name keyword) is the tag of the HTML element.</li>
<li>If it is a symbol, reagent will treat the vector as a component, as described in the next section.</li>
</ul>
</li>
<li>If the second element is a map, it represents the attributes to the element. The attribute map may be omitted.</li>
<li>Any additional elements must either be Hiccup vectors representing child nodes or string literals representing child text nodes.</li>
</ul>
</blockquote>
<p>This code:</p>
<ul>
<li>Displays an <code>h1</code> header</li>
<li>Wraps the <code>RentalCollection</code> we created in Figma / ui-components with the <code>AmplifyProvider</code></li>
</ul>
<p>The <code>:&gt;</code> is a function, <a target="_blank" href="http://reagent-project.github.io/docs/master/reagent.core.html#var-adapt-react-class">adapt-react-class</a>, that tells hiccup/reagent to interpret the next symbol as a React Component.
More info at: <a target="_blank" href="https://cljdoc.org/d/reagent/reagent/1.1.0/doc/tutorials/react-features">React Features in Reagent</a></p>
<p>The <code>app</code> function:</p>
<pre><code class="lang-clojure">(defn app []
  [:&gt; AmplifyProvider
  [:h1 "Amplify Studio Tutorial"]
   [:&gt; RentalCollection]])
</code></pre>
<p>For comparison here is the equivalent Javascript:</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AmplifyProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">RentalCollection</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">AmplifyProvider</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-update-the-main-function">Update the <code>main</code> function</h3>
<p>This function is the first code called in the program. It is where you would put any initialization code and then it calls the render function that kicks of the reagent/react event loop.</p>
<ul>
<li>Add a bit of logging so we can see that we're hitting the code at runtime</li>
<li>Add the Amplify initialization code.</li>
</ul>
<pre><code class="lang-clojure">(defn ^:export main []
  (js/console.log "main top")
  (-&gt; Amplify (.configure aws-exports))
  (render))
</code></pre>
<p>In the Clojurescript statement:</p>
<pre><code class="lang-clojure">(-&gt; Amplify (.configure aws-exports))
</code></pre>
<p><code>-&gt;</code> is the <a target="_blank" href="https://clojuredocs.org/clojure.core/-%3E">thread-first macro</a>. In this case it means that <code>Amplify</code> will be passed in as the second argument of the following form. I.E. its the equivalent to this Clojurescript statement:</p>
<pre><code class="lang-clojure">(.configure Amplify aws-exports)
</code></pre>
<p>In ether case, it is the Javascript interop equivalent to:</p>
<pre><code class="lang-javascript">Amplify.configure(config);
</code></pre>
<h2 id="heading-setup-webpack-babel">Setup Webpack / Babel</h2>
<h3 id="heading-babel-config-file">Babel config file</h3>
<p>Babel does the work of converting JSX files to Javascript files suitable for consumption by webpack and shadow-cljs. It is called by webpack.</p>
<p>Create the file <code>.babelrc</code> in the top level of the repo with the content:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"presets"</span>: [<span class="hljs-string">"@babel/preset-env"</span>, <span class="hljs-string">"@babel/preset-react"</span>]
}
</code></pre>
<p>This tells babel to run the presets:</p>
<blockquote>
<p><a target="_blank" href="https://babeljs.io/docs/en/babel-preset-env">@babel/preset-env</a> is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). This both makes your life easier and JavaScript bundles smaller!</p>
</blockquote>
<p>and</p>
<blockquote>
<p><a target="_blank" href="https://babeljs.io/docs/en/babel-preset-react#docsNav">@babel/preset-react</a> loads the following plugins:</p>
<p><a target="_blank" href="https://babeljs.io/docs/en/babel-plugin-syntax-jsx">@babel/plugin-syntax-jsx</a> - enables parsing of JSX
<a target="_blank" href="https://babeljs.io/docs/en/babel-plugin-transform-react-jsx">@babel/plugin-transform-react-jsx</a> - transform JSX to Javascript
<a target="_blank" href="https://babeljs.io/docs/en/babel-plugin-transform-react-display-name">@babel/plugin-transform-react-display-name</a> - Set displayName in the Javascript</p>
<p>And with the development option Classic runtime adds:</p>
<p><a target="_blank" href="https://babeljs.io/docs/en/babel-plugin-transform-react-jsx-self">@babel/plugin-transform-react-jsx-self</a> - sets <code>self</code> in the transformed code
<a target="_blank" href="https://babeljs.io/docs/en/babel-plugin-transform-react-jsx-source">@babel/plugin-transform-react-jsx-source</a> - injects the source information (file, lineno) into the the Javascript</p>
</blockquote>
<h3 id="heading-webpack-configuration-file">Webpack configuration file</h3>
<p><a target="_blank" href="https://webpack.js.org/concepts/">Webpack</a>:</p>
<blockquote>
<p>At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, which are static assets to serve your content from.</p>
</blockquote>
<p>We are using it to convert the JSX <code>ui-component</code> files from Figma/Amplify Studio into vanilla Javascript via babel.</p>
<p>Webpack is also being used to bundle the <code>src/amplify/models</code> and <code>src/amplify/ui-components</code> directories/files that are pulled from amplify into the repo as modules so that their objects can be <code>imported</code> into the app. This is configured in the <code>resolve</code> block below.</p>
<p>This will be a webpack configuration file, <code>webpack.config.js</code> in the top level of the repo. The following will describe the elements we're going to use in that file.</p>
<h4 id="heading-requires">Requires</h4>
<p>The following requires the webpack modules and plugins used</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);
<span class="hljs-keyword">const</span> webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">"webpack"</span>);
<span class="hljs-keyword">const</span> HtmlWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">"html-webpack-plugin"</span>);
<span class="hljs-keyword">const</span> HtmlBeautifierPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">"html-beautifier-webpack-plugin"</span>);
</code></pre>
<h4 id="heading-basic-webpack-config">Basic Webpack config</h4>
<ul>
<li>Set mode to development</li>
<li><code>entry</code> - The file generated by shadow-cljs describing all the require/imports seen in the code</li>
<li><code>output</code> - Where webpack should put its final bundle of javascript that will be included by a <code>&lt;script&gt;</code> tag in the index.html</li>
<li><code>devtool</code> - Tells webpack to generate source maps to be consumed by the browser devtools</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">mode</span>: <span class="hljs-string">"development"</span>,
  <span class="hljs-attr">entry</span>: <span class="hljs-string">"./target/index.js"</span>,
  <span class="hljs-attr">output</span>: {
    <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">"public"</span>),
    <span class="hljs-attr">filename</span>: <span class="hljs-string">"js/libs/bundle.js"</span>,
    <span class="hljs-attr">clean</span>: <span class="hljs-literal">false</span>,
  },
  <span class="hljs-attr">devtool</span>: <span class="hljs-string">"source-map"</span>,
</code></pre>
<h4 id="heading-rules">Rules</h4>
<p>This is the main directives that tell weback what to do.</p>
<ul>
<li><code>test: /\.m?js/,</code> - Regex that specifies what file types to apply to the first rule to (ones that end with <code>.mjs</code> or <code>.js</code>)<ul>
<li><code>fullySpecified: false</code> - the import / require statements should not end with file suffixes</li>
<li><code>alias</code> - Maps the path to the javascript files to a module name. This allows the code to require the Amplify Studio <code>models</code> and <code>ui-components</code> as importable modules.</li>
</ul>
</li>
<li><code>test: /\.jsx$/</code> - Regex that specifies which file types to apply to the second rule (JSX files)<ul>
<li><code>exclude</code> - Don't apply it to files installed by npm in <code>/node_modules/</code></li>
<li><code>use</code> - Apply babel to the JSX files. The <code>.babelrc</code> file specified earlier tells babel to transform the JSX files to vanilla javascript</li>
</ul>
</li>
</ul>
<pre><code class="lang-javascript">    rules: [
      {
        <span class="hljs-comment">// docs: https://webpack.js.org/configuration/module/#resolvefullyspecified</span>
        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.m?js/</span>,
        resolve: {
          <span class="hljs-attr">fullySpecified</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-attr">alias</span>: {
            <span class="hljs-attr">models</span>: <span class="hljs-string">"../src/amplify/models/index.js"</span>,
            <span class="hljs-string">"ui-components"</span>: <span class="hljs-string">"../src/amplify/ui-components"</span>,
          },
        },
      },
      {
        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.jsx$/</span>,
        exclude: <span class="hljs-regexp">/node_modules/</span>,
        use: [<span class="hljs-string">"babel-loader"</span>],
      },
    ],
</code></pre>
<h4 id="heading-plugins">Plugins</h4>
<p>This is where plugins are loaded.</p>
<ul>
<li><code>process</code> - This was needed as webpack 5 no longer includes a polyfil for the <code>process</code> Node.js variable. There were some dependencies that required <code>process.env</code></li>
<li><a target="_blank" href="https://webpack.js.org/plugins/html-webpack-plugin/">HtmlWebpackPlugin</a> - Enables creating <code>public/index.html</code> from a temlate so that webpack can inject the path to its bundle into the index.html. Also useful if you want to automate the updates of the index.html for other things.</li>
<li><a target="_blank" href="https://github.com/zamanruhy/html-beautifier-webpack-plugin#readme">HtmlBeautifierPlugin</a> Cleans up the index.html with proper newlines mainly</li>
</ul>
<pre><code class="lang-javascript">  plugins: [
    <span class="hljs-keyword">new</span> webpack.ProvidePlugin({
      <span class="hljs-attr">process</span>: <span class="hljs-string">"process/browser"</span>,
    }),
    <span class="hljs-keyword">new</span> HtmlWebpackPlugin({
      <span class="hljs-attr">template</span>: <span class="hljs-string">"./public/index.html.tmpl"</span>,
      <span class="hljs-attr">filename</span>: <span class="hljs-string">"index.html"</span>,
    }),
    <span class="hljs-keyword">new</span> HtmlBeautifierPlugin(),
  ],
</code></pre>
<p>The Html plugins / index.html templating are not totally necessary. You could just add your own script tag to index.html instead such as:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"js/libs/bundle.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-the-full-webpackconfigjs">The full <code>webpack.config.js</code></h4>
<p>Create a file <code>webpack.config.js</code> also at the top level of the repo with the content:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);
<span class="hljs-keyword">const</span> webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">"webpack"</span>);
<span class="hljs-keyword">const</span> HtmlWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">"html-webpack-plugin"</span>);
<span class="hljs-keyword">const</span> HtmlBeautifierPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">"html-beautifier-webpack-plugin"</span>);

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">mode</span>: <span class="hljs-string">"development"</span>,
  <span class="hljs-attr">entry</span>: <span class="hljs-string">"./target/index.js"</span>,
  <span class="hljs-attr">output</span>: {
    <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">"public"</span>),
    <span class="hljs-attr">filename</span>: <span class="hljs-string">"js/libs/bundle.js"</span>,
    <span class="hljs-attr">clean</span>: <span class="hljs-literal">false</span>,
  },
  <span class="hljs-attr">devtool</span>: <span class="hljs-string">"source-map"</span>,
  <span class="hljs-attr">module</span>: {
    <span class="hljs-attr">rules</span>: [
      {
        <span class="hljs-comment">// docs: https://webpack.js.org/configuration/module/#resolvefullyspecified</span>
        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.m?js/</span>,
        resolve: {
          <span class="hljs-attr">fullySpecified</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-attr">alias</span>: {
            <span class="hljs-attr">models</span>: <span class="hljs-string">"../src/amplify/models/index.js"</span>,
            <span class="hljs-string">"ui-components"</span>: <span class="hljs-string">"../src/amplify/ui-components"</span>,
          },
        },
      },
      {
        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.jsx$/</span>,
        exclude: <span class="hljs-regexp">/node_modules/</span>,
        use: [<span class="hljs-string">"babel-loader"</span>],
      },
    ],
  },
  <span class="hljs-attr">resolve</span>: {
    <span class="hljs-attr">extensions</span>: [<span class="hljs-string">""</span>, <span class="hljs-string">".js"</span>, <span class="hljs-string">".jsx"</span>],
  },
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-keyword">new</span> webpack.ProvidePlugin({
      <span class="hljs-attr">process</span>: <span class="hljs-string">"process/browser"</span>,
    }),
    <span class="hljs-keyword">new</span> HtmlWebpackPlugin({
      <span class="hljs-attr">template</span>: <span class="hljs-string">"./public/index.html.tmpl"</span>,
      <span class="hljs-attr">filename</span>: <span class="hljs-string">"index.html"</span>,
    }),
    <span class="hljs-keyword">new</span> HtmlBeautifierPlugin(),
  ],
};
</code></pre>
<h4 id="heading-add-a-script-to-run-webpack">Add a script to run webpack</h4>
<p>Add the following line to the <code>”scripts”</code> section of <code>package.json</code>. It will allow you to run a that will update the bundle automatically when you change any of the amplify files or when shadow-cljs updates the <code>target/index.js</code></p>
<pre><code class="lang-json">    <span class="hljs-string">"pack"</span>: <span class="hljs-string">"webpack --watch"</span>
</code></pre>
<h3 id="heading-update-git">Update git</h3>
<h4 id="heading-update-gitignore">Update .gitignore</h4>
<p>add the following to <code>.gitignore</code></p>
<pre><code class="lang-git-config">/target/
</code></pre>
<h4 id="heading-add-all-the-new-files-to-the-commit">Add all the new files to the commit</h4>
<p>git add -A
git commit -m "Sync up all the final changes"</p>
<h2 id="heading-running-the-development-service-locally">Running the development service locally</h2>
<p>Start the shadow-cljs watch process. (<code>shadow-cljs watch app</code>) using the npm command:</p>
<pre><code class="lang-bash">npm start
</code></pre>
<p>And in another terminal window, also at the top of the repo run the webpack watch process:</p>
<pre><code>npm run <span class="hljs-keyword">pack</span>
</code></pre><p>Should see something like the following. The images and values are dependent on how you set up the data when following along with the first part of the <a target="_blank" href="https://welearncode.com/studio-vacation-site/">Build a Vacation Rental Site with Amplify Studio</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313801128/a4oMX5zJT.png" alt="Initial Integration View" /></p>
<h2 id="heading-troubleshooting">Troubleshooting</h2>
<p>If when you start the shadow-cljs process, <code>npm start</code>, and you get something like:</p>
<pre><code class="lang-bash">...
shadow-cljs - watching build :app
[:app] Configuring build.
[:app] Compiling ...
[2022-01-02 21:55:15.214 - WARNING] :shadow.cljs.devtools.server.util/handle-ex - {:msg {:<span class="hljs-built_in">type</span> :start-autobuild}}
AssertionError Assert failed: (map? rc)
...
</code></pre>
<p>The <code>js-provider :external</code> config in <code>shadow-cljs.edn</code> is masking the actual error. In order to see what the error is, comment out the <code>:js-options</code> block in <code>shadow-cljs.edn</code> like:</p>
<pre><code class="lang-edn">:builds
 {:app
  {:target     :browser
   :output-dir "public/js"
   :asset-path "/js"
   ;; :js-options {:js-provider    :external
   ;;              :external-index "target/index.js"}
   :modules    {:main
                {:init-fn amplifystudio-cljs-tutorial.app.core/main}}}
</code></pre>
<p>and then run <code>npm start</code> again and see what the error is. Correct the error and then remember to uncomment the <code>:js-options</code> block.</p>
<h2 id="heading-completing-the-tutorial-with-amplify-ui-overrides">Completing the Tutorial with Amplify UI Overrides</h2>
<p>We pick back up the original tutorial at the <code>Use a Prop</code> section to show how the UI Components can be customized just with Component Props and runtime Overrides.</p>
<p>Overrides are a powerful feature that are builtin to the Amplify UI Components and allow you to inject attributes into the children of components at runtime. It makes the Amplify UI Components very flexible without having to modify the actual code of the components. This allows you to update the Figma design aspects and still update your local copy of the ui-components with an <code>amplify pull</code> since you don't make local changes to that code.</p>
<h3 id="heading-use-a-prop">Use a Prop</h3>
<blockquote>
<p>You can customize these React components in your own code. First, you can use props in order to modify your components. If you wanted to make your grid of rentals into a list, for example, you could pass the prop type="list" to your RentalCollection.</p>
</blockquote>
<p>In Javascript you would say:</p>
<pre><code class="lang-javascript">&lt;RentalCollection type=<span class="hljs-string">"list"</span> /&gt;
</code></pre>
<p>and in Clojurescript:</p>
<pre><code class="lang-clojure">[:&gt; RentalCollection {:type "list"}]
</code></pre>
<h4 id="heading-and-that-will-make-the-view-go-from-a-grid-to-a-list">And that will make the view go from a grid to a list:</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313803468/5nuNynyEC.png" alt="Colllection as a list" /></p>
<p>The props are listed for each component type at <a target="_blank" href="https://ui.docs.amplify.aws/components">Amplify UI Connected Components</a></p>
<h3 id="heading-use-an-override">Use an Override</h3>
<p>Overrides allow you to inject props into the children of a component.</p>
<p>In our example RentalCollection, the images in the child cards are kind of squashed. To fix that we want to set the <code>objectFit</code> prop of the image element of the card to <code>cover</code>.</p>
<p>In Javascript you would use:</p>
<pre><code class="lang-javascript">&lt;RentalCollection
  type=<span class="hljs-string">"list"</span>
  overrides={{
    <span class="hljs-string">"Collection.CardA[0]"</span>: {
      <span class="hljs-attr">overrides</span>: {
        <span class="hljs-string">"Flex.Image[0]"</span>: { <span class="hljs-attr">objectFit</span>: <span class="hljs-string">"cover"</span> },
      },
    },
  }}
/&gt;
</code></pre>
<p>In Clojurescript we use:</p>
<pre><code class="lang-clojure">[:&gt; RentalCollection {:type "list"
                      :overrides {"Collection.CardA[0]"
                                  {:overrides {"Flex.Image[0]"
                                               {:object-fit "cover"}}}}}]])
</code></pre>
<h4 id="heading-now-the-images-are-no-longer-squished">Now the images are no longer squished:</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313805748/JTp5f_Yhd.png" alt="objectFit cover prop applied to children images" /></p>
<h2 id="heading-themes-and-conclusion">Themes and Conclusion</h2>
<p>That completes showing the differences of using Clojurescript instead of Javascript with Amplify Studio and Amplify UI Connected Components.</p>
<p>You can refer back to the original AWS Tutorial <a target="_blank" href="https://welearncode.com/studio-vacation-site/">Build a Vacation Rental Site with Amplify Studio</a> for the remaining content on how to use the <a target="_blank" href="https://www.figma.com/community/plugin/1040722185526429545/AWS-Amplify-Theme-Editor">AWS Amplify Theme Editor</a> in Figma to add a theme to the UI Components. This should work without having to change any of your Clojurescript code as you modify the Ui component code that you load via <code>amplify pull</code> via Figma.</p>
<p>It is also possible to <a target="_blank" href="https://ui.docs.amplify.aws/theming">apply themes directly in your code</a>. Doing that with Clojurescript will be left to a possible future article.</p>
<p>The full project / code for this repo is at
{% github rberger/amplifystudio-cljs-tutorial no-readme %}</p>
<p>Feel free to post issues or questions there.</p>
]]></content:encoded></item><item><title><![CDATA[Set up SSL/TLS for shadow-cljs https server]]></title><description><![CDATA[While developing clojurescript web apps, you may require that the development http server shadow-cljs operate with SSL/TLS to serve up HTTPS, not just HTTP.
This is particuarly true if you need to test things out on an iPhone or Android phone but sti...]]></description><link>https://hashnode.ibd.com/set-up-ssltls-for-shadow-cljs-https-server</link><guid isPermaLink="true">https://hashnode.ibd.com/set-up-ssltls-for-shadow-cljs-https-server</guid><dc:creator><![CDATA[Robert J. Berger]]></dc:creator><pubDate>Thu, 21 Oct 2021 06:10:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313819742/y8pa8OtMv.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While developing clojurescript web apps, you may require that the development http server <a target="_blank" href="https://github.com/thheller/shadow-cljs">shadow-cljs</a> operate with SSL/TLS to serve up HTTPS, not just HTTP.</p>
<p>This is particuarly true if you need to test things out on an iPhone or Android phone but still run with the development server so you can iterated changes just as quick as when you are working with desktop clients.</p>
<p>Its a bit tricky to get everything lined up to make SSL/TLS work locally as Apple (and I presume other browsers) no longer support self-signed certificates for HTTPS servers. So you need a private CA and a certificate generated from the private CA.</p>
<p>This is a guide to set up:</p>
<ul>
<li>A Private Certificate Authority (CA)</li>
<li>A Server Certificate for your shadow-cljs development server</li>
<li>How to configure shadow-cljs.edn for SSL</li>
<li>How to install the CA Root Certificate on other clients (like an iPhone) so they can access the shadow-cljs servers</li>
</ul>
<p><strong>*NOTE</strong>: This server / CA / Certificates should never be used in production or in any particularly public way. It’s not secure. We’re doing this to get around the normal browser / server security just for local development.* </p>
<h2 id="heading-install-mkcert">Install mkcert</h2>
<p>See the following for more info or how to install on Linux: <a target="_blank" href="https://github.com/FiloSottile/mkcert">GitHub - FiloSottile/mkcert</a></p>
<h3 id="heading-install-mkcert-on-macos">Install mkcert on macOS</h3>
<pre><code class="lang-bash">&gt; brew install mkcert
&gt; brew install nss <span class="hljs-comment"># if you use Firefox</span>
</code></pre>
<h2 id="heading-create-a-local-ca-to-be-used-by-mkcert-and-clients">Create a local CA to be used by mkcert and clients</h2>
<pre><code class="lang-bash">&gt; mkcert -install
Created a new <span class="hljs-built_in">local</span> CA 💥
Sudo password:
The <span class="hljs-built_in">local</span> CA is now installed <span class="hljs-keyword">in</span> the system trust store! ⚡️
The <span class="hljs-built_in">local</span> CA is now installed <span class="hljs-keyword">in</span> the Firefox trust store (requires browser restart)! 🦊
The <span class="hljs-built_in">local</span> CA is now installed <span class="hljs-keyword">in</span> Java<span class="hljs-string">'s trust store! ☕️</span>
</code></pre>
<h2 id="heading-create-a-pkcs12-certificate">Create a pkcs12 certificate</h2>
<p>Easiest to do this in the directory you are running the shadow-cljs project.
Create a subdirectory <code>ssl</code> at the same level as shadow-cljs (top level of the repo usually) and cd into <code>ssl</code></p>
<pre><code class="lang-bash">❯ <span class="hljs-built_in">cd</span> ~/work/my-project
❯ ls
Makefile        RELEASE_TAG     bin             dev             package.json    shadow-cljs.edn <span class="hljs-built_in">test</span>
README.org      amplify         deps.edn        node_modules    resources       src             yarn.lock

❯ mkdir ssl
❯ <span class="hljs-built_in">cd</span> ssl
</code></pre>
<p>Create the certificate that the shadow-cljs servers will use as their server certificates. You want to specify all the domains and IPs that would be associated with the certificate and the way you will access the server.
In my case my iMac has two interfaces plus localhost. One interface is the Ethernet, the other is the wifi. And just to be safe, I’m putting in their IPv6 addresses as well.</p>
<pre><code class="lang-bash">❯ mkcert -pkcs12 discovery.local localhost  192.168.20.10 192.168.20.11 127.0.0.1 ::1 fd95:cb6f:7955:0:1878:b8b5:1b3b:ad27 fd95:cb6f:7955:0:4cd:c922:d1b3:2eb5

Created a new certificate valid <span class="hljs-keyword">for</span> the following names 📜
 - <span class="hljs-string">"discovery.local"</span>
 - <span class="hljs-string">"localhost"</span>
 - <span class="hljs-string">"192.168.20.10"</span>
 - <span class="hljs-string">"192.168.20.11"</span>
 - <span class="hljs-string">"127.0.0.1"</span>
 - <span class="hljs-string">"::1"</span>
 - <span class="hljs-string">"fd95:cb6f:7955:0:1878:b8b5:1b3b:ad27"</span>
 - <span class="hljs-string">"fd95:cb6f:7955:0:4cd:c922:d1b3:2eb5"</span>

The PKCS<span class="hljs-comment">#12 bundle is at "./discovery.local+7.p12" ✅</span>

The legacy PKCS<span class="hljs-comment">#12 encryption password is the often hardcoded default "changeit" ℹ️</span>

It will expire on 20 January 2024 🗓
</code></pre>
<h2 id="heading-install-the-cert-into-the-keystore">Install the cert into the keystore</h2>
<p><strong><em>NOTE:</em></strong> <em>The passwords  you use here should not be used anywhere else, particularly on public services. They do not have to be super secret, great passwords as they will be in the clear in your shadow-cljs.</em></p>
<p>You will create a local Java JKS Keystore in <code>ssl</code> to be used by shadow-cljs servers</p>
<ul>
<li><code>Destination Password</code>: This will be the password specified in shadow-cljs.edn to gain access to the keystore.  Our example will be <code>super-secret</code></li>
<li><code>Source keystore password</code>: The password that <code>mkcert</code> used to generate the Server Certificate and thus the password of the Server Certificate. I could not find a way to specify it. It defaults to <code>changeit</code>
```bash
❯ keytool -importkeystore -destkeystore keystore.jks -srcstoretype PKCS12 -srckeystore discovery.local+7.p12</li>
</ul>
<p>Importing keystore discovery.local+7.p12 to keystore.jks...
Enter destination keystore password: super-secret
Re-enter new password: super-secret
Enter source keystore password: changeit
Entry for alias 1 successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled</p>
<pre><code>
<span class="hljs-meta">## Configure shadow-cljs.edn to enable SSL</span>
Mainly need to <span class="hljs-keyword">add</span> an `:ssl` coda to the start of the `shadow-cljs.edn` 

```clojure
{:deps  <span class="hljs-literal">true</span>
 :nrepl {:port <span class="hljs-number">8777</span>}
 :ssl {:keystore <span class="hljs-string">"ssl/keystore.jks"</span>
       :password <span class="hljs-string">"retold-fever"</span>}
 :dev-http {<span class="hljs-number">8020</span> {:root <span class="hljs-string">"resources/public"</span>}}
 ... rest of your shadow-cljs.edn file...
</code></pre><p>No need to specify the hostnames. In fact that will limit access to IP addresses that resolve to that name which may be incorrect.
More info on the <code>:ssl</code> configuration at <a target="_blank" href="https://shadow-cljs.github.io/docs/UsersGuide.html#_ssl">Shadow CLJS User’s Guide: SSL</a></p>
<p>[Re]start your shadow-cljs watch process and it should say something like the following at some point in its startup where <code>https</code> is the protocol shown for the http and shadow-cljs servers:</p>
<pre><code class="lang-bash">...
shadow-cljs - HTTP server available at https://localhost:8020
shadow-cljs - server version: 2.15.8 running at https://localhost:9631
shadow-cljs - nREPL server started on port 8777
shadow-cljs - watching build :app
...
</code></pre>
<p>Assuming you set the certificate to support any other domain names and IP addresses associated with your computer running this, they will also work as the host address in your client URL accessing this server. But only if running on the same machine as this server.</p>
<p>If you want to make another device (like an iPhone or another computer) access this server, follow the next steps.</p>
<h2 id="heading-export-the-root-ca-of-your-private-ca-to-other-clients">Export the Root CA of your Private CA to other Clients</h2>
<p>In order for other machines on your LAN to access the shadow-cljs server running with the Private CA and Server certificate set up in the earlier steps,  you will need to export the Root CA from that machine to these other clients.</p>
<h3 id="heading-find-the-location-of-the-root-certificate-of-the-private-ca">Find the location of the Root Certificate of the Private CA</h3>
<p>When you ran <code>mkcert install</code> it created the root certificates of the Private CA and stashed them somewhere appropriate for your system. You can find out where with the command:</p>
<pre><code class="lang-bash">❯ mkcert -CAROOT
/Users/rberger/Library/Application Support/mkcert

❯ ls <span class="hljs-string">'/Users/rberger/Library/Application Support/mkcert'</span>
rootCA-key.pem rootCA.pem
</code></pre>
<p>You will want to copy the <code>rootCA.pem</code> to other clients that would access the shadow-cljs servers.</p>
<h3 id="heading-for-transferring-to-other-macs-or-ios-devices">For transferring to other Macs or iOS devices</h3>
<pre><code class="lang-bash">open <span class="hljs-string">'/Users/rberger/Library/Application Support/mkcert'</span>
</code></pre>
<p>Which will open a finder window with the directory where these pem files are:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313812588/k7gAPuH3q.png" alt="Root Cert in Finder" /></p>
<p>And then select AirDrop to send them to other macOS or iOS devices
Otherwise you can email it or send the file some other way to a destination device.</p>
<h2 id="heading-install-the-private-ca-root-cert-on-ios-device">Install the Private CA Root Cert on iOS device</h2>
<ul>
<li><p>Once you send the Cert to an iOS device, you will get a message 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313814581/ea3dDab7l.png" alt="Choose Device" /></p>
</li>
<li><p>Select iPhone  and then select Close:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313816379/bztI8vsDx.jpeg" alt="Close" /></p>
</li>
<li><p>Go to Settings and you’ll see the a new option <code>Profile Downloaded</code> Click on that and the go thru the rest of the dialogs agreeing to Install the downloaded profile.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313818159/1DhH8peGI.jpeg" alt="Profile Downloaded in Settings" /></p>
<ul>
<li>After completing all the install dialogs, this client should be ready to connect to the shadow-cljs using https.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Accessing AppSync APIs that require Cognito Login outside of Amplify]]></title><description><![CDATA[The Need
You have this great Amplify App using AppSync GraphQL. You eventually find that you need to be able to access that data in your AppSync GraphQL database from tools other than your Amplify App. Its easy if you just have your AppSync API prote...]]></description><link>https://hashnode.ibd.com/accessing-appsync-apis-that-require-cognito-login-outside-of-amplify</link><guid isPermaLink="true">https://hashnode.ibd.com/accessing-appsync-apis-that-require-cognito-login-outside-of-amplify</guid><dc:creator><![CDATA[Robert J. Berger]]></dc:creator><pubDate>Wed, 01 Sep 2021 01:23:37 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-the-need">The Need</h2>
<p>You have this great Amplify App using AppSync GraphQL. You eventually find that you need to be able to access that data in your AppSync GraphQL database from tools other than your Amplify App. Its easy if you just have your AppSync API protected just by an API Key. But that isn't great security for your data!</p>
<p>One way to protect your AppSync data is to use <a target="_blank" href="https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/js/#cognito-user-pools">Cognito Identity Pools</a>. Amplify makes it pretty transparent if you are  using Amplify to build your clients. AppSync lets you do really nice <a target="_blank" href="https://docs.aws.amazon.com/appsync/latest/devguide/security-authorization-use-cases.html">table and record level access control based on logins and roles</a>.</p>
<p>What happens if you want to access that data from something other than an Amplify based client? How do you "login" and get the JWT credentials you need to access your AppSync APIs?</p>
<h2 id="heading-use-aws-cli">Use AWS CLI</h2>
<p>The most general way is to use the AWS CLI to effectively login and retrieve the JWT credentials that can then be passed in the headers of any requests you make to your AppSync APIs.</p>
<p>Unfortunately its not as easy as just having your login and password. It also depends on how you configured your Cognito Identity Pool and its related Client Apps.</p>
<h3 id="heading-cognito-user-pool-client-app">Cognito User Pool Client App</h3>
<p>You can have multiple Client Apps specified for your Cognito User Pool. I suggest  having one dedicated to these external applications. That way you can have custom configuration just for this and not disrupt your main  Amplify apps. Also you can easily turn it off if you need too.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74z6pgmf1qdyqv9wkllr.png" alt="User Pool Client Apps" /></p>
<p>In my case I created a new client app <code>shoppabdbe800b-rob-test2</code> as a way to test a client app with no <code>App Client Secret</code>. This makes it easier to access from the command line as you do not have to generate a Secret Hash (will describe how to deal with that below).</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hipjcnn0e3q4ronqvgi0.png" alt="App Client Config with no secret" /></p>
<p>If you want to allow admin level access (ie a user with admin permission) you need to check <code>Enable username password auth for admin APIs for authentication (ALLOW_ADMIN_USER_PASSWORD_AUTH)</code></p>
<p>If you want to allow regular users to login you must also select <code>Enable username password based authentication (ALLOW_USER_PASSWORD_AUTH)</code></p>
<p>The defaults for the other fields should be ok. Be sure to save your changes.</p>
<h3 id="heading-minimal-iam-permissions">Minimal IAM permissions</h3>
<p>As far as I can tell, these are the minimal IAM permissions to make the aws <code>cognito-idp</code> command work for admin and regular users of AppSync (replace the Resource arn with the arn of the user pool[s] you want to control):</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor0"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"cognito-idp:AdminInitiateAuth"</span>,
                <span class="hljs-string">"cognito-idp:AdminGetUser"</span>
            ],
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:cognito-idp:us-east-1:XXXXXXXXXXXXX:userpool/us-east-1_XXXXXXXXX"</span>
        },
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor1"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"cognito-idp:GetUser"</span>,
                <span class="hljs-string">"cognito-idp:InitiateAuth"</span>
            ],
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>
        }
    ]
}
</code></pre>
<h3 id="heading-get-the-credentials-with-no-app-client-secret">Get the Credentials with no App Client Secret</h3>
<p>This example is if you did not set the App Client Secret.</p>
<p>You should now be able to get the JWT credentials from the AWS CLI.</p>
<p>This assumes you have<a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html"> set up your</a> <code>~/.aws/credentials</code> file or whatever is appropriate for your command line environment so that you have the permissions to access this service.</p>
<ul>
<li>When using the <code>ADMIN_USER_PASSWORD_AUTH</code></li>
</ul>
<pre><code class="lang-bash">aws cognito-idp admin-initiate-auth --user-pool-id us-east-1_XXXXXXXXXX --auth-flow ADMIN_USER_PASSWORD_AUTH --client-id XXXXXXXXXXXXX --auth-parameters USERNAME=username1,PASSWORD=XXXXXXXXXXXXX &gt; creds.json
</code></pre>
<ul>
<li>When using the <code>USER_PASSWORD_AUTH</code></li>
</ul>
<pre><code class="lang-bash">aws cognito-idp initiate-auth --auth-flow USER_PASSWORD_AUTH --client-id XXXXXXXXXXXXX --auth-parameters USERNAME=username2,PASSWORD=XXXXXXXXXXXX &gt; creds.json
</code></pre>
<p>Of course replace the <code>XXXX</code>'s with the actual values.</p>
<ul>
<li><code>user-pool-id</code> - The pool id found at the top of the <em>User Pool Client Apps</em> page</li>
<li><code>client-id</code> - The <code>client-id</code> of the <code>app client</code> you are using</li>
<li><code>USERNAME</code> - The Username normally used to login to your Amplify app</li>
<li><code>PASSWORD</code> - The Password normally used to login to your Amplify app</li>
</ul>
<p>The results will be in <code>creds.json</code>. (You could not use the <code>&gt; creds.json</code> if you want to just see the results)</p>
<h3 id="heading-get-the-credentials-when-there-is-an-app-client-secret">Get the Credentials when there is an App Client Secret</h3>
<p>This assumes you have an App Client that has an <code>app secret key</code> set.</p>
<p>The main thing here is you need to generate a <code>secret hash</code> to send along with the command.</p>
<p>You can do that by creating a little python program to generate it for you when you need it:</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/env python3</span>

<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">import</span> hmac, hashlib, base64

<span class="hljs-keyword">if</span> (len(sys.argv) == <span class="hljs-number">4</span>):
    username = sys.argv[<span class="hljs-number">1</span>]
    app_client_id = sys.argv[<span class="hljs-number">2</span>]
    key = sys.argv[<span class="hljs-number">3</span>]
    message = bytes(sys.argv[<span class="hljs-number">1</span>]+sys.argv[<span class="hljs-number">2</span>],<span class="hljs-string">'utf-8'</span>)
    key = bytes(sys.argv[<span class="hljs-number">3</span>],<span class="hljs-string">'utf-8'</span>)
    secret_hash = base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest()).decode()

    print(<span class="hljs-string">"SECRET HASH:"</span>,secret_hash)
<span class="hljs-keyword">else</span>:
    (print(<span class="hljs-string">"len sys.argv: "</span>, len(sys.argv)))
    print(<span class="hljs-string">"usage: "</span>,  sys.argv[<span class="hljs-number">0</span>], <span class="hljs-string">" &lt;username&gt; &lt;app_client_id&gt; &lt;app_client_secret&gt;"</span>)
</code></pre>
<p>Save the file someplace that you can execute it from like <code>~/bin/app-client-secret-hash</code> and make it executable (<code>chmod a+x ~/bin/app-client-secret-hash</code>).</p>
<p>You will need:</p>
<ul>
<li><code>app-client-id</code> - The <code>client-id</code> of the <code>app client</code> you are using</li>
<li><code>app-client-secret</code> - The secret of the <code>app client</code> you are using (its on the App Client page of the User Pool)</li>
<li><code>USERNAME</code> - The Username normally used to login to your Amplify app</li>
</ul>
<p>To use:</p>
<pre><code class="lang-bash">~/bin/app-client-secret-hash  &lt;username&gt; &lt;app_client_id&gt; &lt;app_client_secret&gt;
</code></pre>
<p>Where of  course you replace the arguments with the actual values. </p>
<p>The result is a <code>secret-hash</code> you will use in the following command to get the actual JWT credentials</p>
<pre><code class="lang-bash">aws cognito-idp admin-initiate-auth --user-pool-id us-east-1_XXXXXXXXXX --auth-flow ADMIN_USER_PASSWORD_AUTH --client-id XXXXXXXXXXXXX --auth-parameters USERNAME=username3,PASSWORD=<span class="hljs-string">'secret password'</span>,SECRET_HASH=<span class="hljs-string">'secret-hash'</span> &gt; creds.json
</code></pre>
<p>You could do the same thing with <code>USER_PASSWORD_AUTH</code> if you nee that instead</p>
<pre><code class="lang-bash">aws cognito-idp initiate-auth --auth-flow USER_PASSWORD_AUTH --client-id XXXXXXXXXXXXX --auth-parameters USERNAME=rob+admin,PASSWORD=XXXXXXXXX,SECRET_HASH=<span class="hljs-string">'secret-hash'</span> &gt; creds.json
</code></pre>
<h2 id="heading-using-the-credentials">Using the Credentials</h2>
<p>How you use these credentials depends on what tool or  how you are trying to access your AppSync APIs.</p>
<h3 id="heading-from-some-javascript">From some Javascript</h3>
<p>You can just add in the <code>IdToken</code> from the <code>creds.json</code> as an <code>Authorization</code> header when you build the request:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">graphQLFetcher</span>(<span class="hljs-params">graphQLParams</span>) </span>{
  <span class="hljs-keyword">const</span> APPSYNC_API_URL = <span class="hljs-string">"TYPE_YOUR_APPSYNC_URL"</span>;
  <span class="hljs-keyword">const</span> credentialsAppSync = {
    <span class="hljs-attr">Authorization</span>: <span class="hljs-string">"eyJraWQiOiI1dVUwMld..."</span>,
  };
  <span class="hljs-keyword">return</span> fetch(APPSYNC_API_URL, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">"post"</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-attr">Accept</span>: <span class="hljs-string">"application/json"</span>,
      <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      ...credentialsAppSync,
    },
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(graphQLParams),
    <span class="hljs-attr">credentials</span>: <span class="hljs-string">"omit"</span>,
  }).then(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">response</span>) </span>{
    <span class="hljs-keyword">return</span> response.json().catch(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">return</span> response.text();
    });
  });
}
</code></pre>
<p>If you are using some GraphQL tool that needs to access your AppSync APIs. The tool should have a way that you can supply the token and it will add it as an <code>Authorization</code> header for its own requests.</p>
<p>Do let me know if you have some examples of tools that would make use of this.</p>
<h2 id="heading-references">References</h2>
<ul>
<li><a target="_blank" href="https://aws.amazon.com/blogs/mobile/appsync-graphiql-local/">Explore AWS AppSync APIs with GraphiQL from your local machine</a></li>
<li><a target="_blank" href="https://aws.amazon.com/premiumsupport/knowledge-center/cognito-unable-to-verify-secret-hash/">How do I troubleshoot "Unable to verify secret hash for client " errors from my Amazon Cognito user pools API?</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[AWS Amplify and Clojurescript Re-Frame Part 2]]></title><description><![CDATA[Introduction
Now that our Re-Frame app is running, let's deploy it to "production" on AWS Cloudfront using Amplify Console.
The Amplify Console allows you to do a CI style deployment from your git repo with almost no work on your part.

AWS Amplify C...]]></description><link>https://hashnode.ibd.com/aws-amplify-and-clojurescript-re-frame-part-2</link><guid isPermaLink="true">https://hashnode.ibd.com/aws-amplify-and-clojurescript-re-frame-part-2</guid><dc:creator><![CDATA[Robert J. Berger]]></dc:creator><pubDate>Tue, 21 Jan 2020 19:31:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313827360/0sskZefFh.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Now that our Re-Frame app is running, let's deploy it to "production" on AWS <a target="_blank" href="https://aws.amazon.com/cloudfront/">Cloudfront</a> using <a target="_blank" href="https://aws.amazon.com/amplify/console/">Amplify Console</a>.</p>
<p>The Amplify Console allows you to do a CI style deployment from your git repo with almost no work on your part.</p>
<ul>
<li><a target="_blank" href="https://docs.aws.amazon.com/amplify/latest/userguide/welcome.html">AWS Amplify Console Documentation</a></li>
</ul>
<h2 id="heading-setup-amplify-console">Setup Amplify Console</h2>
<h3 id="heading-connect-your-repo-to-the-amplify-console">Connect your repo to the Amplify Console</h3>
<ul>
<li>Use your web browser to go to https://console.aws.amazon.com/amplify/home</li>
<li><p>If you have no previous Amplify Deployments you will see:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313825901/XKxqYz_d_.png" alt /></p>
<ul>
<li>You should select the <strong>Get Started</strong> under <strong>Deploy</strong></li>
</ul>
</li>
<li><p>Select the appropriate Git service (We used Github)
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313827360/0sskZefFh.png" alt /></p>
<ul>
<li>It will then go thru the process of allowing you to authenticate that the
Amplify service can have the appropriate access to your github repos for the
github account/organization you enable</li>
<li>Will then prompt you for the actual repo and branch you want to build and deployments
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313828724/39NfJRjUV.png" alt /></li>
<li>Click <strong>NEXT</strong></li>
</ul>
</li>
</ul>
<p>Note: The previous article <a target="_blank" href="https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-1-3d3f">Add Serverless AWS Amplify Authentication to a Clojurescript Re-Frame App</a> and this current article are based on branch <code>basic-amplify-with-cloudfront</code> of the <code>omnyway-labs/amplify_cljs_example</code> repo</p>
<h3 id="heading-configure-basic-and-backend-build-settings">Configure Basic and Backend build settings</h3>
<p>The Backend is the AWS services that were created using the Amplify CLI. For us that was the Authentication service.</p>
<p>The Frontend is our Web App.</p>
<ul>
<li>You will see the following:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313830437/ycK6I-r8j.png" alt /><ul>
<li>Change the <code>App name</code> if you want</li>
</ul>
</li>
</ul>
<h4 id="heading-backend-deployment-options">Backend Deployment Options</h4>
<ul>
<li>You have the choice under <code>Would you like Amplify Console to deploy changes to these resources with your frontend?</code> to:<ul>
<li>Not have it automatically deploy the backend along with your frontend<ul>
<li><code>No - only deploy my frontend</code></li>
</ul>
</li>
<li>Deploy the same backend for prod as you are using in the dev environment <ul>
<li>Select <code>dev</code> and not <code>Create new environment</code></li>
</ul>
</li>
<li>Deploy a different copy of the backend from your dev environment<ul>
<li>Select <code>Create new environment</code></li>
<li>Fill in the name of the new environment. In our case <code>prod</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="heading-service-role">Service Role</h4>
<p>This is the role that the Amplify Console runs as when setting up your backend services on AWS</p>
<ul>
<li>If this is the first time thru, you should <ul>
<li>Click <strong>Create new role</strong><ul>
<li>Go thru the pages and accept the pre-selected defaults on each page and create the new service role</li>
</ul>
</li>
<li>Click the <strong>Refresh</strong> option</li>
<li>Use the pulldown to select the role you created</li>
</ul>
</li>
<li>If it wasn't the first time, select the role that already exists</li>
</ul>
<h4 id="heading-build-settings">Build Settings</h4>
<p>This is the CI Commands to build and deploy your app. It has the default YAML file that at a minimum requires you to set the Frontend build commands and the build output directory. <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313832164/7mnCg9wCS.png" alt /></p>
<p>The Backend commands are fine, but we need to do several things to be able to build our Clojurescript application. You can edit it on the Amplify Console webpage (by clicking on <code>Edit</code>) and / or locally by downloading it and editing locally. In ether case we'll want to download it and put the <code>amplify.yml</code> file in the root directory of our repo.</p>
<p>(Note: Its not clear to me if we really need the file on the console if we have it in our repo. It seems that the one that is in your repo will supersede the one here on the Amplify Console)</p>
<ul>
<li>Install<ul>
<li>Java</li>
<li>Leiningen</li>
<li>shadow-cljs</li>
<li>Add the following to the frontend prebuild commands:</li>
</ul>
</li>
<li>Set the base directory to where shadow-cljs puts the build assets</li>
<li>Set the frontend build command<pre><code>frontend:
phases:
  preBuild:
    commands:
      <span class="hljs-operator">-</span> amazon<span class="hljs-operator">-</span>linux<span class="hljs-operator">-</span>extras enable corretto8
      <span class="hljs-operator">-</span> yum <span class="hljs-operator">-</span>y install java<span class="hljs-number">-1.8</span><span class="hljs-number">.0</span><span class="hljs-operator">-</span>amazon<span class="hljs-operator">-</span>corretto<span class="hljs-operator">-</span>devel
      <span class="hljs-operator">-</span> java <span class="hljs-operator">-</span>version
      <span class="hljs-operator">-</span> curl https:<span class="hljs-comment">//raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein &gt; lein</span>
      <span class="hljs-operator">-</span> mv lein <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>lein
      <span class="hljs-operator">-</span> chmod a<span class="hljs-operator">+</span>x <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>lein
      <span class="hljs-operator">-</span> lein version
      <span class="hljs-operator">-</span> npm install <span class="hljs-operator">-</span>g shadow<span class="hljs-operator">-</span>cljs
      <span class="hljs-operator">-</span> npm ci
  build:
    commands: [lein prod]
artifacts:
  baseDirectory: resources<span class="hljs-operator">/</span><span class="hljs-keyword">public</span><span class="hljs-operator">/</span>
</code></pre></li>
<li>If all went well, you will eventually have a successful build / deploy
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313834044/1UlllYiej.png" alt /><ul>
<li>Click on the Domain link to open the application
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313835715/iyjICM5Gk.png" alt /></li>
</ul>
</li>
<li>The full features of the Authentication Lifecycle are now available<ul>
<li>Click on <code>Create account</code> to create an account
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313837271/_YIj1M1Gn.png" alt /><ul>
<li>The email needs to be a real email address you will receive as confirmation codes will be sent there
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313838603/Z6F_llStz.png" alt /></li>
</ul>
</li>
<li>Sign-in then with the credentials you had just created
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313840064/SuXqmt_c0.png" alt /></li>
<li>And then you are in the app with the option to logout
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313841410/2jzpTlEOQ.png" alt /></li>
</ul>
</li>
</ul>
<h2 id="heading-set-up-custom-domain">Set up Custom Domain</h2>
<p>If you use AWS <a target="_blank" href="https://aws.amazon.com/route53/">Route53</a> for your DNS
management, the Amplify console can set up a custom DNS Domain and SSL
Certificate to go with your new app!</p>
<p>If you don't use Route53, there is a way to insert a custom DNS but will not show how in this doc.</p>
<ul>
<li>Select the Domain Management option for the application you just created in the Amplify Console
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313842825/eUSa2ODDk.png" alt /><ul>
<li>Click on <code>Add Domain</code></li>
</ul>
</li>
<li>Enter the root domain of the route53 zone you want to add this domain to<ul>
<li>We are using a domain zone (<code>lab.omnyway.net</code>) that is a subdomain<ul>
<li>The important point is it should be the domain that is a zone in your route53</li>
<li>The Route53 zone probably has to be the same AWS account as the App
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313844319/iCgO6Cy9u.png" alt /></li>
</ul>
</li>
<li>Click <code>Configure domain</code></li>
</ul>
</li>
<li>If this is the first time you use this feature, it has to first try some things to get authenticated. <ul>
<li>It will add a weird CNAME to the zone automatically. You don't have to do anything</li>
</ul>
</li>
<li>You could use the defaults if you want<ul>
<li>The root domain to resolve to your webapp</li>
<li><code>www.yourdomain</code> to resolve to your webapp
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313846132/QdCXK1BM8.png" alt /></li>
</ul>
</li>
<li>But we have other things in this domain so we want it to just have a single domainname not <code>www</code><ul>
<li>Click <code>Exclude root</code> so it turns to <code>Include root</code> and dims the pulldown </li>
<li>Replace <code>www</code> with the domain hostname you want.<ul>
<li>In this case the name of the app <code>amplify_cljs_example</code>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313847858/ur3duL4I3.png" alt /></li>
</ul>
</li>
<li>Click <code>Save</code></li>
</ul>
</li>
<li>Assuming it all works<ul>
<li>It will add the CNAME to your route53 zone</li>
<li>It will create an SSL Certificate  and wire it all up</li>
<li>Will get the domain fully activated</li>
<li>You can then use the new url shown to access the webapp</li>
</ul>
</li>
</ul>
<h2 id="heading-wrap-up">Wrap Up</h2>
<p>You now have an example of how to create and deploy a Clojurescript Re-Frame SPA with really nice Authentication capabilities with almost no coding. </p>
<p>The next article (Not yet written) will show how to use the AWS Amplify Authentication services to allow access to other standard AWS services.</p>
]]></content:encoded></item><item><title><![CDATA[AWS Amplify and Clojurescript Re-Frame Part 1]]></title><description><![CDATA[Introduction
AWS Amplify
AWS Amplify makes it super simple to build Web and Mobile Apps that use the entire suite of AWS Services with minimal coding by the app developer. Clojurescript and Re-Frame make creating SPA apps a joy. The combination of al...]]></description><link>https://hashnode.ibd.com/aws-amplify-and-clojurescript-re-frame-part-1</link><guid isPermaLink="true">https://hashnode.ibd.com/aws-amplify-and-clojurescript-re-frame-part-1</guid><dc:creator><![CDATA[Robert J. Berger]]></dc:creator><pubDate>Tue, 21 Jan 2020 19:30:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313852678/gvvjpvG4r.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<h3 id="heading-aws-amplify">AWS Amplify</h3>
<p><a target="_blank" href="https://aws.amazon.com/amplify/">AWS Amplify</a> makes it super simple to build Web and Mobile Apps that use the entire suite of AWS Services with minimal coding by the app developer. Clojurescript and Re-Frame make creating SPA apps a joy. The combination of all three is truly a superpower!</p>
<p>The AWS Amplify CLI makes it easy to setup various AWS services and make them available via the Amplify SDK to your Web or Mobile App. It also has great support for React based apps.</p>
<p>The goal of this project is to show how to be able to use AWS Amplify with Clojurescript / Re-Frame Web Apps.</p>
<p>The initial AWS feature to incorporate is the Amplify Authentication service which has AWS Cognito at its core. Amplify offers a React <a target="_blank" href="https://reactjs.org/docs/higher-order-components.html">Higher Order Component</a> that allows you to wrap your JS app with Authentication.</p>
<ul>
<li>Ensures that your App can only be accessed when authenticated</li>
<li><p>Handles all the UI and API integration with Cognito</p>
<ul>
<li>You do not have to write any of the code</li>
<li>Sign-up<ul>
<li>Verify phone_number or email address</li>
</ul>
</li>
<li>Sign-in</li>
<li>Sign-out</li>
<li>Forgot Password</li>
<li>Password reset</li>
</ul>
</li>
<li><p>Optionally (and not described in this first article but easy to add)</p>
<ul>
<li>Customize Authenticator UI</li>
<li>Use your own UI components</li>
<li>AWS Auth for AWS services which require signing requests.</li>
<li>Social Provider Federation / OAuth</li>
<li>Force new password</li>
<li>Multi-Factor Authentication</li>
<li>User Attributes</li>
<li>Lambda Triggers</li>
</ul>
</li>
</ul>
<p>Note: This article and its followup <a target="_blank" href="https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-2-47gk">AWS Amplify and Clojurescript Re-Frame Part 2: Deploy to Production Cloudfront</a> are based on branch <code>basic-amplify-with-cloudfront</code> of the <code>omnyway-labs/amplify_cljs_example</code> repo.</p>
<h3 id="heading-re-frame-clojurescript-shadow-cljs">Re-Frame / Clojurescript / Shadow-cljs</h3>
<p>The main goal of this project is to show how to leverage Amplify in a <a target="_blank" href="https://clojurescript.org/">Clojurescript</a> Single Page App (<a target="_blank" href="https://en.wikipedia.org/wiki/Single-page_application">SPA</a>). In particular, using the <a target="_blank" href="https://github.com/Day8/re-frame">re-frame</a> Clojurescript Library.</p>
<p>Re-frame is a functional framework for building SPAs leveraging <a target="_blank" href="http://reagent-project.github.io/">Reagent</a>. Reagent is a minimalistic interface between ClojureScript and React.</p>
<p>Clojurescript is hosted on Javascript and can naturally interoperate with Javascript in general. This project uses <a target="_blank" href="http://shadow-cljs.org/">shadow-cljs</a> as the buildtool. Its major superpower is to make it pretty easy to leverage node.js packages. The main features it offers include:</p>
<ul>
<li>Good configuration defaults so you don't have to sweat the details</li>
<li>Seamless npm integration</li>
<li>Fast builds, reliable caching, ...</li>
<li>Supporting various targets :browser, :node-script, :npm-module, :react-native, :chrome-extension, ...</li>
<li>Live Reload (CLJS + CSS)</li>
<li>CLJS REPL</li>
<li>Code splitting (via :modules)</li>
</ul>
<p>Put together, these tools allows one to build SPAs in a functional / Clojure centric way, but still leverage all the wonders of React and node.</p>
<p>That being said. There are a lot of learning curves that also need to be climbed to make it all work together.</p>
<p>This project is an attempt to make it easier for folks to jump right in. It can be used as a starting point to build your own SPA with sophisticated Authentication services.</p>
<p>This article and the associated github repo <a target="_blank" href="https://github.com/omnyway-labs/amplify_cljs_example">omnyway-labs/amplify_cljs_example</a> should tell you everything you need from scratch to get you to a working basis of a re-frame SPA running on your local machine with full AWS Cognito Simple Authentication backend. The amount of code you have to write to make it work is surprisingly trivial due to the scaffolding features of re-frame and the AWS generation / auto-configuration of Amplify.</p>
<p>The second article in this series <a target="_blank" href="https://github.com/omnyway-labs/amplify_cljs_example/wiki/2.-Deploy-to-CloudFront-with-Amplify-Console">Deploy to CloudFront with Amplify Console</a> shows how to deploy the SPA to AWS CloudFront with minimal effort.</p>
<h2 id="heading-assumptions">Assumptions</h2>
<p>You have already installed the following on your laptop / dev machine:</p>
<ul>
<li>An <a target="_blank" href="https://aws.amazon.com">AWS Account</a> that you have administrative access to</li>
<li><a target="_blank" href="https://git-scm.com/">git</a> (Ideally have a <a target="_blank" href="https://github.com/">github</a> account)</li>
<li><a target="_blank" href="https://adoptopenjdk.net/">Java</a></li>
<li><a target="_blank" href="https://nodejs.org/en/">Node.js</a> and <a target="_blank" href="https://www.npmjs.com/">npm</a></li>
<li><a target="_blank" href="https://clojure.org/">Clojure</a></li>
<li><a target="_blank" href="https://clojurescript.org/">Clojurscript</a></li>
<li><a target="_blank" href="http://shadow-cljs.org/">shadow-cljs</a></li>
<li><a target="_blank" href="https://leiningen.org/">leiningen</a></li>
<li>Have an <a target="_blank" href="http://spacemacs.org/">editor</a> and know how to use it </li>
</ul>
<h2 id="heading-initial-setup">Initial Setup</h2>
<p>We're using the <a target="_blank" href="https://github.com/Day8/re-frame-template">re-frame scaffold template</a> to create the initial app. We're not going to add much to the app in this article other than the Amplify Authentication wrapper.</p>
<p>Later articles will add more functionality so we're including some extras like <a target="_blank" href="https://github.com/noprompt/garden">garden</a> for doing CSS in clojure. More importantly we're including <a target="_blank" href="https://re-com.day8.com.au/">re-com</a> Which is a library of ClojureScript UI components, built on top of Reagent. It integrates with re-frame in a very functional / clojure way.</p>
<p><a target="_blank" href="https://github.com/Day8/re-frame-10x">10x</a> is a very nice debugging dashboard for re-frame. Again, won't be using it until later articles but its something you will want to have if you use this for building on top of.</p>
<ul>
<li>Generate the scaffold</li>
</ul>
<pre><code class="lang-shell">lein new re-frame amplify_cljs_example +10x +cider +re-com +test +garden
cd amplify_cljs_example
lein garden once
git init
</code></pre>
<ul>
<li>Update .gitignore by adding:</li>
</ul>
<pre><code><span class="hljs-operator">/</span>resources<span class="hljs-operator">/</span><span class="hljs-keyword">public</span><span class="hljs-operator">/</span>css<span class="hljs-operator">/</span>screen.css
.shadow-cljs
node_modules
</code></pre><ul>
<li>More setup; build and run the local instance of the initial scaffolded SPA</li>
</ul>
<pre><code class="lang-shell">git add -A
git commit -a -m "Initial commit after lein new re-frame amplify_cljs_example +10x +cider +re-com +test +garden"
npm install
git add package-lock.json
git commit -m "package-lock for npm" package-lock.json
lein garden once
lein dev
</code></pre>
<p>The <code>lein dev</code> will build and run the application locally. It doesn't exit, it keeps running, monitoring the app files. If there are any changes it will automatically hot load the application. Lein is effectively running <a target="_blank" href="https://shadow-cljs.github.io/docs/UsersGuide.html#_hot_code_reload">shadow-cljs watch app</a></p>
<ul>
<li>The <code>lein dev</code> probably added more packages for npm and so you should update your git in another shell window in the same directory<pre><code class="lang-shell">git commit -m "lein updates to package.json" package-lock.json  package.json
</code></pre>
<h2 id="heading-ensure-the-basics-are-working">Ensure the basics are working</h2>
</li>
</ul>
<p>So if the <code>lein dev</code> is running properly, it will end with:</p>
<pre><code>[:app] Build completed. (<span class="hljs-number">1659</span> files, <span class="hljs-number">1658</span> compiled, <span class="hljs-number">0</span> warnings, <span class="hljs-number">46</span>.89s)
</code></pre><p>At that point, you should be able to:</p>
<ul>
<li>Go to <code>http://localhost:8280</code> in a browser (Chrome is optimal)</li>
<li>Should look something like:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642313852678/gvvjpvG4r.png" alt="Initial Working Display" /></p>
<h2 id="heading-set-up-aws-amplify">Set up AWS Amplify</h2>
<ul>
<li>You should be in the top level of the <code>amplify_cljs_example</code> directory</li>
<li><p>Assuming you don’t already have AWS Amplify installed on your dev machine:</p>
<pre><code class="lang-shell">npm install -g @aws-amplify/cli
amplify configure
</code></pre>
</li>
<li><p>Follow the instructions as described in <a target="_blank" href="https://youtu.be/fWbM5DLh25U">Installing &amp; Configuring the AWS Amplify CLI - YouTube</a> to configure you local dev amplify environment to manage your AWS environment</p>
</li>
<li>Initialize the Amplify project.<ul>
<li>Mostly accept the defaults or as appropriate for you</li>
<li>Start command : <code>lein dev</code></li>
<li>AWS Profile yes and use the one you created when you did <code>amplify configure</code> which by default is <code>default</code>
```<blockquote>
<p>amplify init
Scanning for plugins...
Plugin scan successful
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project reframerecomamplifye
? Enter a name for the environment dev
? Choose your default editor: Emacs (via Terminal, Mac OS only)
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path:  src
? Distribution Directory Path: build
? Build Command:  lein prod
? Start Command: lein dev
Using default provider  awscloudformation</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<p>For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html</p>
<p>? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default
⠧ Initializing project in the cloud...</p>
<p>CREATE_IN_PROGRESS AuthRole                                AWS::IAM::Role             Fri Oct 04 2019 22:50:40 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS DeploymentBucket                        AWS::S3::Bucket            Fri Oct 04 2019 22:50:40 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UnauthRole                              AWS::IAM::Role             Fri Oct 04 2019 22:50:40 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS AuthRole                                AWS::IAM::Role             Fri Oct 04 2019 22:50:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS DeploymentBucket                        AWS::S3::Bucket            Fri Oct 04 2019 22:50:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UnauthRole                              AWS::IAM::Role             Fri Oct 04 2019 22:50:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 22:50:36 GMT-0700 (Pacific Daylight Time) User Initiated
⠴ Initializing project in the cloud...</p>
<p>CREATE_COMPLETE UnauthRole AWS::IAM::Role Fri Oct 04 2019 22:50:52 GMT-0700 (Pacific Daylight Time)
⠏ Initializing project in the cloud...</p>
<p>CREATE_COMPLETE AuthRole AWS::IAM::Role Fri Oct 04 2019 22:50:52 GMT-0700 (Pacific Daylight Time)
⠹ Initializing project in the cloud...</p>
<p>CREATE_COMPLETE DeploymentBucket AWS::S3::Bucket Fri Oct 04 2019 22:51:01 GMT-0700 (Pacific Daylight Time)
⠇ Initializing project in the cloud...</p>
<p>CREATE_COMPLETE reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 22:51:03 GMT-0700 (Pacific Daylight Time)
✔ Successfully created initial AWS cloud resources for deployments.
✔ Initialized provider successfully.
Initialized your environment successfully.</p>
<p>Your project has been successfully initialized and connected to the cloud!</p>
<pre><code><span class="hljs-bullet">*</span> Add the Amplify Authentication service
<span class="hljs-bullet">    *</span> Details at [<span class="hljs-string">AWS Amplify Authentication</span>](<span class="hljs-link">https://aws-amplify.github.io/docs/js/authentication</span>)
<span class="hljs-bullet">    *</span> Sign-in: Selecting 'Email' and/or 'Phone Number' will allow end users to sign-up using these values.  Selecting 'Username' will require a unique username for users.
</code></pre><blockquote>
<p>amplify add auth
Using service: Cognito, provided by: awscloudformation</p>
</blockquote>
<p> The current configured provider is Amazon Cognito.</p>
<p> Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections.
 How do you want users to be able to sign in? Email
 Do you want to configure advanced settings? No, I am done.
Successfully added resource reframerecomamplifye16cd456a locally</p>
<pre><code><span class="hljs-operator">*</span> Push the auth feature <span class="hljs-operator">/</span> config to AWS
</code></pre><blockquote>
<p>amplify auth push</p>
</blockquote>
<p>Current Environment: dev</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Category</td><td>Resource name</td><td>Operation</td><td>Provider plugin</td></tr>
</thead>
<tbody>
<tr>
<td>Auth</td><td>reframerecomamplifye16cd456a</td><td>Create</td><td>awscloudformation</td></tr>
</tbody>
</table>
</div><p>? Are you sure you want to continue? Yes
⠹ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionRole          AWS::IAM::Role             Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS authreframerecomamplifye16cd456a        AWS::CloudFormation::Stack Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS authreframerecomamplifye16cd456a        AWS::CloudFormation::Stack Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionRole          AWS::IAM::Role             Fri Oct 04 2019 23:08:00 GMT-0700 (Pacific Daylight Time)
UPDATE_IN_PROGRESS reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 23:07:57 GMT-0700 (Pacific Daylight Time) User Initiated
⠇ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS reframerecomamplifye-dev-20191004225035-authreframerecomamplifye16cd456a-19BSJYQRK36 AWS::CloudFormation::Stack Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time) User Initiated
⠋ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS SNSRole AWS::IAM::Role Fri Oct 04 2019 23:08:08 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS SNSRole AWS::IAM::Role Fri Oct 04 2019 23:08:07 GMT-0700 (Pacific Daylight Time)
⠴ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE UpdateRolesWithIDPFunctionRole AWS::IAM::Role Fri Oct 04 2019 23:08:15 GMT-0700 (Pacific Daylight Time)
⠋ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE SNSRole AWS::IAM::Role Fri Oct 04 2019 23:08:20 GMT-0700 (Pacific Daylight Time)
⠙ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE    UserPool AWS::Cognito::UserPool Fri Oct 04 2019 23:08:27 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPool AWS::Cognito::UserPool Fri Oct 04 2019 23:08:27 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPool AWS::Cognito::UserPool Fri Oct 04 2019 23:08:24 GMT-0700 (Pacific Daylight Time)
⠇ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE    UserPoolClientWeb AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:33 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClientWeb AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:33 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_COMPLETE    UserPoolClient    AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:33 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClient    AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:32 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientWeb AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:31 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClient    AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:31 GMT-0700 (Pacific Daylight Time)
⠙ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS UserPoolClientRole AWS::IAM::Role Fri Oct 04 2019 23:08:36 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientRole AWS::IAM::Role Fri Oct 04 2019 23:08:36 GMT-0700 (Pacific Daylight Time)
⠏ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE UserPoolClientRole AWS::IAM::Role Fri Oct 04 2019 23:08:48 GMT-0700 (Pacific Daylight Time)
⠹ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE    UserPoolClientLambda AWS::Lambda::Function Fri Oct 04 2019 23:08:52 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClientLambda AWS::Lambda::Function Fri Oct 04 2019 23:08:52 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientLambda AWS::Lambda::Function Fri Oct 04 2019 23:08:52 GMT-0700 (Pacific Daylight Time)
⠸ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS UserPoolClientLambdaPolicy AWS::IAM::Policy Fri Oct 04 2019 23:08:57 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientLambdaPolicy AWS::IAM::Policy Fri Oct 04 2019 23:08:56 GMT-0700 (Pacific Daylight Time)
⠼ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE UserPoolClientLambdaPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:02 GMT-0700 (Pacific Daylight Time)
⠋ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS UserPoolClientLogPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:06 GMT-0700 (Pacific Daylight Time)
⠸ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS UserPoolClientLogPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:06 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠸ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE UserPoolClientLogPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:12 GMT-0700 (Pacific Daylight Time)
⠸ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS UserPoolClientInputs Custom::LambdaCallout Fri Oct 04 2019 23:09:15 GMT-0700 (Pacific Daylight Time)
⠹ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS IdentityPool         AWS::Cognito::IdentityPool Fri Oct 04 2019 23:09:23 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE    UserPoolClientInputs Custom::LambdaCallout      Fri Oct 04 2019 23:09:19 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClientInputs Custom::LambdaCallout      Fri Oct 04 2019 23:09:19 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠼ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE    IdentityPool AWS::Cognito::IdentityPool Fri Oct 04 2019 23:09:25 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS IdentityPool AWS::Cognito::IdentityPool Fri Oct 04 2019 23:09:24 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠋ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS IdentityPoolRoleMap AWS::Cognito::IdentityPoolRoleAttachment Fri Oct 04 2019 23:09:28 GMT-0700 (Pacific Daylight Time)
⠴ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE    reframerecomamplifye-dev-20191004225035-authreframerecomamplifye16cd456a-19BSJYQRK36 AWS::CloudFormation::Stack               Fri Oct 04 2019 23:09:32 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE    IdentityPoolRoleMap                                                                  AWS::Cognito::IdentityPoolRoleAttachment Fri Oct 04 2019 23:09:30 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS IdentityPoolRoleMap                                                                  AWS::Cognito::IdentityPoolRoleAttachment Fri Oct 04 2019 23:09:30 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠧ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_COMPLETE authreframerecomamplifye16cd456a AWS::CloudFormation::Stack Fri Oct 04 2019 23:09:36 GMT-0700 (Pacific Daylight Time)
⠏ Updating resources in the cloud. This may take a few minutes...</p>
<p>CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionOutputs Custom::LambdaCallout Fri Oct 04 2019 23:09:41 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE    UpdateRolesWithIDPFunction        AWS::Lambda::Function Fri Oct 04 2019 23:09:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UpdateRolesWithIDPFunction        AWS::Lambda::Function Fri Oct 04 2019 23:09:39 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UpdateRolesWithIDPFunction        AWS::Lambda::Function Fri Oct 04 2019 23:09:38 GMT-0700 (Pacific Daylight Time)
⠹ Updating resources in the cloud. This may take a few minutes...</p>
<p>UPDATE_COMPLETE                     reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 23:09:47 GMT-0700 (Pacific Daylight Time)
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 23:09:47 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE                     UpdateRolesWithIDPFunctionOutputs       Custom::LambdaCallout      Fri Oct 04 2019 23:09:45 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS                  UpdateRolesWithIDPFunctionOutputs       Custom::LambdaCallout      Fri Oct 04 2019 23:09:44 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
✔ All resources are updated in the cloud</p>
<pre><code>
<span class="hljs-operator">*</span> Install the Amplify Node modules we’ll be <span class="hljs-keyword">using</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Authentication</span>
```<span class="hljs-title">shell</span>
<span class="hljs-title">npm</span> <span class="hljs-title">add</span> <span class="hljs-title">aws</span><span class="hljs-operator">-</span><span class="hljs-title">amplify</span> <span class="hljs-title">aws</span><span class="hljs-operator">-</span><span class="hljs-title">amplify</span><span class="hljs-operator">-</span><span class="hljs-title">react</span>
</code></pre><ul>
<li>Copy the css for the amplify UI widgets into the proper resource for our app’s
css to be picked up<ul>
<li>Note: I was looking for a better way to do this that is more via
configuration. If anyone knows a better way please let me know</li>
</ul>
</li>
</ul>
<pre><code class="lang-shell">cp node_modules/@aws-amplify/ui/dist/style.css resources/public/css/aws-amplify-ui-style.css
</code></pre>
<ul>
<li><p>Update <code>resources/public/index.html</code> to incorporate that css by adding the following line in the <code>&lt;head&gt;</code> section</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"css/aws-amplify-ui-style.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>&gt;</span>
</code></pre>
</li>
<li><p>Get the changes into git</p>
<pre><code class="lang-shell">git add amplify resources/public/css/aws-amplify-ui-style.css
git commit -m "Added and configured Amplify" .gitignore package-lock.json package.json amplify resources/public/index.html resources/public/css
</code></pre>
</li>
<li>Your application at <code>http://localhost:8280</code> should still be running fine
assuming you still have the <code>lein dev</code> process running</li>
</ul>
<h2 id="heading-update-the-code-to-enable-the-authenticator">Update the code to enable the Authenticator</h2>
<h3 id="heading-edit-srccljsreframerecomamplifyexpcorecljs">Edit <code>src/cljs/re_frame_re_com_amplify_exp/core.cljs</code></h3>
<ul>
<li>Add the following lines to the require block. This is what will import the node js into your Clojurescript</li>
</ul>
<pre><code class="lang-clojure">   ["aws-amplify" :default Amplify :as amp]
   ["aws-amplify-react" :refer (withAuthenticator)]
   ["/aws-exports.js" :default aws-exports]
</code></pre>
<ul>
<li>Add the following def after <code>dev-setup</code> function</li>
</ul>
<p>This is where you are wrapping the Amplify HOC around your application. The <code>withAuthenticator</code> is the HOC function being pulled in from the Amplify Auth SDK. It ensures that your app only is accessed after authentication and it handles all the Frontend UI and communication with the Backend to implement the full suite of Authentication.</p>
<pre><code class="lang-clojure">(def root-view
  (reagent/adapt-react-class
   (withAuthenticator
    (reagent/reactify-component views/main-panel) true)))
</code></pre>
<ul>
<li>Change the function <code>mount-root</code> to be:<pre><code class="lang-clojure">(defn ^:dev/after-load mount-root []
(re-frame/clear-subscription-cache!)
(.configure Amplify aws-exports)
(re-frame/dispatch-sync [::events/initialize-db])
(reagent/render [root-view]
                (.getElementById js/document "app")))
</code></pre>
</li>
<li>Remove the following line from <code>init</code> function:</li>
</ul>
<p>This makes it so that the re-frame db is inited after the cache is cleared. As far as I can tell the Scaffolding puts it in the wrong place</p>
<pre><code class="lang-clojure">(re-frame/dispatch-sync [::events/initialize-db])
</code></pre>
<ul>
<li>The final file should be:
``` clojure
(ns amplify_cljs_example.core
(:require
 [reagent.core :as reagent]
 [re-frame.core :as re-frame]
 [amplify_cljs_example.events :as events]
 [amplify_cljs_example.views :as views]
 [amplify_cljs_example.config :as config]
 ["aws-amplify" :default Amplify :as amp]
 ["aws-amplify-react" :refer (withAuthenticator)]
 ["/aws-exports.js" :default aws-exports]
 ))</li>
</ul>
<p>(defn dev-setup []
  (when config/debug?
    (println "dev mode")))</p>
<p>(def root-view
  (reagent/adapt-react-class
   (withAuthenticator
    (reagent/reactify-component views/main-panel) true)))</p>
<p>(defn ^:dev/after-load mount-root []
  (re-frame/clear-subscription-cache!)
  (.configure Amplify aws-exports)
  (re-frame/dispatch-sync [::events/initialize-db])
  (reagent/render [root-view]
                  (.getElementById js/document "app")))</p>
<p>(defn init []
  (dev-setup)
  (mount-root))</p>
<pre><code><span class="hljs-bullet">*</span> The display should look like this:
![<span class="hljs-string">Initial Working Display</span>](<span class="hljs-link">https://cdn.hashnode.com/res/hashnode/image/upload/v1642313854225/naWPzlKZK.png</span>)

![](assets/1/working<span class="hljs-emphasis">_authentication.png)
* You should click the button on the debugger to have it be in its own window as it will block the “sign-out” button later
![<span class="hljs-string">Initial Working Display</span>](<span class="hljs-link">https://cdn.hashnode.com/res/hashnode/image/upload/v1642313855640/RRL_URKuC.png</span>)
* Commit the changes to git
``` shell
git commit -m "App updated to require Cognito Authenticator before any other operation" src/cljs/re_</span>frame<span class="hljs-emphasis">_re_</span>com<span class="hljs-emphasis">_amplify_</span>exp/core.cljs
</code></pre><h2 id="heading-wrap-up">Wrap Up</h2>
<p>You now have a template of how to create a basic Re-Frame SPA with authentication from AWS Amplify with almost not coding and without having to set up your own backend authentication infrastructure. You can continue with this to use other AWS Amplify authentication features (Social Provider Federation, Multi-factor auth, etc) as well as the other Amplify services such as graphql access to DynamoDB, Push notifications, etc.). </p>
<p>The next article, <a target="_blank" href="https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-2-47gk">AWS Amplify and Clojurescript Re-Frame Part 2: Deploy to Production Cloudfront</a>, will show how to deploy it to "production" on AWS Cloudfront using Amplify Console so that the Re-Frame SPA can be used on the general Internet also without you having to set up any servers yourself.</p>
]]></content:encoded></item></channel></rss>