<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: MatHem Tech</title>
    <description>The latest articles on DEV Community by MatHem Tech (@mathem).</description>
    <link>https://dev.to/mathem</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3470%2F6d3594ca-dd58-42a1-a5ce-6c0bc9eee1e5.png</url>
      <title>DEV Community: MatHem Tech</title>
      <link>https://dev.to/mathem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mathem"/>
    <language>en</language>
    <item>
      <title>An approach to loosely coupled CloudWatch alarms and contextual alerts</title>
      <dc:creator>Lars Jacobsson</dc:creator>
      <pubDate>Tue, 14 Sep 2021 21:50:27 +0000</pubDate>
      <link>https://dev.to/mathem/an-approach-to-loosely-coupled-cloudwatch-alarms-and-contextual-alerts-2fh2</link>
      <guid>https://dev.to/mathem/an-approach-to-loosely-coupled-cloudwatch-alarms-and-contextual-alerts-2fh2</guid>
      <description>&lt;p&gt;We have for the past four years used a third party for monitoring and alerting of our large scale serverless ecommerce platform. &lt;/p&gt;

&lt;p&gt;For a number of reasons, we have recently decided to leave this provider in favour of CloudWatch.&lt;/p&gt;

&lt;p&gt;This post will take you through how we used my two favourite AWS services, EventBridge and StepFunctions, to set up error anomaly detection for 100% of our Lambda functions along with context rich Slack alerts in less than two days. This model supports any resource type and alarm type, but for brevity I will focus on error anomaly alarms for Lambda.&lt;/p&gt;

&lt;p&gt;Some understanding of both &lt;a href="https://aws.amazon.com/eventbridge/" rel="noopener noreferrer"&gt;EventBridge&lt;/a&gt; and &lt;a href="https://aws.amazon.com/step-functions/" rel="noopener noreferrer"&gt;StepFunctions&lt;/a&gt; is assumed.&lt;/p&gt;




&lt;p&gt;One thing we lacked with the third party solution was a coupling with CloudFormation stacks. Monitors were created by hand and if something was removed on the AWS side we were left with an orphaned monitor.&lt;/p&gt;

&lt;p&gt;Being obsessed with automation, we needed a fast way to onboard all core resources to being monitored by CloudWatch. As a first approach we build &lt;a href="https://github.com/mhlabs/cfn-alarms" rel="noopener noreferrer"&gt;cfn-alarms&lt;/a&gt;, a CLI tool to generate CloudFormation alarm and alerting boilerplate resources based on the resources in a template, but to roll that out on hundreds of stacks proved inefficient. However, we still wanted to tie the alarms with the lifecycle of the resources they monitor so that when a Lambda function is deleted, the associated alarms are deleted with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loosely coupled alarms
&lt;/h2&gt;

&lt;p&gt;At first we looked at what we require our teams to monitor when they deploy features to our platform. We came up with a quite small list of services including Lambda errors, SQS queue depth, API 5XX/4XX rate, etc.&lt;/p&gt;

&lt;p&gt;When new resources are created or deleted, CloudTrail emits events to EventBridge's default bus with the configuration of the resource. To create or delete alarms we can simply create rules to match these events and route them to Lambda functions that programmatically spin them up or down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  LambdaCreation:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./src
      Handler: lambda/creation.handler
      Events:
        LambdaCreationEvent:
          Type: EventBridgeRule
          Properties:
            InputPath: $.detail.requestParameters
            EventBusName: default
            Pattern:
              source:
                - aws.lambda
              detail-type:
                - AWS API Call via CloudTrail
              detail:
                eventName:
                  - prefix: CreateFunction
    [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rule will forward the CloudTrail event's requestParameters to the function handler, which looks like this (truncated for brevity. Refer to the &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudWatch.html#putMetricAlarm-property" rel="noopener noreferrer"&gt;SDK docs&lt;/a&gt; for syntax):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exports.handler = async (event) =&amp;gt; {
  const functionName = event.functionName;
  const tags = event.tags;

  const threshold = tags["alarm:lambda:errors:anomaly:threshold"] || 2;

  await cloudWatch
    .putAnomalyDetector({
        ... truncated ...
    })
    .promise();

  await cloudWatch
    .putMetricAlarm({
      AlarmName: `auto:${functionName}:lambda:errors:anomaly`,
      AlarmDescription: `Error anomaly detected`,
      Tags: Object.keys(tags).map((p) =&amp;gt; {
        return { Key: p, Value: tags[p] };
      }),
      ... truncated ...
    })
    .promise();
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how we allow for customisation of certain alarm configurations by using a tagging strategy of &lt;code&gt;alarm:&amp;lt;service&amp;gt;:&amp;lt;metric&amp;gt;:&amp;lt;evaluation type&amp;gt;:&amp;lt;variable&amp;gt;&lt;/code&gt;. This naming is reflected in the &lt;code&gt;AlarmName&lt;/code&gt; property for a semantic coupling. Also note how we relay the tags from the monitored resource onto the alarm resource. This will later be used in the alerting phase when monitors go in and out of alarm.&lt;/p&gt;

&lt;p&gt;The alarm lifecycle event flow is very simple and looks like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nkrqclfpsun3nv7n923.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nkrqclfpsun3nv7n923.png" alt="Alarm creation and deletion diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach also allows us to build custom tooling to onboard or reconfigure the entire platform in one go without the need of deploying all stacks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Alerting
&lt;/h2&gt;

&lt;p&gt;Getting alerting right is difficult and is a balance act of not skipping valuable alerts, but at the same time keeping it brief and relevant to avoid &lt;a href="https://en.wikipedia.org/wiki/Alarm_fatigue" rel="noopener noreferrer"&gt;alarm fatigue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At Mathem we use Slack across all development teams and we have a requirement to tag all resources with the team name owning the service. We wanted to leverage this to automatically direct alerts to team specific Slack channels. This is why we relay the resource tags on the alarm resource in the code example above.&lt;/p&gt;

&lt;p&gt;When a CloudWatch alarm changes state there's an event put on EventBridge. A state can be one of &lt;code&gt;OK&lt;/code&gt;, &lt;code&gt;ALARM&lt;/code&gt; and &lt;code&gt;INSUFFICIENT_DATA&lt;/code&gt;. We are interested in alerting on &lt;code&gt;ALARM&lt;/code&gt; and communicating recovery on &lt;code&gt;OK&lt;/code&gt;. The aim is to be as brief as possible whilst giving the developers quick access to extended context, such as logs and CloudWatch metrics related to the alarm. For this we'll use a StepFunctions state machine triggered by an EventBridge rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source:
  - aws.cloudwatch
detail-type:
  - CloudWatch Alarm State Change
detail:
  state:
    value:
      - ALARM
      - OK
resources:
  - prefix: !Sub &amp;gt;-
      arn:aws:cloudwatch:${AWS::Region}:${AWS::AccountId}:alarm:auto:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the prefix matching on the alarm ARN. Including &lt;code&gt;auto:&lt;/code&gt; at the end ensures only automatically created alarms are consumed by the state machine.&lt;/p&gt;

&lt;p&gt;The state machine takes different actions depending on the alarm state and resource type the alarm is concerning.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffiqwe10bsqkpew4cafty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffiqwe10bsqkpew4cafty.png" alt="State machine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first state simply fetches the tags from the alarm resource. The tags we are interested in are &lt;code&gt;team&lt;/code&gt;, &lt;code&gt;aws:cloudformation:stack-name&lt;/code&gt; and &lt;code&gt;aws:cloudformation:logical-id&lt;/code&gt;. The &lt;code&gt;team&lt;/code&gt; tag decides where to send the alert and the &lt;code&gt;aws:cloudformation:...&lt;/code&gt; tags are used to make the alert message more human readable.&lt;/p&gt;

&lt;p&gt;Next, we check if it's an alarm or a recovery. If it's an alarm we'll send a message to a Slack channel following &lt;code&gt;#alerts-&amp;lt;teamname&amp;gt;-&amp;lt;environment&amp;gt;&lt;/code&gt;. If the channel doesn't exist, our Slack bot creates it for us.&lt;/p&gt;

&lt;p&gt;The alert is short and to the point. It also provides the developer buttons that instantly takes them to either the alarm page or the failing resource's page in the AWS console. This saves us from wasting time manually clicking our way to the root cause.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeu6wbc2a3jtpadtuhpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeu6wbc2a3jtpadtuhpz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the message is sent we pass the output to a parallel state. In one branch we store the message id, or timestamp, we get back from Slack to a DynamoDB table. We'll retrieve this when the alarm has recovered to update the original message.&lt;/p&gt;

&lt;p&gt;The other branch in the parallel state allow us to extend the alarm notification with additional context depending on the resource type we're notifying about. At this point in time we have only implemented extra context for Lambda error alarms for which we fetch the most recent error log and post it as a thread reply along with a link to the log group:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs88kwdjijajq4c3cb1c8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs88kwdjijajq4c3cb1c8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This can be extended with for example X-Ray data or whatever might be useful without flooding the channel.&lt;/p&gt;

&lt;p&gt;When an alarm reach an &lt;code&gt;OK&lt;/code&gt; state we update the original alarm instead of posting a new message.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This post covered an approach using EventBridge and Lambda to onboard a large set of resources to be monitored by CloudWatch Alarms and StepFunctions to create contextual  Slack alerts that can easily be extended to other notification channels.&lt;/p&gt;

&lt;p&gt;You can find a reference project for this post &lt;a href="https://github.com/ljacobsson/cw-alarms-poc" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The state of the project is early days and be mindful of &lt;a href="https://aws.amazon.com/cloudwatch/pricing/" rel="noopener noreferrer"&gt;CloudWatch Alarm costs&lt;/a&gt; before deploying.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>eventbridge</category>
      <category>stepfunctions</category>
    </item>
    <item>
      <title>Accelerate your serverless development with sam-patterns-cli</title>
      <dc:creator>Lars Jacobsson</dc:creator>
      <pubDate>Tue, 06 Apr 2021 21:17:39 +0000</pubDate>
      <link>https://dev.to/mathem/accelerate-your-sam-development-with-sam-patterns-cli-1f08</link>
      <guid>https://dev.to/mathem/accelerate-your-sam-development-with-sam-patterns-cli-1f08</guid>
      <description>&lt;p&gt;&lt;em&gt;This post targets users of the AWS Serverless Application Model (SAM)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AWS recently released the &lt;a href="https://serverlessland.com/patterns"&gt;Serverless Patterns Collection&lt;/a&gt; which serves as a central directory of common best practice serverless patterns that, when they are combined, can make up complex applications.&lt;/p&gt;

&lt;p&gt;At Mathem we have a home built templating engine that contains patterns that are common to our SAM stacks which developers can inject directly into their templates. Many of these are specific to our use cases and not easy to open source.&lt;/p&gt;

&lt;p&gt;Contributing to our solution is fairly easy, but is still enough of a learning curve and a context switching exercise for the collection to remain more basic than we’d desire.&lt;/p&gt;

&lt;p&gt;Our initial thought when we saw the Serverless Patterns Collection announcement was to build a command line interface around it to make the patterns easily accessible and usable directly from the code editor. From experience we've learned that for such collection to be alive and grow there has to be minimal effort involved in contributing to it. The goal is that a pattern gets written once, then shared and reused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing sam-patterns-cli
&lt;/h2&gt;

&lt;p&gt;Installation:&lt;br&gt;
&lt;code&gt;npm install -g sam-patterns-cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is still a very young project under development - version 0.0.9 at the time of writing, but is nevertheless ready to use.&lt;/p&gt;

&lt;p&gt;It’s by default linked to the Serverless Patterns Collection’s &lt;a href="https://github.com/aws-samples/serverless-patterns"&gt;GitHub repository&lt;/a&gt;, but more sources, public or private, can be added.&lt;/p&gt;

&lt;p&gt;The tool comes with 4 commands;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;source&lt;/strong&gt; - Lets the user add their own repositories. This could be a private and company specific repository or some other collection, like Jeremy Daly’s &lt;a href="https://www.jeremydaly.com/serverless-reference-architectures/"&gt;Serverless Reference Architectures&lt;/a&gt;. See the &lt;a href="https://github.com/mhlabs/sam-patterns-cli#sam-patterns-source"&gt;readme&lt;/a&gt; for instructions on how to link it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;explore&lt;/strong&gt; - Lets the user navigate available patterns and visualise then using the power of &lt;a href="https://github.com/mhlabs/cfn-diagram"&gt;cfn-diagram&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;import&lt;/strong&gt; - Lets the user import a pattern straight into their CloudFormation/SAM template without manually copy/pasting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;share&lt;/strong&gt; - When working on a template, a developer might identify some resources making up a serverless pattern. This lets them extract that pattern and share it with other users of the tool by pushing it to a patterns collection on GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following example we'll build an application that receives order updates from EventBridge, processes the order in a Lambda function and stores it in an S3 bucket. There will be no focus on the function code itself - just the SAM template.&lt;/p&gt;

&lt;p&gt;We launch &lt;code&gt;sam-patterns import&lt;/code&gt; and get presented with a list of available patterns. Searching for ‘eventbridge lambda’ gives us a few options. These patterns are named according to the flow order of them. In our case the ideal pattern would be ‘eventbridge-lambda-s3’, but the closest one is ‘eventbridge-lambda’, so we’ll choose that.&lt;/p&gt;

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

&lt;p&gt;At the time of writing there was no pattern where a Lambda function writes to an S3 bucket, so we’ll go ahead and create one. The template now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWSTemplateFormatVersion: 2010-09-09
Transform:
 - AWS::Serverless-2016-10-31
Resources:
 OrderConsumer:
   Type: AWS::Serverless::Function
   Properties:
     CodeUri: src/
     Handler: OrderConsumer.handler
     Runtime: nodejs14.x
     Timeout: 3
     Policies:
       - S3WritePolicy:
         BucketName: !Ref OrderBucket
     Environment:
       Variables:
         OrderBucket: !Ref OrderBucket
     Events:
       Trigger:
         Type: EventBridgeRule
         Properties:
           EventBusName: custom-bus
           Pattern:
             source:
               - order-service
             detail-type:
               - order-updated
 OrderBucket:
   Type: AWS::S3::Bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have now made a modification to an existing pattern that makes up a new pattern; EventBridge-&amp;gt;Lambda-&amp;gt;S3.&lt;/p&gt;

&lt;p&gt;To avoid having to write the same code again or to help someone else in your team or in the world to quickly create the same pattern you can now share it back to GitHub. If the pattern is of a generic nature we encourage you to submit a pull request to &lt;a href="https://github.com/aws-samples/serverless-patterns"&gt;aws-samples/serverless-patterns&lt;/a&gt;, but to quickly share it with your team you can submit it to a repository of your own.&lt;/p&gt;

&lt;p&gt;To do this, we’ve created a repository, github.com/mhlabs/sam-patterns-collection that will serve as our patterns repository. Next we’ll add it as a source for sam-patterns-cli. For this we use &lt;code&gt;sam-patterns source&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;We’ve now linked our repository and we’re ready to use &lt;code&gt;sam-patterns share&lt;/code&gt; to share the pattern with the world.&lt;/p&gt;

&lt;p&gt;It’s likely that the developer reusing this pattern will work on a stack that handles something else than orders, so in this step we’ll make the shared template dynamically editable by the end user.&lt;/p&gt;

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

&lt;p&gt;That’s it! The new pattern is ready to be used by ourselves or by someone else needing the same functionality.&lt;/p&gt;

&lt;p&gt;Let’s say that someone is building a payment processor service that receives payments from EventBridge, processes them and stores the records in S3. They’ll be up and running in seconds:&lt;/p&gt;

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

&lt;p&gt;The user gets prompted to fill in the dynamic values; item name, Lambda runtime, eventbus name, pattern source and detail-type and that’s all they need to do. Now, this was a simple example for the sake of brevity. More complex patterns can easily be created and shared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This example took us through how to import a serverless pattern, modify it into a new pattern, share it and, finally, reuse it.&lt;/p&gt;

&lt;p&gt;To understand how the dynamic values work, please head over to the &lt;a href="https://github.com/mhlabs/sam-patterns-collection/blob/main/eventbridge-lambda-s3/template.yaml"&gt;pattern we created&lt;/a&gt; in this example and study the Metadata section of the template.&lt;/p&gt;

&lt;p&gt;At this point in time only SAM template code can be imported and shared. The aim is to support backing files, such as Lambda function code and OpenAPI definitions in the future.&lt;/p&gt;

&lt;p&gt;This project is open source and can be found &lt;a href="https://github.com/mhlabs/sam-patterns-cli"&gt;here&lt;/a&gt;. Contributions, bug reports and feature requests make us happy :-)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Maximize your EventBridge productivity with evb-cli</title>
      <dc:creator>Lars Jacobsson</dc:creator>
      <pubDate>Tue, 29 Dec 2020 07:46:56 +0000</pubDate>
      <link>https://dev.to/mathem/maximize-your-eventbridge-productivity-with-evb-cli-2o3l</link>
      <guid>https://dev.to/mathem/maximize-your-eventbridge-productivity-with-evb-cli-2o3l</guid>
      <description>&lt;p&gt;The aim of this article is to demonstrate how &lt;a href="https://www.npmjs.com/package/@mhlabs/evb-cli"&gt;evb-cli&lt;/a&gt; can be used to boost your productivity when working with &lt;a href="https://aws.amazon.com/eventbridge/"&gt;Amazon EventBridge&lt;/a&gt;. I will present use cases and how you can use the tool to solve them.&lt;/p&gt;

&lt;p&gt;I assume that you are familiar with or a user of EventBridge. If not, there are some &lt;a href="https://www.youtube.com/watch?v=0gPZx9ex9gY"&gt;great videos&lt;/a&gt; and &lt;a href="https://aws.amazon.com/blogs/compute/tag/amazon-eventbridge/"&gt;blog posts&lt;/a&gt; available to get started.&lt;/p&gt;

&lt;p&gt;This is part one of two covering this tool. Some commands require a back-end deployed to the target account and for the sake of digestability I will cover them in a separate post at a later date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Background&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Installation&lt;/li&gt;
&lt;li&gt;Schema Registry basics&lt;/li&gt;
&lt;li&gt;
Commands

&lt;ul&gt;
&lt;li&gt;
evb pattern - generate event patterns from schemas in the Schema Registry&lt;/li&gt;
&lt;li&gt;
evb input - generate InputTransformer based on schemas in the Schema Registry&lt;/li&gt;
&lt;li&gt;
evb code-binding - generate code bindings for you Lambda consumers&lt;/li&gt;
&lt;li&gt;
evb browse - browse where and how events from a given source/detail-type are consumed &lt;/li&gt;
&lt;li&gt;
evb diagram - Visualise your EventBridge architecture&lt;/li&gt;
&lt;li&gt;
evb extract-sam-event - convert SAM notation to &lt;code&gt;AWS::Events::Rule&lt;/code&gt; for more complex use cases&lt;/li&gt;
&lt;li&gt;
evb test-event - test event pattern against an event payload&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Commands covered in part two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;evb replay - replay archived events against select target(s)&lt;/li&gt;
&lt;li&gt;evb replay-dead-letter - replay dead letter events&lt;/li&gt;
&lt;li&gt;evb local - local debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Background &lt;a&gt;&lt;/a&gt;&lt;a&gt;
&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;EventBridge was released in July 2019, but it took about six months before we started using it heavily at &lt;a href="https://mathem.se"&gt;MatHem&lt;/a&gt;. Up until then we were leveraging SNS and SQS for our events, but as we started playing with EventBridge we quickly noticed how it improved the decoupling of our services and decreased friction between teams. &lt;/p&gt;

&lt;p&gt;Prior to EventBridge, when someone from team A wanted to subscribe to messages using filtering from an SNS topic owned by team B, they had to submit a pull request to team B to add the message attribute they wanted to filter on. We found this being backwards and inefficient.&lt;/p&gt;

&lt;p&gt;With EventBridge, the producing team doesn't need to know its consumers. The consumers have access to perform filtering on any part of the payload, but they still need to know the contract of the data they can filter on. The release of the &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-amazon-eventbridge-schema-registry-now-in-preview/"&gt;EventBridge Schema Registry&lt;/a&gt; at re:Invent 2019 meant that the decoupling got even more optimized.&lt;/p&gt;

&lt;p&gt;As we started composing patterns we found that although we had removed team friction we still spent a lot of time on getting the patterns right. Also, writing complex patterns is error prone and requires multiple deploys to test and get right, so we began automating the bridge between the Schema Registry and the pattern composition.&lt;/p&gt;

&lt;p&gt;The first version of &lt;code&gt;evb-cli&lt;/code&gt; had that sole purpose - to generate event patterns to be pasted into the template. As we got really fast at that, we quickly found further bottlenecks, so we added commands for composing &lt;code&gt;InputTransformer&lt;/code&gt;s, architecture visualization, code bindings, etc. The sections below describe each command in depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Much of the functionality is based on content in the EventBridge Schema Registry. Make sure you have &lt;a href="https://aws.amazon.com/blogs/compute/introducing-amazon-eventbridge-schema-registry-and-discovery-in-preview/#:~:text=To%20discover%20the%20schema%2C%20in,bus%20and%20choose%20Start%20discovery."&gt;enabled schema discovery&lt;/a&gt; on your event buses.&lt;/p&gt;

&lt;p&gt;To get the most of this tool you need to have the &lt;a href="https://aws.amazon.com/cli/"&gt;aws-cli&lt;/a&gt; preconfigured with an IAM or SSO user with permissions covering &lt;code&gt;events:Get*&lt;/code&gt;, &lt;code&gt;events:List*&lt;/code&gt;, &lt;code&gt;schemas:Get*&lt;/code&gt;, &lt;code&gt;schemas:List*&lt;/code&gt; and &lt;code&gt;events:List*&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You will also need &lt;a href="https://www.npmjs.com/"&gt;NPM package manager&lt;/a&gt; and NodeJS 12+ installed on your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npm install -g @mhlabs/evb-cli&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema Registry basics &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To make the most of this tool it's important to understand the basics and the limitations of the EventBridge Schema Registry.&lt;/p&gt;

&lt;p&gt;There are three types of registries;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS event schema registry - the schemas of the events the AWS services produce. These are the events that live on the default event bus&lt;/li&gt;
&lt;li&gt;Discovered schema registry - on a custom eventbus you can enable schema discovery. These schemas for these events end up here. They can be from an SaaS-provider or your own custom events&lt;/li&gt;
&lt;li&gt;Custom schema registry - here you can upload your own schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The discoverer ingests a sampling of the real events and analyses the values. An annoying thing here is that it can't handle null very well and seem to create new versions from 2 events where a value is null in one and non-null in the other. This means when we run commands like &lt;code&gt;evb pattern&lt;/code&gt; it only knows the structure of the &lt;em&gt;last&lt;/em&gt; event it ingested. If that contained many null values it simply won't know about them.&lt;/p&gt;

&lt;p&gt;Another important thing to know is that what constitutes a schema is the combination of &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;detail-type&lt;/code&gt;. For example, the following event will get an entry in the registry under the name &lt;code&gt;my-source@my-detail-type&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "source": "my-source",
  "detail-type": "my-detail-type
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's therefore a good practice to stick to the same &lt;code&gt;detail&lt;/code&gt; structure for all events sharing that combination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commands &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The commands of the CLI have come to life organically as we identified the need for them. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;evb pattern&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Purpose:&lt;/em&gt; Quickly and accurately build event patterns to match events from the EventBridge Schema Registry&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example use case&lt;/em&gt;: You are tasked with sending a Slack message to a channel each time a CodePipeline execution in any US region fails.&lt;/p&gt;

&lt;p&gt;The events from CodePipeline look like this (taken from &lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/event-types.html#codepipeline-event-type"&gt;here&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "version": "0",
  "id": "CWE-event-id",
  "detail-type": "CodePipeline Pipeline Execution State Change",
  "source": "aws.codepipeline",
  "account": "123456789012",
  "time": "2017-04-22T03:31:47Z",
  "region": "us-east-1",
  "resources": [
    "arn:aws:codepipeline:us-east-1:123456789012:pipeline:myPipeline"
  ],
  "detail": {
    "pipeline": "myPipeline",
    "version": "1",
    "state": "STARTED",
    "execution-id": "01234567-0123-0123-0123-012345678901"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern you're after looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source:
  - "aws.codepipeline"
detail-type:
  - "CodePipeline Pipeline Execution State Change"
region:
  - prefix: "us-"
detail:
  state:
    - "FAILED"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though this is a very simple pattern it still requires googling the event structure if you want to compose it manually.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;evb pattern&lt;/code&gt; you can solve this in seconds:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/K48-gfii9p8"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Note that I'm passing &lt;code&gt;-t template.yaml&lt;/code&gt; to the command. This will prompt you with the option to attach the generated pattern to existing resources in your template. These can either be of type &lt;code&gt;AWS::Serverless::Function&lt;/code&gt; or &lt;code&gt;AWS::Events::Rule&lt;/code&gt;. If you skip the &lt;code&gt;-t&lt;/code&gt; flag, the event will be written to the console.&lt;/p&gt;

&lt;p&gt;Template injection works for both YAML and JSON, but note that any YAML comments will be stripped during serialization.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;evb input&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Purpose:&lt;/em&gt; Quickly and accurately build &lt;code&gt;InputTransformer&lt;/code&gt; CloudFormation objects.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example use case&lt;/em&gt;: Building on the use case above we now want to invoke a Lambda function when events from CodePipeline match our pattern. To make the function code simpler, we only want to forward the properties we actually need from the event payload. Let's say we want to let the Slack users know the name of the pipeline and the region it was running in.&lt;/p&gt;

&lt;p&gt;To achieve this we need to compose an &lt;code&gt;InputTransformer&lt;/code&gt; block for the &lt;code&gt;Target&lt;/code&gt; under the &lt;code&gt;AWS::Events::Rule&lt;/code&gt; resource that maps JSON paths form the original payload into a new template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;InputTransformer:
  InputPathsMap:
    region: "$.region"
    pipeline: "$.detail.pipeline"
  InputTemplate: "{\"region\": &amp;lt;region&amp;gt;, \"pipeline\": &amp;lt;pipeline&amp;gt;}" 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is tedious copy/paste work and, at least for us, the &lt;code&gt;InputTemplate&lt;/code&gt; is often a straight off representation of the &lt;code&gt;InputPathsMap&lt;/code&gt;. A nice feature would be if the CloudFormation team made &lt;code&gt;InputTemplate&lt;/code&gt; optional and default it to mirror the &lt;code&gt;InputPathsMap&lt;/code&gt; if omitted.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;evb input&lt;/code&gt; is very similar to evb-pattern, but the output is different. Also, at the time of writing it doesn't yet support template injection, so a copy/paste from the console is required:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/oa23XcZPJmk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Note that I pass in &lt;code&gt;-f yaml&lt;/code&gt;. This is telling the tool to output the result in YAML. Default is JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;evb code-binding&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Purpose:&lt;/em&gt; Generate code bindings in your preferred language for your Lambda function to use.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example use case&lt;/em&gt;: Given the above &lt;code&gt;InputTemplate&lt;/code&gt; we now want to receive this as a strongly typed object. For this example I will use C#, but a fill list of supported languages can be found in &lt;a href="https://github.com/quicktype/quicktype#target-languages"&gt;quicktype's documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the following video I generate two types of bindings; one for the &lt;code&gt;InputTemplate&lt;/code&gt; and one for the entire original event.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/nbuY-HS0VAU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To get the optimized code binding for my &lt;code&gt;InputTemplate&lt;/code&gt; (FromTemplate.cs), I run &lt;code&gt;evb code-binding&lt;/code&gt; and point it at my template. At first I get prompted which language I want to generate for. Next it asks me in which registry the schema can be found. Since we're working with AWS produced events, it's under &lt;code&gt;aws.events&lt;/code&gt;. If it's from a custom event bus, then you'd select &lt;code&gt;discovered-schemas&lt;/code&gt;. Lastly, I select which &lt;code&gt;Rule&lt;/code&gt; and &lt;code&gt;Target&lt;/code&gt; combination I want to generate for. Here we only have one choice, so I select that one.&lt;/p&gt;

&lt;p&gt;For the sake of demo I also generate a class for the entire original untransformed event (FromSchema.cs). I do this by skipping to provide the &lt;code&gt;-t&lt;/code&gt; flag. The tool now takes me through prompts to select language, registry, source and detail-type. As you can see, the output is very verbose and the majority of properties will not be of any interest to the function.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;evb browse&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Purpose:&lt;/em&gt; Quickly find usages of events conforming to a given schema.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example use cases:&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I could be a producer of an event and I've sent broken data for a period of time and I need to alert downstream teams&lt;/li&gt;
&lt;li&gt;I'm about to start working on a feature and I want to check if something similar already exists in the system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/C_qrJd6GIiI"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Here we can see our Lambda target that consumes events belonging to the &lt;code&gt;aws.codepipeline@CodePipelinePipelineExecutionStateChange&lt;/code&gt; schema along with some other pre-existing consumers. I can view the event pattern and any input transformations.&lt;/p&gt;

&lt;p&gt;Another way to browse events is to visualize them using &lt;code&gt;evb diagram&lt;/code&gt; as described below.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;evb diagram&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Purpose:&lt;/em&gt; Visualize how events are flowing within an event bus&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example use cases:&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To have a discussion around during project planning meetings&lt;/li&gt;
&lt;li&gt;To quickly get a mental view of how things hang together&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In my next post I will go through setting up the &lt;code&gt;evb-local&lt;/code&gt; back-end. With that deployed on the target account you are able to see the events as JSON in real-time as they happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;evb extract-sam-event&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Purpose:&lt;/em&gt; To convert a SAM &lt;a href="https://github.com/aws/serverless-application-model/blob/master/versions/2016-10-31.md#eventbridgerule"&gt;EventBridgeRule&lt;/a&gt; to an &lt;code&gt;AWS::Event::Rule&lt;/code&gt; to get more control over the rule.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example use case:&lt;/em&gt; Often when you need an EventBridge to Lambda integration it's tempting to start with the &lt;a href="https://github.com/aws/serverless-application-model/blob/master/versions/2016-10-31.md#eventbridgerule"&gt;EventBridgeRule&lt;/a&gt; SAM shorthand. Under the hood, the SAM macro inflates this into a full &lt;code&gt;AWS::Events::Rule&lt;/code&gt; and an &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt;, but using this disables you from using the powerful &lt;code&gt;InputTransformer&lt;/code&gt;. I've opened an &lt;a href="https://github.com/aws/serverless-application-model/issues/1535"&gt;issue&lt;/a&gt; about this, so please upvote. &lt;/p&gt;

&lt;p&gt;I've found myself refactoring a SAM &lt;code&gt;EventBridgeRule&lt;/code&gt; into the real thing enough times to automate it with a simple command. Again, note that if you have comments in your YAML, then these will be stripped using this command.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;code&gt;evb test-event&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Purpose:&lt;/em&gt; Tests an event payload against rules on an event bus.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example use cases:&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have been given an example payload to consume and you want to test your rule.&lt;/li&gt;
&lt;li&gt;You are a producer of events and want to see which rules match it.&lt;/li&gt;
&lt;li&gt;Integration tests&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post I've gone through how we optimize many of the tasks developers working with EventBridge encounter - from building patterns, input transformers, code bindings to gaining insights into the system by browsing schema usage or visualizing the architecture.&lt;/p&gt;

&lt;p&gt;In my next post I will focus on local debugging and replaying of archived events.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>eventbridge</category>
    </item>
    <item>
      <title>Turning hand written shopping lists into online carts using Amazon Textract</title>
      <dc:creator>Lars Jacobsson</dc:creator>
      <pubDate>Wed, 23 Dec 2020 13:41:40 +0000</pubDate>
      <link>https://dev.to/mathem/turning-hand-written-shopping-lists-into-online-carts-using-amazon-textract-5eb5</link>
      <guid>https://dev.to/mathem/turning-hand-written-shopping-lists-into-online-carts-using-amazon-textract-5eb5</guid>
      <description>&lt;p&gt;&lt;em&gt;MatHem is Sweden's leading independent online grocery shop, with a distribution network reaching over 50% of Swedish households and a strong brand built over the past ten years.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Motivation&lt;/li&gt;
&lt;li&gt;Hardware&lt;/li&gt;
&lt;li&gt;
Device software

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Components setup&lt;/li&gt;
&lt;li&gt;Capture and upload image&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Backend architecture

&lt;ul&gt;
&lt;li&gt;Choosing AI service&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Designing the case&lt;/li&gt;

&lt;li&gt;End result&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Motivation &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Using your fingers to type on a keyboard or phone when searching for things to buy is the assumed most efficient way to shop online. Along came voice assistants a few years ago as a good alternative to the screen.&lt;/p&gt;

&lt;p&gt;I think there's a good point in minimizing pulling the phone up from our pockets and in my family we have a habit of using old fashioned hand written shopping lists that, when it's time to place the order, we type into the search bar at &lt;a href="//mathem.se"&gt;Mathem&lt;/a&gt;. This means we're writing it twice - once with a pen and once with a keyboard which is way too inefficient.&lt;/p&gt;

&lt;p&gt;To tackle this problem I decided to bridge the gap between the physical list and the online cart by building a scanning device that uses hand writing recognition along with existing Mathem APIs. I wanted to have this done as a weekend project, and knowing that the 3D modelling and printing would be the most time consuming parts, I decided to time cap the development time to a minimum and write as little code as possible. &lt;/p&gt;

&lt;p&gt;Since MatHem is already runs on a 100% serverless architecture, the obvious path was to hook this into that and rely on AWS's serverless offerings for AI/ML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware components &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I decided to use components I had lying around at home;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/ELEMENT-Element14-Raspberry-Pi-Motherboard/dp/B07P4LSDYV" rel="noopener noreferrer"&gt;Raspberry Pi 3 Model B+&lt;/a&gt; running latest Raspberry Pi OS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://joy-it.net/en/products/SEN-BUMP01" rel="noopener noreferrer"&gt;Bump sensor&lt;/a&gt;. Used as button. There are probably better buttons out there, but this was accessible&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Raspberry-Pi-Camera-Module-Megapixel/dp/B01ER2SKFS/" rel="noopener noreferrer"&gt;Raspberry Pi Camera Module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The case was printed using a &lt;a href="https://www.amazon.com/Creality-Ender-Printer-Year-Warranty/dp/B08GJ8FV37" rel="noopener noreferrer"&gt;Creality Ender Pro 3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wiring the bump sensor is easily done. It has three pins - one for ground, one for power and one for GPIO - take note of the pin you use. In the code below I use GPIO 12, but it could be any GPIO.&lt;/p&gt;

&lt;h2&gt;
  
  
  Device software &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Firstly I had to decide where to do the inference - on the edge or in the cloud. There are solutions for this that can run on the Pi, but that would tie this feature to the hardware. What if I want to move it to a less performant hardware or integrate it in our mobile apps?&lt;/p&gt;

&lt;p&gt;The only benefit I could see for running it on the Raspberry Pi would be that it'd probably be faster since it would be fewer round trips to the cloud.&lt;/p&gt;

&lt;p&gt;With extensibility in mind I decided to make the edge device dumb and literally just capture the image and upload to the cloud and handle the AI there.&lt;/p&gt;

&lt;h4&gt;
  
  
  Authentication &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;At Mathem we use Cognito User Pools for authentication. Since the UI for this this device is just a bump sensor we need to perform a headless login. I perform this the same way as you do &lt;a href="https://www.raspberrypi.org/documentation/configuration/wireless/headless.md" rel="noopener noreferrer"&gt;headless authentication&lt;/a&gt; to your WiFi network, but with a custom file in the boot partition, &lt;code&gt;mh-credentials.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "username": "me@email.com",
    "password": "mypassword"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;make sure to change your Pi's default password and make sure that it's not publicly accessible over the internet&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/home/pi/camera&lt;/code&gt; we place a python script, camera.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import RPi.GPIO as GPIO
import time
import json
from picamera import PiCamera
from pycognito import Cognito
import requests

userPoolId = 'eu-west-1_abcdefgh'
appClientId = '1unghcf5krf83i76325abcdefg'

credentials = None
with open('/boot/mh-credentials.json') as credentials_file:
    credentials = json.load(credentials_file)

user = Cognito(userPoolId, appClientId, username=credentials['username'])

user.authenticate(password=credentials['password'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reads the credentials file and uses &lt;a href="https://pypi.org/project/pycognito/" rel="noopener noreferrer"&gt;pycognito&lt;/a&gt; to authenticate the username and password.&lt;/p&gt;

&lt;h4&gt;
  
  
  Components setup &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Next, we initialize the camera and the bump sensor&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;camera = PiCamera()
camera.rotation = 180  # The camera is fitted on the device upside down, so we need to rotate it
GPIO.setmode(GPIO.BCM) # This means I'm using GPIO numbering instead of board numbering

GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Use GPIO 12
GPIO.add_event_detect(12, GPIO.RISING, callback=capture, bouncetime=3000) # add event listener to bump sensor
while True:
    time.sleep(10)  # Wait for input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above snippet assigns &lt;code&gt;capture()&lt;/code&gt; as a callback to the GPIO event.&lt;/p&gt;

&lt;h4&gt;
  
  
  Capture and upload image&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def capture(channel):    
    global user

    # Refresh token if expired
    user = Cognito(userPoolId, appClientId, id_token=user.id_token,refresh_token=user.refresh_token, access_token=user.access_token)

    # Set authorization header
    headers = {'authorization': user.id_token, 'content-type': 'image/jpg'}

    # Request presigned URL
    s3url = requests.get('https://api.mathem.io/cam-cart/s3/signedurl', headers=headers)
    contentDict = json.loads(s3url.content)
    signedUrl = contentDict["url"]

    # Take picture
    camera.capture('/tmp/capture.jpg')
    print('Captured image. Uploading...')

    # Upload
    with open('/tmp/capture.jpg', 'rb') as f:
        http_response = requests.put(signedUrl, data=f.read())
        print('Done!')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the S3 key will have the format &lt;code&gt;&amp;lt;userid&amp;gt;/&amp;lt;timestamp&amp;gt;.jpg&lt;/code&gt;. More on that later.&lt;/p&gt;

&lt;p&gt;To make this run on startup, add the following to &lt;code&gt;/etc/rc.local&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo python3 /home/pi/camera/camera.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Backend architecture &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Already having a 100% serverless architecture made it simple to hook this workflow into the mix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Farch-diagram.png%3Fv%3D1" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Farch-diagram.png%3Fv%3D1" alt="Arch diagram"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The prerequisite for uploading an image in a secure manner is to obtain a pre-signed URL that allows this. Having already authenticated the device, we can now call an API Gateway endpoint that authorizes the user before invoking the following code. For nodejs we use Jeremy Daly's &lt;a href="https://github.com/jeremydaly/lambda-api" rel="noopener noreferrer"&gt;lambda-api&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function get(req, res) {
  const jwt = req.headers.authorization;
  const claims = jwtDecode(jwt);
  // adding custom claim `userid` to the file name
  const filename = `${claims['custom:userid']}/${new Date().getTime()}.jpg`;
  const url = await s3.getSignedUrlPromise('putObject', {
    Bucket: process.env.Bucket,
    Key: filename,
    Expires: 10 // valid for 10 seconds
  });
  return res.status(200).send({ url });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need to act when new images get uploaded. We do this by triggering a lambda function &lt;code&gt;upload-trigger.js&lt;/code&gt; when new objects get created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UploadTrigger:
    Type: 'AWS::Serverless::Function'
    Properties:
        Handler: src/upload-trigger.handler
        Policies:
        - AmazonAPIGatewayInvokeFullAccess
        - Version: 2012-10-17
            Statement:
            - Sid: Statement1
                Effect: Allow
                Action:
                - 'textract:detectDocumentText'
                Resource: '*'
        - S3CrudPolicy:
            BucketName: !Sub ${AWS::AccountId}-${AWS::Region}-cam-cart
        Events:
        S3Event:
            Type: S3
            Properties:
            Bucket: !Ref Bucket
            Events: s3:ObjectCreated:*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Choosing AI service  &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;AWS offers two AI services that can recognize hand writing; &lt;a href="https://aws.amazon.com/rekognition/" rel="noopener noreferrer"&gt;Rekognition&lt;/a&gt; and &lt;a href="https://aws.amazon.com/textract/" rel="noopener noreferrer"&gt;Textract&lt;/a&gt;. At first I wasn't aware that Textract had this capabliity, but it turns out it was &lt;a href="https://aws.amazon.com/blogs/machine-learning/amazon-textract-recognizes-handwriting-and-adds-five-new-languages/" rel="noopener noreferrer"&gt;announced&lt;/a&gt; only about a month prior to the time of writing.&lt;/p&gt;

&lt;p&gt;Using the Rekognition API worked ok, but it wasn't great once the hand writing turned scribbly. Also, as you'll see later on, the camera is fitted a bit too close to the paper, so no matter how I adjust the lens, it will always be out of focus on that distance.&lt;/p&gt;

&lt;p&gt;Once I realized Textract had similar capabilities I did some side-by-side comparison and saw that using Textract takes this project a step away from being a fun proof-of-concept and closer to an actually useful device.&lt;/p&gt;

&lt;p&gt;Here follows an example of using the same blurry image in Rekogintion vs. Textract. Since MatHem is currently only offered in Swedish, so are the items on the list:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td colspan="2"&gt;Rekognition&lt;/td&gt;
   &lt;/tr&gt; 
   &lt;tr&gt;
      &lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Frekognition.png%3Fv%3D1"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Frekognition-zoom.png%3Fv%3D1"&gt;&lt;/td&gt;
  &lt;/tr&gt;
   &lt;tr&gt;
      &lt;td colspan="2"&gt;Pricing&lt;/td&gt;
  &lt;/tr&gt;
   &lt;tr&gt;
      &lt;td colspan="2"&gt;&lt;a href="https://aws.amazon.com/rekognition/pricing/" rel="noopener noreferrer"&gt;$0.001 per image after free tier&lt;/a&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td colspan="2"&gt;Textract&lt;/td&gt;
   &lt;/tr&gt; 
   &lt;tr&gt;
      &lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Ftextract.png%3Fv%3D1"&gt;&lt;/td&gt; 
      &lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Ftextract-zoom.png%3Fv%3D1"&gt;&lt;/td&gt; 
   &lt;/tr&gt;
&lt;tr&gt;
      &lt;td colspan="2"&gt;Pricing&lt;/td&gt;
  &lt;/tr&gt;
   &lt;tr&gt;
      &lt;td colspan="2"&gt;&lt;a href="https://aws.amazon.com/textract/pricing/" rel="noopener noreferrer"&gt;$0.0015 per document (image) after free tier&lt;/a&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here we can see that Textract nailed all four words while Rekognition really struggled. Now, our product search engine allows for typos, so the end result still often made sense with Rekogintion's results, but Textract was still superior.&lt;/p&gt;

&lt;p&gt;Also - Rekognition just gives a list of words and their coordinates in the image while Textract categorizes the matches into blocks, so for example "bar of soap" would be one search term using Textract, but three separate ones using Textract.&lt;/p&gt;

&lt;p&gt;Pricing is similar and although Textract is charged a bit higher, for our volumes it's neglectable.&lt;/p&gt;

&lt;p&gt;With all that in account and the fact that Textract is the better service name, the choice was simple.&lt;/p&gt;

&lt;h4&gt;
  
  
  upload-trigger.js
&lt;/h4&gt;

&lt;p&gt;When an image is uploaded, we trigger the following Lambda function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exports.handler = async function (event) {
  const s3Record = event.Records[0].s3;

  const detection = await textract
    .detectDocumentText({
      Document: {
        S3Object: { Bucket: s3Record.bucket.name, Name: s3Record.object.key }
      }
    })
    .promise();

  // Get user id based on the S3 object prefix
  const userId = s3Record.object.key.split('/')[0]; 

  for (const block of detection.Blocks.filter((p) =&amp;gt; p.BlockType === 'LINE')) {
    if (block.Confidence &amp;gt; 80) {
        // call internal APIs to search product and add to cart
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When calling the Textract API you can choose if you want to do it synchronously or in a asynchronous fire-and-forget manner. The latter means that you pass it an SNS topic ARN to which it sends a notification when the job is done. &lt;/p&gt;

&lt;p&gt;Here I went for the synchronous call since these images are small and processed fairly quickly (~2s) and I feel it favours the end-to-end duration.&lt;/p&gt;

&lt;p&gt;That's it for the code. The detected text is passed to internal APIs and the front end is updated using web sockets, but that's out of scope for this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the case &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The initial proof of concept wasn't presented in a very usability inviting way, so I had to wrap all the wiring up in a sturdy case:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/elFLHO7BnVk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The end result is this wall mounted beauty:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fwallmounted.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fwallmounted.jpg" alt="wall mounted device"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm a noob when it comes to 3D modelling, but it's really easy to get started with Tinkercad.com. &lt;/p&gt;

&lt;p&gt;The case consists of three items;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A case with screw holes for attaching the Raspberry Pi and bump sensor.&lt;/li&gt;
&lt;li&gt;An arm holding the camera. The arm is hollow to allow us to hide the flex cable. This arm would benefit from being longer to get less blurry images, but I was constrained to the 10cm length of the stock cable.&lt;/li&gt;
&lt;li&gt;A wall mount that the inner construction slides in to.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Inner case and arm
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fcase-and-arm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fcase-and-arm.png" alt="Case and arm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Printed and all wired up it looks like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fcase-inside.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fcase-inside.jpg" alt="Case inside"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These took ~12 hours to print&lt;/p&gt;
&lt;h4&gt;
  
  
  Outer casing / wall mount
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fwallmount.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmhlabs%2Ftech-blog%2Fmaster%2Fcart-cam%2Fwallmount.png" alt="Wall mount model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This took an additional 8-9 hours to print. &lt;/p&gt;

&lt;p&gt;The inner casing slides perfectly into the mount and the back of the mount secures the loose cables in place.&lt;/p&gt;
&lt;h2&gt;
  
  
  End result &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Here I have mounted it on the wall in the kitchen next to my Echo Show on which I've used Firefox to browse and log in to &lt;a href="//mathem.se"&gt;mathem.se&lt;/a&gt; for the sake of this demo. I also coated the device with a blue paper to add some friction to when I push the shopping list in.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/7P1CiuTNRKU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Please get in touch if you have ideas for improvement or other applications for this use case.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>mathem</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
