<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://docs.openfn.org/es/articles</id>
    <title>OpenFn Help Articles</title>
    <updated>2022-09-19T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://docs.openfn.org/es/articles"/>
    <subtitle>OpenFn/docs Blog</subtitle>
    <icon>https://docs.openfn.org/es/img/favicon.ico</icon>
    <rights>Copyright © 2026 Open Function Group, LLC.</rights>
    <entry>
        <title type="html"><![CDATA[Secure by design: a roadmap to secure authentication and authorization]]></title>
        <id>https://docs.openfn.org/es/articles/2022/09/19/auth-security</id>
        <link href="https://docs.openfn.org/es/articles/2022/09/19/auth-security"/>
        <updated>2022-09-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[In order to ensure that Global Goods and Digital Public Goods can enable impact]]></summary>
        <content type="html"><![CDATA[<p>In order to ensure that Global Goods and Digital Public Goods can enable impact
without doing any harm, it is crucial that their authentication and
authorization systems are secure from the outset.</p>
<p>We’ve put together a resource to help other Digital Public Goods and Global
Goods achieve just this. You can view and comment on the resource
<a href="https://docs.google.com/document/d/1QvOcOdk2iZCWnAVNiBbmhh-6Q2gapUhsMCUzJQNWqEU/edit?usp=sharing" target="_blank" rel="noopener noreferrer" class="">here</a>,
or read on to learn why.</p>
<p>Digital health technologies have a critical role to play in both improving the
delivery of health services <em>and</em> making health systems more robust and
adaptable. Whilst this has been the consensus for a while, various factors such
as bureaucracy, lack of capacity, authority/ownership, budget or simply lack of
prioritization have hindered the adoption and implementation of such
technologies.</p>
<p>However, the COVID-19 pandemic has brought digital health systems back to the
top of the agenda, having forced governments to adapt their health systems and
processes in a short amount of time and confronted them with the fragility and
inflexibility of some of their existing systems. This has led to an increase in
funding for both new and existing health technologies.</p>
<p>At the same time, the recent pandemic also saw an increase in data breaches and
data leaks in the healthcare sector. Whilst these are a serious matter in any
system, the nature of the healthcare technologies means that the data they are
storing or managing almost always includes Personally Identifiable Information
(PII) which, if leaked, can have dramatic consequences for those they identify.</p>
<p>The higher the number of technologies and quantity of data stored in these, the
higher the risk of vulnerabilities and negative impact of a data breach or data
leak. If Global Goods are to make the most of this renewed interest and increase
in funding for digital health technologies, it is crucial for them to keep
security as a top priority to ensure that they ‘do no harm’, a
core<a href="https://digitalpublicgoods.net/standard/" target="_blank" rel="noopener noreferrer" class=""> Digital Public Good</a> principle.</p>
<p>Several organizations have already put some thought into documenting security
best practices and standards. However, none situate these security decisions
within the wider context of a product development timeline or roadmap. This
requires individuals to regularly scan through long lists of security
recommendations to pick out and prioritize their implementation, increasing the
likelihood of an oversight or early-made product decisions which make it harder
to implement such security features down the line.</p>
<p>As product manager at Open Function Group, I myself have recently been reviewing
and analyzing numerous resources to identify and prioritize features required to
get to fully-secure authentication and authorization for our newest product
<a class="" href="https://docs.openfn.org/es/documentation#openfn-v2-lightning-">Lightning</a>–and make sure that nothing
falls through the cracks. I’ve also had the opportunity to gather insights from
Digital Public Good community forums (OpenMRS, DHIS2, OpenLMIS, etc) and speak
to other product managers and engineers from the Digital Public Goods community
to ask them about their learnings, and any tips they might have: Austin and
Morten from DHIS2, Dev and Biyeun from Dimagi.</p>
<p><strong>To more widely share these learnings, we have drafted this resource
documenting the specific security measures that should be considered at each
stage of product development _specifically with regards to authentication and
authorization. _</strong>Our hope is that this article will help other product managers
in the Global Good (GG) and Digital Public Good (DPG) communities consider these
features into their backlog/roadmap from the outset and make sure they are
following a ‘secure by design’ approach. To inform our analysis, we analyzed
learnings from the wider DPG community, as well as recommendations from other
security experts and international standards, including GovStack, OpenHIE,
OWASP, NIST, and more.</p>
<p><strong>The resource can be found
<a href="https://docs.google.com/document/d/1QvOcOdk2iZCWnAVNiBbmhh-6Q2gapUhsMCUzJQNWqEU/edit?usp=sharing" target="_blank" rel="noopener noreferrer" class="">here</a>.</strong></p>]]></content>
        <author>
            <name>Amber Rignell</name>
        </author>
        <category label="security" term="security"/>
        <category label="authentication" term="authentication"/>
        <category label="authorization" term="authorization"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Workflow Automation; Why do it yourself when a program can do it for you?]]></title>
        <id>https://docs.openfn.org/es/articles/2022/06/07/workflow-automation</id>
        <link href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation"/>
        <updated>2022-06-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Do you think twice when you get an automatic SMS notification because your]]></summary>
        <content type="html"><![CDATA[<p>Do you think twice when you get an automatic SMS notification because your
prescription is ready? (Neither do I.) This seamless experience is driven by
“workflow automation”, a key feature that OpenFn provides. The OpenFn
Integration Toolkit is a Digital Public Good (DPG) used by governments and NGOs
to boost efficiency through workflow automation. The automation that OpenFn
provides includes automatically sending SMSs, automating stock updates across
supply chain systems, tracking clinical visits, and helping plan vaccine
rollouts. We support our partners’ work by lifting the burden of manual data
transfers between platforms.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>nota</div><div class="admonitionContent_BuS1"><p>This article
<a href="https://www.linkedin.com/pulse/workflow-automation-why-do-yourself-when-program-can-you-openfn/?trackingId=TnWiYNtf5QP4GfAj6R4meQ%3D%3D" target="_blank" rel="noopener noreferrer" class="">originally appeared</a>
as a LinkedIn article from Open Function Group.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-workflow-automation">What is workflow automation?<a href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation#what-is-workflow-automation" class="hash-link" aria-label="Enlace directo al What is workflow automation?" title="Enlace directo al What is workflow automation?" translate="no">​</a></h2>
<p>Workflow automation is an approach to making the flow of tasks across platforms
run independently. A prescription SMS notification feels seamless, but it
probably requires complex automation across multiple systems like clinical
registries, pharmacy stock databases, and SMS gateways. Tools like OpenFn run
“behind the scenes” to perform calculations and transfer relevant data to/from
different systems autonomously, minimising the risk of human error and
ultimately saving time and money that an agency would spend on manual data
transfers. The goal is to preserve data integrity, uphold data security, and
enforce compliance with policies and data standards.</p>
<p>Since 2014, OpenFn has been at the forefront of workflow automation in the
social sector, providing a platform to help governments and NGOs focus on the
human aspect of their work rather than wasting time trying to exchange data
between systems or perform calculations manually.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="myagro">MyAgro<a href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation#myagro" class="hash-link" aria-label="Enlace directo al MyAgro" title="Enlace directo al MyAgro" translate="no">​</a></h3>
<p><a href="https://www.myagro.org/" target="_blank" rel="noopener noreferrer" class="">MyAgro</a>, an NGO helping West African farmers increase
their yield, uses OpenFn to power its SMS-based savings deposit system for
small-scale farmers. MyAgro distributes and tracks saving vouchers using the
<a href="https://www.dimagi.com/commcare/" target="_blank" rel="noopener noreferrer" class="">CommCare</a> app. Smallholder farmers purchase
pre-paid myAgro saving scratch cards and text the one-time code to secure the
voucher credit in their account. Once the amount has been added to their
balance, a confirmation notification is sent to the farmer. MyAgro works with
the farmers to reach their savings goal, which they then use to pay for seeds,
fertilisers, and training courses. OpenFn:</p>
<p align="center"><img src="https://user-images.githubusercontent.com/105651463/172341045-62a3eebd-98fb-4189-987f-06bbdb03ac30.png"></p>
<ol>
<li class="">Syncs voucher codes from CommCare to Salesforce so the relevant offices can
track who purchased the saving vouchers.</li>
<li class="">Matches incoming SMSs against the database of vouchers to deposit correct
funds into the individual account.</li>
<li class="">Allocates savings to farmers’ accounts in Salesforce and updates the account
status.</li>
<li class="">Sends a confirmation SMS with updated account balances to farmers, so they
are aware of their credit.</li>
</ol>
<p>OpenFn helps myAgro reach more farmers with less administrative overhead by
automating these key steps. MyAgro calculated that even at their 2014 levels of
scale, OpenFn saved them 260+ hours per year and increased customer confidence
as they worked to serve poor farmers.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sinapis">Sinapis<a href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation#sinapis" class="hash-link" aria-label="Enlace directo al Sinapis" title="Enlace directo al Sinapis" translate="no">​</a></h3>
<p><a href="https://sinapis.org/" target="_blank" rel="noopener noreferrer" class="">Sinapis</a>, an NGO that empowers entrepreneurs through
training courses, uses OpenFn to automate its payment collection process. Every
time a client’s new
<a href="https://www.vodafone.com/about-vodafone/what-we-do/consumer-products-and-services/m-pesa/globalmerchants" target="_blank" rel="noopener noreferrer" class="">M-Pesa</a>
payment is registered in <a href="https://kopokopo.co.ke/" target="_blank" rel="noopener noreferrer" class="">Kopo Kopo</a>, OpenFn:</p>
<p align="center"><img src="https://user-images.githubusercontent.com/105651463/172341175-5505b647-93dc-44af-9143-00ebdd0b5bae.png"></p>
<ol>
<li class="">Automatically matches incoming payments with existing Sinapis accounts.</li>
<li class="">Logs new payments and updated payment status in Salesforce to track
entrepreneurs’ charges.</li>
<li class="">Updates account balances to help staff monitor outstanding balances and
overdue fees.</li>
</ol>
<p>With OpenFn handling the routine data processing, Sinapis has more time to
support its entrepreneurs. Precious staff time can be spent delivering training,
the essential face-to-face coaching that transforms their clients’ fledgling
enterprises. Their team does not have to worry about the accuracy of payments
and account balances; the OpenFn implementation replaces error-prone manual data
entry with a systematised, intelligible, fully automated process.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="dial">DIAL<a href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation#dial" class="hash-link" aria-label="Enlace directo al DIAL" title="Enlace directo al DIAL" translate="no">​</a></h3>
<p>The <a href="https://digitalimpactalliance.org/" target="_blank" rel="noopener noreferrer" class="">Digital Impact Alliance (DIAL)</a>, an
organisation that works to overcome digital development barriers, wanted to
prototype a “building-blocks based approach” for their upcoming GovStack.global
project. They configured an HR workflow solution that connects mobile data
collection apps, databases, and payment apps using OpenFn. Every time an
organisation registers new workers via
<a href="https://docs.opendatakit.org/collect-intro/" target="_blank" rel="noopener noreferrer" class="">ODK Collect</a>, OpenFn:</p>
<p align="center"><img src="https://user-images.githubusercontent.com/105651463/172341214-06a1e74b-c1e3-45e4-83c8-4a60af5a9d2d.png"></p>
<ol>
<li class="">Automatically registers new employees in an <a href="https://www.ihris.org/" target="_blank" rel="noopener noreferrer" class="">iHRIS</a>
database to oversee all new hires.</li>
<li class="">Makes payments to workers via
<a href="https://www.google.com/aclk?sa=l&amp;ai=DChcSEwicha-ltsnnAhUB1N4KHY8hDEoYABAAGgJ3Yg&amp;sig=AOD64_27g900G2RtoQsW0km9iT4Oq262ag&amp;q=&amp;ved=2ahUKEwj8wKeltsnnAhVSxYUKHVcrBbUQ0Qx6BAgMEAE&amp;adurl=" target="_blank" rel="noopener noreferrer" class="">Mifos</a>,
ensuring all wage transfers are direct.</li>
</ol>
<p>While it’s not in use at scale, this prototype is a powerful illustration of how
automation enables scalable, component-based solutions to become more than the
sum of their parts. The processes that DIAL implement with OpenFn help unlock
the true potential of these powerful underlying DPGs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-the-big-picture-here">What’s the big picture here?<a href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation#whats-the-big-picture-here" class="hash-link" aria-label="Enlace directo al What’s the big picture here?" title="Enlace directo al What’s the big picture here?" translate="no">​</a></h2>
<p>OpenFn’s goal is to increase the efficiency and effectiveness of the social
sector. Why should health and humanitarian organisations waste precious
resources manually moving data across systems when workflow automation can do it
more safely and quickly? We want to ensure employees focus on using data to do
their work, not transferring data between systems and running rote calculations.</p>
<p>Saving organisations time and money is part of the picture, but we also deliver
better program outcomes through efficiency and effectiveness. Suppose OpenFn
securely automates the complex digital processes critical health and
humanitarian interventions require. Reducing error rates and security
vulnerabilities and getting higher quality data into the hands of a doctor
faster actually saves lives. Responsible automation leads to better health and
humanitarian outcomes, enabling larger-scale intervention delivery via
efficiency gains and improving service quality by getting accurate data into the
hands of patients, clinicians, and policymakers when they need it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="call-to-action">Call to Action<a href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation#call-to-action" class="hash-link" aria-label="Enlace directo al Call to Action" title="Enlace directo al Call to Action" translate="no">​</a></h2>
<p>Think about your own experiences; have you found yourself in a situation where
manual data processing—some rote, mindless task—has gotten in the way of doing
the more exciting parts of your job? Whether you are in the health and
humanitarian space or just want to make your life easier, OpenFn is here for
you. Our open-source toolkit is available for everyone to automate tasks such as
sending SMS and email notifications or automating data cleaning and formatting
for faster reporting.</p>
<p>Head to <a href="http://docs.openfn.org/" target="_blank" rel="noopener noreferrer" class="">docs.openfn.org</a> to learn more about
enterprise-grade automations with our DPG and check out
<a href="http://community.openfn.org/" target="_blank" rel="noopener noreferrer" class="">community.openfn.org</a> to connect and collaborate
with like-minded social-sector integrators and OpenFn staff. We hope to see you
soon!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-open-function-group">About Open Function Group<a href="https://docs.openfn.org/es/articles/2022/06/07/workflow-automation#about-open-function-group" class="hash-link" aria-label="Enlace directo al About Open Function Group" title="Enlace directo al About Open Function Group" translate="no">​</a></h2>
<p>Open Function Group (OFG) is a global team of integration specialists and
developers of the OpenFn Integration Toolkit. As a
<a href="https://digitalpublicgoods.net/" target="_blank" rel="noopener noreferrer" class="">DPG</a>, OpenFn’s core technology is free and
open-source. If you want more information about the Toolkit or to sign up for a
free plan on our associated SaaS application, visit <a href="https://www.openfn.org/" target="_blank" rel="noopener noreferrer" class="">https://www.openfn.org/</a>.</p>]]></content>
        <author>
            <name>Alexa de Vegvar</name>
        </author>
        <category label="automation" term="automation"/>
        <category label="solution" term="solution"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[How learning JavaScript helps me better understand OpenFn jobs]]></title>
        <id>https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs</id>
        <link href="https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs"/>
        <updated>2021-10-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[OpenFn automation happens via jobs which]]></summary>
        <content type="html"><![CDATA[<p>OpenFn automation happens via <a class="" href="https://docs.openfn.org/es/documentation/build/workflows">jobs</a> which
define specific steps ("operations") that OpenFn should perform. They're written
in a basic scripting language that runs on top of (and has full access to)
<strong>Javascript</strong>. A basic understanding of Javascript will take your job writing
on OpenFn to the next level. To improve my limited knowledge of JavaScript, I
have been taking Codecademy's
<a href="https://www.codecademy.com/learn/introduction-to-javascript" target="_blank" rel="noopener noreferrer" class="">Introduction to JavaScript Course</a>.</p>
<p>In this post we'll discuss:</p>
<ol>
<li class="">Arrow functions</li>
<li class="">Falsy values</li>
<li class="">Other short-hand notation</li>
</ol>
<p>Keep reading for all the important learnings I have taken from just the first
four modules!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-that-little--arrow">What is that little <code>=&gt;</code> arrow?<a href="https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs#what-is-that-little--arrow" class="hash-link" aria-label="Enlace directo al what-is-that-little--arrow" title="Enlace directo al what-is-that-little--arrow" translate="no">​</a></h2>
<p>Javascript allows us to create <em>functions</em> which take <em>arguments</em> and <em>do stuff</em>
with them. Most times, function declaration in Javascript looks like this:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">getAge</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">dateString</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">dateString</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> today </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Date</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> birthDate </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Date</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">dateString</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">var</span><span class="token plain"> age </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> today</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">getFullYear</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> birthDate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">getFullYear</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">var</span><span class="token plain"> m </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> today</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">getMonth</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> birthDate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">getMonth</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">m </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">m </span><span class="token operator" style="color:rgb(137, 221, 255)">===</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> today</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">getDate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> birthDate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">getDate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    age</span><span class="token operator" style="color:rgb(137, 221, 255)">--</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> age</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>This was simple enough for me to follow. We are declaring a function,
<code>getAge()</code>, which has the steps to calculate a person's age and can be easily
reused throughout the job.</p>
<p>But other times, function writing looks like this:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">getAge</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">dateString</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">dateString</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// and so on...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>In OpenFn, you're often writing unnamed functions that take <code>state</code> as their
only argument and you use them right away. See how concise writing and using a
function can be with the arrow:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">upsert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'tbl_study'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'study_id'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(130, 170, 255)">study_id</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">studyIDMap</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">formType</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>These <strong><code>arrow functions</code></strong> confused me when reading through jobs. I've learned
that, for the most part, they can be thought of as short-hand notation which
removes the need to type out <code>function</code> for every function declaration.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>consejo</div><div class="admonitionContent_BuS1"><p>MDN has a great explanation
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions" target="_blank" rel="noopener noreferrer" class="">here</a>.
Tl;dr: "An arrow function expression is a compact alternative to a traditional
function expression, but is limited and can't be used in all situations."</p></div></div>
<p>Further, we can make the code even more concise by also removing the
parenthesis, curly braces, and the return keyword when certain criteria are met.
In the code above, the parenthesis are omitted because there are no parameters,
and the curly braces and return keyword are omitted because the function body is
composed of a single-line block. As you can see, there are a variety of ways to
write functions and this course is helping me better recognize them. Some key
terminology here is: <code>function expression</code>, <code>arrow function</code>,
<code>concise function</code>, <code>anonymous function</code>, and <code>implicit returns</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="falsy-values">Falsy values<a href="https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs#falsy-values" class="hash-link" aria-label="Enlace directo al Falsy values" title="Enlace directo al Falsy values" translate="no">​</a></h2>
<p>During the <a class="" href="https://docs.openfn.org/es/adaptors/salesforce#mapping-and-design-considerations">mapping</a>
phase of integration design, we often discuss how each answer choice for
<strong>picklist</strong> values should map from the source system to the destination system.
Sometimes the mapping is simple but other times, there is an extensive list of
possible choices that can be found in the
<a class="" href="https://docs.openfn.org/es/documentation/legacy/getting-started/terminology#message">message</a> and not all
are relevant to the destination system. Then the question is, <strong>how should the
job handle values which are not explicitly mapped?</strong></p>
<p>Sometimes we hear clients say to "ignore" those values. <strong>But what does it
really mean to "ignore" a value?</strong> Should we set it to <code>0</code>? An empty string? How
about <code>null</code>, <code>undefined</code> or <code>NaN</code>? In Javascript these are all <code>falsy</code> values.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>consejo</div><div class="admonitionContent_BuS1"><p>MDN is great for falsy values too! Check their explanation out
<a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy" target="_blank" rel="noopener noreferrer" class="">here</a>.</p></div></div>
<p>Determining what is really meant here is important and the value selected has
different implications depending on the system.</p>
<p>Take this sample mapping for different districts in Conakry. The value left of
the colon is from the source system and the value to the right is for the
destination system.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> districtMapping </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">Ratoma</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'RT'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">Kaloum</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'KL'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">Dixinn</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'DX'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">Matam</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'MA'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">Matoto</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'MT'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">Other</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token keyword nil" style="font-style:italic">undefined</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>If the destination system is Salesforce, this mapping would not upload "Other"
to Salesforce. However if "Other" mapped to an empty string instead, this would
upload the empty string to Salesforce. <strong>This distinction is especially
important in cases where we are overwriting existing data.</strong> For instance, if a
student previously lived in Ratoma and then moved to an unknown district marked
as "Other", <code>undefined</code> <em>would not</em> update the student's district in Salesforce
but the empty string would. Both of these are falsy values but have different
meanings in Salesforce.</p>
<p>How about if the message includes a value for a district that <em>is not</em> in the
mapping? Such as "New York". Should the job default to <code>undefined</code>? Or <code>null</code>?</p>
<p>These questions are just a few examples of how understanding <code>falsy</code> values in
Javascript can make it easier to implement the best mapping for the real-world
use case.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="short-circuit-evaluation-template-literals-and-all-the-short-hand-that-used-to-confuse-me">Short-circuit evaluation, template literals, and all the short hand that used to confuse me<a href="https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs#short-circuit-evaluation-template-literals-and-all-the-short-hand-that-used-to-confuse-me" class="hash-link" aria-label="Enlace directo al Short-circuit evaluation, template literals, and all the short hand that used to confuse me" title="Enlace directo al Short-circuit evaluation, template literals, and all the short hand that used to confuse me" translate="no">​</a></h2>
<p><strong>Short-circuit evaluation</strong></p>
<p>When we are syncing forms to a database, we sometimes expect different versions
of the same form with fields present one version but not the other. One way to
mitigate this discrepancy is by submitting a dummy value for the field whenever
it is missing in the message. I just learned the fastest way to add this code to
my jobs! For example, the code below will ensure that <code>household_id</code> always has
a value in the destination system: if <code>survey_info/household_id</code> is present in
the message this will be the assigned value, otherwise it will assign the dummy
value <code>state.data.body._id</code>.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token literal-property property">household_id</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">body</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(195, 232, 141)">'survey_info/household_id'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">||</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">body</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><br></div></code></pre></div></div>
<p><strong>Template literals</strong></p>
<p>Like the arrow discussed above, the "dollar sign" was another symbol that often
confused me. But template literals are actually very straightforward. They
increase the readability of the code and make it easier to see what the
resulting string will be. Wrap your template in the backtick (`) symbols and
then each variable in <code>${}</code>.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> sign </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'$'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token console class-name" style="color:rgb(255, 203, 107)">console</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">log</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token template-string string" style="color:rgb(195, 232, 141)">The </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token template-string interpolation">sign</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token template-string string" style="color:rgb(195, 232, 141)"> isn't so confusing!</span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="higher-order-functions--iterators">Higher-order functions &amp; iterators<a href="https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs#higher-order-functions--iterators" class="hash-link" aria-label="Enlace directo al Higher-order functions &amp; iterators" title="Enlace directo al Higher-order functions &amp; iterators" translate="no">​</a></h2>
<p>The most challenging module in the course covered <strong>higher-order functions</strong>.
These are defined as <strong>functions that accept other functions as arguments and/or
return functions as output.</strong> But why are these important and how are they used
in OpenFn jobs? It turns out we use them quite alot! The code below is an
example from an existing integration with the field names replaced.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> participantsToUpdate </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">json</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">data</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">idList</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">includes</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> participantsToCreate </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">json</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token parameter">data</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">idList</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">includes</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>This code is using a built-in JavaScript method that <strong>helps us iterate on
arrays to manipulate elements and return values.</strong> The <code>.filter()</code> method is
being used to return a new array after filtering out certain elements from the
original array. We have declared two variables to store the new arrays:
<code>participantsToUpdate</code> and <code>participantsToCreate</code>. Participants whose ids are
existing in the destination system (or who are in the <code>idList</code> array) are added
to <code>participantsToUpdate</code>, and any remaining participants are added to
<code>participantsToCreate</code>. This filtering helped us perform the correct operations
on each participant type. For instance, later in the job, we use the filtered
arrays to only overwrite a participant's <code>reportNumber</code> field if it is a new
participant. <code>.filter()</code> is just one of many higher-order functions that power
OpenFn jobs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="objects--job-mappings">Objects &amp; job mappings<a href="https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs#objects--job-mappings" class="hash-link" aria-label="Enlace directo al Objects &amp; job mappings" title="Enlace directo al Objects &amp; job mappings" translate="no">​</a></h2>
<p>The final lesson was on objects and key-value pairs--something used in almost
every OpenFn job! The result of the design and mapping phase of requirements
gathering is almost always a mapping document which includes key-value pairs in
plain English. These specifications are then translated to JavaScript via
<strong><em>objects</em></strong>. The code below is a snippet of an object which captures the
key-value pairs for states in the US.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> stateMapping </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   </span><span class="token constant" style="color:rgb(130, 170, 255)">AK</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'ALASKA'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   </span><span class="token constant" style="color:rgb(130, 170, 255)">AZ</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'ARIZONA'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   </span><span class="token constant" style="color:rgb(130, 170, 255)">AR</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'ARKANSAS'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   </span><span class="token constant" style="color:rgb(130, 170, 255)">CA</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'CALIFORNIA'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><br></div></code></pre></div></div>
<p>The value left of the colon is how the state is represented in the source
system, and the value right of the colon represents how OpenFn will send the
data to the destination system. This mapping process is key to integration
design. Learn more about mappings
<a class="" href="https://docs.openfn.org/es/adaptors/salesforce#mapping-and-design-considerations">here</a>.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>consejo</div><div class="admonitionContent_BuS1"><p>MDN has more details on objects
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" target="_blank" rel="noopener noreferrer" class="">here</a>.
Tl;dr: "The Object class represents one of JavaScript's data types. It is used
to store various keyed collections and more complex entities."</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps">Next steps<a href="https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs#next-steps" class="hash-link" aria-label="Enlace directo al Next steps" title="Enlace directo al Next steps" translate="no">​</a></h2>
<p>I'm well on my way to becoming a better job reader and writer. Here are some
next steps:</p>
<ol>
<li class="">
<p>Understand <code>fn(state)</code> and how <code>state</code> can be manipulated in OpenFn jobs.</p>
</li>
<li class="">
<p>Explore what's available on the JavaScript docs
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" rel="noopener noreferrer" class="">site</a>.</p>
</li>
<li class="">
<p>Sign up for the next level JavaScript course.</p>
</li>
</ol>]]></content>
        <author>
            <name>Aicha Diallo</name>
        </author>
        <category label="javascript" term="javascript"/>
        <category label="tips" term="tips"/>
        <category label="jobs" term="jobs"/>
        <category label="learning" term="learning"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Testing a React app, the blurred line between Unit, integration and E2E]]></title>
        <id>https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound</id>
        <link href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound"/>
        <updated>2021-10-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Have you ever struggled to layout the strategy for testing your React App? Well,]]></summary>
        <content type="html"><![CDATA[<p>Have you ever struggled to layout the strategy for testing your React App? Well,
you are not alone! Here a few hints from the lessons I learned during my
experience testing a
<a href="https://reactjs.org/" target="_blank" rel="noopener noreferrer" class="">React</a>/<a href="https://redux.js.org/" target="_blank" rel="noopener noreferrer" class="">Redux</a> app with a
<a href="https://www.phoenixframework.org/" target="_blank" rel="noopener noreferrer" class="">Phoenix</a>/<a href="https://elixir-lang.org/" target="_blank" rel="noopener noreferrer" class="">Elixir</a>
backend.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-blurred-line">The Blurred Line<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#the-blurred-line" class="hash-link" aria-label="Enlace directo al The Blurred Line" title="Enlace directo al The Blurred Line" translate="no">​</a></h2>
<p>Because a React app is built on
<a href="https://reactjs.org/docs/react-component.html" target="_blank" rel="noopener noreferrer" class="">components</a>, the basic UI units,
it is natural to think and organise your tests around components! And so unit
testing, in this case, would refer to "component testing", which may be
confusing at times, especially when the concept of unit testing is again applied
to testing functions such as Redux <code>reducers</code> and <code>action creators</code> or any other
JavaScript function in your application.</p>
<p>The other challenge that I often faced was whether to write tests for each
component in isolation or write a test for a feature that encapsulates a set of
related components. The later would be equivalent to writing what I would call
"integration tests".</p>
<p>Finally, one would say "well then you could have just written the tests in a way
that resemble the way the application is used"! This approach is commonly
recommended in the React community, but it quickly becomes really complex to
maintain the layers of separation between <strong><em>unit tests</em></strong>, <strong><em>integration
tests</em></strong> and <strong><em>end-to-end tests</em></strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-did-i-learn">What did I learn?<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#what-did-i-learn" class="hash-link" aria-label="Enlace directo al What did I learn?" title="Enlace directo al What did I learn?" translate="no">​</a></h2>
<p>Given a React/Redux application, here is how I would organise my testing
strategy:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="unit-tests">Unit Tests<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#unit-tests" class="hash-link" aria-label="Enlace directo al Unit Tests" title="Enlace directo al Unit Tests" translate="no">​</a></h3>
<ul>
<li class="">
<p>In a React app, <strong>unit tests</strong> will largely apply to testing "helper
functions" and not to testing components, as justified in the next section.
Helper functions, in this case, would refer to functions that live outside the
components and are neither Redux action creators nor reducers. These functions
can be used inside components, action creators, reducers or other parts of
your application.</p>
</li>
<li class="">
<p>Writing unit tests for "helper functions" would ensure their signatures and
expected outputs are protected against regressions. This would also ensure
their use across components or other functions is consistent and as expected.</p>
</li>
<li class="">
<p>Where possible, each "helper function" must have its own <code>unit test</code>.</p>
</li>
<li class="">
<p>An example of a unit test would like:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">require</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'../../js/sum'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'adds 1 + 2 to equal 3'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">sum</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">toBe</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">3</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
</li>
<li class="">
<p>Write a <em>thousand</em> of these.</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="integration-tests">Integration Tests<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#integration-tests" class="hash-link" aria-label="Enlace directo al Integration Tests" title="Enlace directo al Integration Tests" translate="no">​</a></h3>
<ul>
<li class="">
<p>In the context of a React/Redux app, component tests can be thought of as
<strong>integration tests</strong>. This is because React components are built around
features such as <code>&lt;Signup /&gt;</code>, <code>&lt;Search /&gt;</code>, etc. So one React component can
be a mix of other components to achieve a UI feature set.</p>
</li>
<li class="">
<p>To test a component, write an <strong>integration test</strong> that covers the use of a
given component for a given UI feature.</p>
</li>
<li class="">
<p>If a component being tested dispatches a Redux <code>action</code>, this is the right
place to test those actions and their effect on the UI.</p>
</li>
<li class="">
<p>Pay attention to the concept of <em>feature isolation</em> vs <em>component isolation</em>
as it will help you write better integration tests and also easily mock
component contexts.</p>
</li>
<li class="">
<p>A classic example of <strong>feature isolation</strong> is when you have a <code>&lt;UserList /&gt;</code>
component which displays a list of users and has a <code>&lt;button /&gt;</code> to add a new
user. Writing a test for <code>&lt;UserList /&gt;</code> would be equivalent to testing a
feature.</p>
</li>
<li class="">
<p>In this example, one would be tempted to test the action of clicking on the
<code>&lt;AddUserButton /&gt;</code> and further test the <code>&lt;NewUser /&gt;</code> form... nope! This is
where we draw the line! Only test that the <code>&lt;UserList /&gt;</code> renders the mock
<code>users</code> in the list and that the <code>&lt;AddUserButton /&gt;</code> is present/enabled. The
<code>&lt;UserList /&gt;</code> feature ends there, otherwise you will be sliding into
<strong>End-to-End</strong> testing :)! The <code>&lt;User /&gt;</code> component, although it is invoked by
<code>&lt;UserList /&gt;</code> component, it is isolated enough to be tested in its own
integration test.</p>
</li>
<li class="">
<p>Testing components this way would make "context mocking" easier for
components.</p>
</li>
<li class="">
<p>Another important benefit for isolating testing context, as in the example
above, is that it will be easier to mock the <code>redux actions</code> and/or api calls
using tools such as <a href="https://jestjs.io/" target="_blank" rel="noopener noreferrer" class="">Jest</a> and
<a href="https://mswjs.io/" target="_blank" rel="noopener noreferrer" class="">Mock Service Worker</a> (or "msw") as explained in the
<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#choosing-testing-tools" class="">Choosing Tools</a> section.</p>
</li>
<li class="">
<p>The value of writing integration tests for components, in this way, ensures
that a given component renders the UI consistently, given all possible
combinations of contexts and interactions. This will also allow you to ensure
redux actions invoked by the component are called as expected and with the
correct arguments.</p>
</li>
<li class="">
<p>An example component integration test would look like:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// ....other imports</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword module" style="font-style:italic">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token imports"> setupServer </span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token keyword module" style="font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'msw/node'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Tell jest to mock the module</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">jest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">mock</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'../js/actions/UserActions'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">jest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">requireActual</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'../js/actions/UserActions'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">saveUser</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> jest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">fn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword module" style="font-style:italic">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token imports"> saveUser </span><span class="token imports keyword module" style="font-style:italic">as</span><span class="token imports"> mockSaveUser </span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token keyword module" style="font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'../js/actions/UserActions'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> server </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">setupServer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">handlers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Enable API mocking before tests</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">beforeAll</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> server</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">listen</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Reset any runtime handlers we may add during the tests</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">afterEach</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> server</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">resetHandlers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Disable API mocking after the tests are done.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">afterAll</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> server</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">close</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">beforeEach</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    jest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">clearAllMocks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">describe</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'&lt;AddUser/&gt;'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'create new user'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain">getByPlaceholderText</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">getByText</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">render</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token maybe-class-name">User</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">defaultProps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    userEvent</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">getByPlaceholderText</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'First Name'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'John'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    userEvent</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">getByPlaceholderText</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Last Name'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Doe'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    userEvent</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">click</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">getByText</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Save'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">mockSaveUser</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">toHaveBeenCalledTimes</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">mockSaveUser</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">toHaveBeenCalledWith</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token literal-property property">firstName</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'John'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token literal-property property">lastName</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Doe'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div></code></pre></div></div>
</li>
<li class="">
<p>Write a <em>good couple</em> of these.</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="end-to-end-e2e-tests">End-to-End (e2e) Tests<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#end-to-end-e2e-tests" class="hash-link" aria-label="Enlace directo al End-to-End (e2e) Tests" title="Enlace directo al End-to-End (e2e) Tests" translate="no">​</a></h3>
<ul>
<li class="">
<p>In a React/Redux App, this would mean testing a <em>full flow</em> of a given
feature. <strong>end-to-end tests</strong> would require launching the entire application,
including the backend, to run a given test.</p>
</li>
<li class="">
<p>Note that <strong>end-to-end tests</strong> are different from <strong>integration tests</strong> as
they require the entire App to run and render the full flow to your component
under test.</p>
</li>
<li class="">
<p>With this understanding, consider writing <strong>e2e</strong> tests <em>per workflow</em>.</p>
</li>
<li class="">
<p>An example <strong>e2e workflow</strong> is the "Viewing and adding users" workflow.</p>
</li>
<li class="">
<p>The e2e test for this workflow would require a test runner to launch the app,
log-in, navigate to the users list page, verify existing users are in the
list, click on the Add New User button and confirm that the new user has been
added to the list.</p>
</li>
<li class="">
<p>As you can see, e2e tests have more dependencies and require that you setup
your testing environment in way that closely simulates your real application
usage.</p>
</li>
<li class="">
<p>An example e2e test for a React/Redux App with a Phoenix/Elixir backend, using
<code>Hound</code> as a test runner looks like this:</p>
</li>
</ul>
<div class="language-elixir codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-elixir codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">defmodule OpenFn.UsersTest do</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  setup do</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    user = insert(:user, confirmed_at: DateTime.utc_now())</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    {:ok, user: user }</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  end</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  @tag :integration</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  test "Sign-up.", %{user: user} do</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    navigate_to("/sign-up")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    form = find_element(:id, "sign_up_form")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    form</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; find_within_element(:id, "first-name")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; fill_field("John")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    form</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; find_within_element(:id, "last-name")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; fill_field("Doe")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    form</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; find_within_element(:id, "email")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; fill_field("doe@gmail.com")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    form</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; find_within_element(:id, "save-button")</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    |&gt; click</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    assert page_title() === ~s/Welcome to my page/</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">end</span><br></div></code></pre></div></div>
<ul>
<li class="">Write <em>only a few</em> of these.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="choosing-testing-tools">Choosing Testing Tools<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#choosing-testing-tools" class="hash-link" aria-label="Enlace directo al Choosing Testing Tools" title="Enlace directo al Choosing Testing Tools" translate="no">​</a></h2>
<p>There are many testing tools out there, but for a typical <em>React/Redux</em> app the
following tools should help you accomplish the above tasks:</p>
<ol>
<li class=""><a href="https://jestjs.io/docs/getting-started" target="_blank" rel="noopener noreferrer" class="">Jest</a> as test runner for <strong>unit</strong>
and <strong>integration</strong> tests.</li>
<li class=""><a href="https://testing-library.com/docs/" target="_blank" rel="noopener noreferrer" class="">React Testing Library</a> used along with
Jest as an "assertion library" for integration tests.</li>
<li class=""><a href="https://mswjs.io/docs/getting-started/install" target="_blank" rel="noopener noreferrer" class="">MSW</a> used along with Jest as
a REST API mocking library.</li>
<li class=""><a href="https://hexdocs.pm/hound/readme.html" target="_blank" rel="noopener noreferrer" class="">Hound</a> as a test runner for <strong>e2e</strong>
tests in Elixir/Phoenix apps.
<a href="https://developers.google.com/web/tools/puppeteer" target="_blank" rel="noopener noreferrer" class="">Puppeteer</a> can also be
used along with Jest.<!-- -->
<ul>
<li class="">If Puppeteer is used, it will work seamlessly with Jest but only in
headless browser mode. It also reduces on tech stack since you will only
need Jest.</li>
<li class="">Hound gives you the ability to run your <strong>e2e</strong> tests both in <code>headless</code>
and <code>browser</code> mode.</li>
</ul>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="final-thoughts-and-next-steps">Final thoughts and next steps<a href="https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound#final-thoughts-and-next-steps" class="hash-link" aria-label="Enlace directo al Final thoughts and next steps" title="Enlace directo al Final thoughts and next steps" translate="no">​</a></h2>
<p>Testing a React App can be really hard, but worth it! By building
<code>Aria-accessible</code> components ahead of time, you save yourself 💰 and good
health! A few more hints would be:</p>
<ul>
<li class="">Build clean, isolated and plugable components for your better testing
experience. "God components" can be a <em>pain</em> to test!</li>
<li class="">Using test runners such as Jest, that use <em>emulated</em> web browsers (e.g.,
<code>jsdom</code>) rather than a real browser come with their own challenges in
rendering and traversing complex DOM trees, especially if you are using UI
libraries such as <a href="https://mui.com/" target="_blank" rel="noopener noreferrer" class="">MUI</a>.</li>
<li class="">If using Jest for <strong>integration tests</strong>, I would recommend the components
under test have as few dependencies as possible to avoid the complexity
involved in mocking http requests and waiting for asynchronous DOM rendering.</li>
</ul>
<p>What would I do differently? Here are my few thoughts:</p>
<ul>
<li class="">Organise and document detailed test cases for manual "click testing".</li>
<li class="">Identify and clearly isolate components for <strong>integration tests</strong>.</li>
<li class="">Do not <em>delete</em> slow tests, instead re-write your component to be faster.
Respect the linter's advice, always!</li>
<li class="">Use a commonly supported frontend testing stack such as Jest, Msw, or
Puppeteer for easier setup and community support.</li>
<li class="">Setup your test runner to use a test database. It helps, especially during
<strong>e2e</strong> testing.</li>
<li class="">Always write <strong><em>all the three types</em></strong> of tests, whenever applicable.</li>
</ul>
<p>All this stuff for what?</p>
<ul>
<li class="">Well because regressions can be much more expensive to your organisation!
Writing high quality and thoroughly tested software will save you 💰 and help
guarantee a maintainable codebase and a progressive software application.</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Still looking for the legend's advice?</div><div class="admonitionContent_BuS1"><p>Gotcha, here you go...</p><ol>
<li class="">Swallow your pride and be humble: <em>always</em> do <strong>manual testing!</strong></li>
<li class="">Click test your way through the <strong>manual test cases</strong> for every new
deployment, catching regressions.</li>
<li class=""><em>Lock in</em> your fixes and new features as <strong>unit tests</strong>, <strong>integration
tests</strong>, and <strong>end-to-end tests</strong>.</li>
</ol></div></div>
<p>Happy testing,</p>
<p>Chaiwa</p>]]></content>
        <author>
            <name>Chaiwa Berian</name>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
        <category label="testing" term="testing"/>
        <category label="browser-testing" term="browser-testing"/>
        <category label="react" term="react"/>
        <category label="elixir" term="elixir"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Moving from Webpack to esbuild on Phoenix]]></title>
        <id>https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1</id>
        <link href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1"/>
        <updated>2021-10-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[We're very happy users of Elixir and Phoenix at OpenFn, given that we've been]]></summary>
        <content type="html"><![CDATA[<p>We're very happy users of Elixir and Phoenix at OpenFn, given that we've been
using it continuously for about 6 years - upgrades and all. Our front-end
toolchain, albeit far from out of date (Webpack <code>5.52.1</code> today) has left some
room for improvement.</p>
<blockquote>
<p>This is a post written on what we call 'Slow Fridays', where we explore and
think about stuff we're curious about. The deal is some artifact at the end of
the day. So while this post is not complete - I believe part two is warranted.</p>
</blockquote>
<p>Phoenix 1.6 started to include esbuild by <a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#ref1" class="">default</a>. The main reasons
cited were the amount of support the team had to continuously give for a
toolchain that although necessary is not under the control of the framework
teams control.</p>
<p>Back in the day, Phoenix used brunch to do front end builds - despite not being
the most common bundler at the time. The objective I assume was to provide
something that is easy to get going with and lowers the barrier to entry for
Phoenix. We actually switched brunch out for webpack before webpack got into
Phoenix in order to more easily use new libraries and newer ES syntax.</p>
<p>It wasn't without its challenges, and still is. Webpack is wonderfully
powerful - but it does do <em>a lot</em>, and it's really easy to stumble onto plugins
that are either order dependent or mutually exclusive.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="so-whats-this-about-esbuild">So what's this about esbuild<a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#so-whats-this-about-esbuild" class="hash-link" aria-label="Enlace directo al So what's this about esbuild" title="Enlace directo al So what's this about esbuild" translate="no">​</a></h2>
<p>It's a bundler/compiler for JS/TS projects that is written in Go, along with
being faster by the simple fact it's natively compiled it also leverages
concurrency. To compare <em>why</em> esbuild is faster than NodeJS on face value is
unfair in my opinion. Although having similar objectives, they are wildly
different in implementation and in features.</p>
<p>So <strong>speed</strong> is the big selling point. But thinking back to other bundlers, what
made us switch (thinking of gulp, grunt, mixing in babel, browserify etc). Those
changes were never about speed, at least a drop in build time was nice - a bit
of caching goes a long way. The changes were about being able to use the syntax
and libraries we wanted with as little fuss as possible.</p>
<p>Webpack <em>can</em> do almost anything. I'm not convinced esbuild can match that, and
as an open-source maintainer I'd argue it shouldn't break its original goals to
match Webpack.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="exploring-the-caveats">Exploring the caveats<a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#exploring-the-caveats" class="hash-link" aria-label="Enlace directo al Exploring the caveats" title="Enlace directo al Exploring the caveats" translate="no">​</a></h2>
<p>I started breaking down what our current bundle setup actually does, after all
if we were just bundling plain JS I'd probably be using rollup and terser. Going
through our <code>webpack.config.js</code> file I can see that it:</p>
<ul>
<li class="">Conditionally provide sourcemaps depending on the environment.</li>
<li class="">Uses babel to parse js/jsx files<br>
<!-- -->At the same time cherry picks lodash imports
<a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#lodash"><sup>*</sup></a>.</li>
<li class="">Parse <code>import</code>s for <code>.css</code> files through <code>style-loader</code> and <code>css-loader</code><br>
<!-- -->Injecting the styles onto the dom.</li>
<li class="">Detect <code>require</code> statements to images, copy them to an assets folder<br>
<!-- -->and replace the statement with a url to image.</li>
<li class="">Can watch files (excluding <code>node_modules</code>) in development.</li>
<li class="">Splits files that come from <code>node_modules</code> into a <code>vendor.js</code> bundle.</li>
</ul>
<p>That's a lot more than just building something, there's some implicit behaviour
here.</p>
<ul>
<li class="">The interaction between <code>style-loader</code> and <code>css-loader</code> results in extra
functions and behaviour being introduced, it's not producing a <code>.css</code> file.<!-- -->
<blockquote>
<p>I'm personally not a big fan of apps injecting styles, but I think I get why
people do it. Maybe I'm old-school and like to have my stylesheets delivered
in a few files (or even one).</p>
</blockquote>
</li>
<li class="">The splitting doesn't know about <code>app.js</code>, it puts everything that resolves to
<code>node_modules</code> in <code>vendor.js</code>. Subtle but worth pointing out.</li>
</ul>
<p><a name="lodash"><sup>*</sup></a> Could probably ignore this and refactor some
files and check that tree-shaking is working properly.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-we-need-esbuild-to-provide">What we need esbuild to provide<a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#what-we-need-esbuild-to-provide" class="hash-link" aria-label="Enlace directo al What we need esbuild to provide" title="Enlace directo al What we need esbuild to provide" translate="no">​</a></h2>
<p>Like I mentioned before, webpack is super versatile and it would be
short-sighted to get esbuild to do everything it does.</p>
<p>So I'm approaching this with the idea that I code will need some changes, the
less the better - I've always firmly believed the kinds of restrictions smaller
tools provide can make things neater and more portable.</p>
<ul>
<li class="">✅ <strong>Provide source maps</strong><br>
<!-- -->Supported out of the box. <a href="https://esbuild.github.io/api/#sourcemap" target="_blank" rel="noopener noreferrer" class="">docs</a></li>
<li class="">✅ <strong>Watching files</strong><br>
<!-- -->Yup. <a href="https://esbuild.github.io/api/#watch" target="_blank" rel="noopener noreferrer" class="">docs</a></li>
<li class="">🆗 <strong>Build our css</strong> (and vendored css)<br>
<!-- -->Compiling CSS is supported, however it doesn't (as far as I can tell) doesn't
replace <code>style-loader</code> and how it injects CSS into the DOM.</li>
<li class="">✅ <strong>Copy image assets referenced in JS</strong><br>
<!-- -->Indeed it can, using the <code>file</code> loader.
<a href="https://esbuild.github.io/content-types/#external-file" target="_blank" rel="noopener noreferrer" class="">docs</a></li>
<li class="">✅ <strong>Can split our files</strong><br>
<!-- -->I picked a confusing green checkbox to illustrate that while esbuild does
support file splitting - it appears to have some caveats, primarily that it
only works with <code>esm</code> output files. We don't ship ES Modules to the browser,
but seems like a good moment to try given the primary targets for our web
front end getting module support in 2019 <sup><a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#esm">2</a></sup>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="giving-it-a-go">Giving it a go<a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#giving-it-a-go" class="hash-link" aria-label="Enlace directo al Giving it a go" title="Enlace directo al Giving it a go" translate="no">​</a></h2>
<p>I've tried to go over the process as methodically as possible, after all this
isn't a new codebase nor am I alone on this. It's always important to understand
who's going to be working with this, and respect the varying skill sets and
focuses of our peers.</p>
<p>So we know what we want, but can any of this work? Let's give it a go with the
simplest of steps:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">./node_modules/.bin/esbuild js/app.js --bundle --outfile=out.js</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> &gt; js/app.js:58:2: error: Unexpected "&lt;"</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    58 │   &lt;React.StrictMode&gt;</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">       ╵   ^</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">1 error</span><br></div></code></pre></div></div>
<p>Bang! Right, that one makes sense to me, it doesn't know what JSX is yet. I'm
gonna take a wild guess and say it won't know about the CSS or our referenced
images.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">./node_modules/.bin/esbuild js/app.js \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.js=jsx \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.png=file \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.jpg=file \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.jpeg=file \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --bundle \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --outdir=out</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> &gt; js/marketing/ProductsList.js:53:40: error: Unexpected ":"</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    53 │     const displayList = products.filter(::this.includesText);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">       ╵                                         ^</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> &gt; js/marketing/Banner.js:13:4: warning: Duplicate key "background" in object literal</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    13 │     background: `-webkit-linear-gradient(to left, ${theme.palette.banner.right}, ${theme.palette.banner.left})`,</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">       ╵     ~~~~~~~~~~</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></div></code></pre></div></div>
<p>Nice! Two things worth mentioning here:</p>
<ol>
<li class="">We're using some non-standard ES syntax here: <code>::</code>, let's replace that with
the equivalent <code>includesText.bind(this)</code>.</li>
<li class="">Some duplicate keys in our theme objects, looks like a warning but worth
cleaning up.</li>
</ol>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">./node_modules/.bin/esbuild js/app.js \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.js=jsx \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.png=file \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.jpg=file \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --loader:.jpeg=file \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --bundle \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        --outdir=out</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  out/app.js                               8.0mb ⚠️</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  out/commcare-dhis2-JKJLM3MQ.png        471.0kb</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  ...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  out/app.css                             85.1kb</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  ...and 145 more output files...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">⚡ Done in 651ms</span><br></div></code></pre></div></div>
<p>Jeepers! 651ms! That's nuts.</p>
<p>We can see it's copied our images and css into the build folder. Note that we're
not doing any bundle splitting right now and from the looks of it</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">... \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">&gt;   --minify</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  out/app.js                               3.4mb ⚠️</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  ...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">⚡ Done in 611ms</span><br></div></code></pre></div></div>
<p>That's better, wow. It's kinda difficult to not be amazed. For context, a
minified and split production build takes about 34s with webpack and that's on
my i7 desktop machine, and 197s (3+ mins) on CI/CD.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's next?<a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#whats-next" class="hash-link" aria-label="Enlace directo al What's next?" title="Enlace directo al What's next?" translate="no">​</a></h2>
<p>So our 'can we actually do this' seems to have gone pretty well so far. I'm
really excited about what this will give us in the end.</p>
<p>But a shell command doth not a replacement for webpack make. We still need to:</p>
<ol>
<li class="">Get Phoenix to use esbuild and watch our files as we work.</li>
<li class="">Ensure that our html templates serve the correct files in dev &amp; production.<br>
<!-- -->Including our CSS that is no longer injected into the DOM.</li>
<li class="">Split at least our vendored modules into their own bundle.</li>
<li class="">Make sure sourcemaps generate correctly for when we upload them to Sentry.</li>
<li class="">Have some kind of cache-busting naming scheme for production builds.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="resources">Resources<a href="https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#resources" class="hash-link" aria-label="Enlace directo al Resources" title="Enlace directo al Resources" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://fly.io/blog/phoenix-moves-to-esbuild-for-assets/" target="_blank" rel="noopener noreferrer" class="">FYI: Phoenix drops webpack and npm for esbuild</a>
<a name="ref1"><sup>1</sup></a></li>
<li class=""><a href="https://caniuse.com/es6-module" target="_blank" rel="noopener noreferrer" class="">JavaScript modules via script tag</a>
<a name="esm"><sup>2</sup></a></li>
</ul>]]></content>
        <author>
            <name>Stuart Corbishley</name>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="js" term="js"/>
        <category label="webpack" term="webpack"/>
        <category label="build" term="build"/>
        <category label="phoenix" term="phoenix"/>
        <category label="ci/cd" term="ci/cd"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Improving Multistage Docker Builds using Buildx]]></title>
        <id>https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx</id>
        <link href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx"/>
        <updated>2021-10-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[So you're using docker's multi-stage builds and noticed that your build times]]></summary>
        <content type="html"><![CDATA[<p>So you're using docker's multi-stage builds and noticed that your build times
aren't nearly as quick as you expected?</p>
<p>As many teams who spend more and more time using docker, it's quite common to
get into multi-stage builds; usually resulting in significantly smaller images.</p>
<p>However this comes with a pretty significant dilemma with caching. Even when
using the <code>--cache-from</code> flag when building, docker only caches the last image.</p>
<p>One proposed solution<sup><a href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx#ref1" class="">1</a></sup>, is to pull, build and push each
individual stage. Coming with tight coupling between the shape of your
Dockerfile and your build process/scripts.</p>
<p>The other solution uses Docker Buildx which the document describes as:</p>
<blockquote>
<p>Docker Buildx is a CLI plugin that extends the docker command with the full
support of the features provided by Moby BuildKit builder toolkit. It provides
the same user experience as docker build with many new features like creating
scoped builder instances and building against multiple nodes concurrently.</p>
</blockquote>
<p>While that sounds pretty cool, it doesn't really touch on caching. This actually
took me a while to find out that it would in fact do caching very differently.
In fact it's a very different experience using it, and has lots of really cool
features that further detach you from the local docker state allowing you to
build in environments that are stateless - such as Google CloudBuild without
having to wire up some kind of persistence or file caching scheme.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="buildx">Buildx<a href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx#buildx" class="hash-link" aria-label="Enlace directo al Buildx" title="Enlace directo al Buildx" translate="no">​</a></h2>
<p>We're only going to scratch the surface of Buildx, and with that let's get the
absolute minimum working; build our image locally.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="local-cache">Local Cache<a href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx#local-cache" class="hash-link" aria-label="Enlace directo al Local Cache" title="Enlace directo al Local Cache" translate="no">​</a></h3>
<p>First things first we need to create a builder, and select it for use. This is
important as without creating a buildx builder (and setting it as the default),
buildx will use the <code>docker</code> driver instead of the <code>docker-container</code> driver
which we want in order to take advantage of cache exporting.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">docker buildx create --name mybuilder --use</span><br></div></code></pre></div></div>
<blockquote>
<p>You only need to run this once, except in the case of CloudBuild where each
invocation is a new node.</p>
</blockquote>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">docker buildx build \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  --cache-from=type=local,src=/tmp/buildx-cache \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  --cache-to=type=local,dest=/tmp/buildx-cache \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  --load \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  .</span><br></div></code></pre></div></div>
<p>While the <code>--cache-*</code> options aren't specifically required when running <code>build</code>,
as <code>buildx</code> does manage its own local cache (distinct from the regular docker
cache), it's there to emphasise the options that cache can be provided via the
CLI options.</p>
<p>This is about as close as you get to a regular docker build, with the
significant difference being that you have to specify where to cache from and
to.</p>
<p>The <code>--load</code> flag is to tell buildx to set the output to the local docker
daemon. Without that you won't actually get a resulting image to run. However,
depending on your use case, this could be seen as a convenience - if you're
wanting to run your tests inside your build; a resulting image isn't
particularly useful.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="remote-cache">Remote Cache<a href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx#remote-cache" class="hash-link" aria-label="Enlace directo al Remote Cache" title="Enlace directo al Remote Cache" translate="no">​</a></h3>
<p>Now comes to the part I'm most interested in, caching in a stateless/remote
environment. Multipart builds for us at OpenFn are essential, since we use
Elixir and like other compiled languages there is a lot to be gained by only
shipping the stuff you're going to run; and no language is safe from requiring
several times more 'stuff' in order to build our apps.</p>
<p>Buildx supports a
<a href="https://github.com/docker/buildx/blob/master/docs/reference/buildx_build.md#-export-build-cache-to-an-external-cache-destination---cache-to" target="_blank" rel="noopener noreferrer" class="">handful of different types</a>
of caching sources and destinations. We're going to be using the <code>registry</code>
type, where you point the cache at a repository reference (repo/image<!-- -->:tag<!-- -->
style).</p>
<blockquote>
<p>One thing to note is that Google Container Registry does not support the
metadata/manifest format that buildx uses, so if you're using Google Cloud you
will need to start using Artifact Registry.</p>
</blockquote>
<p><strong>Inline</strong></p>
<p>Push the image and the cache together:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">--cache-from=type=registry,ref=$IMAGE_NAME \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">--cache-to=type=inline \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></div></code></pre></div></div>
<p>This comes with the constraint that cache mode is always <code>min</code>, which only
exports/caches the resulting layers; which is still better than the plain docker
build caching but I think having the intermediary layers is generally a win. We
want to avoid a single line change invalidating an entire build step.</p>
<p><strong>Registry</strong></p>
<p>Resulting image and cache are separated:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">--cache-from=type=registry,ref=$IMAGE_NAME-build-cache \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">--cache-to=type=registry,ref=$IMAGE_NAME-build-cache,mode=max \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></div></code></pre></div></div>
<p>Again coming back to the cache mode, here being <code>max</code>; all intermediary laters
are exported to the cache image as well.</p>
<p>I have opted to create <em>two</em> images, one for caching and another for the
resulting image used to deploy. This gains us a much more granular cache and the
ability to more easily manage the cache image - like deleting the whole thing
when wanting to invalidate the cache. Not to mention I'm fairly sure the size of
our images that get pulled on kubernetes would get significantly larger with
many more layers.</p>
<p>It feels like a safer bet to have lean images for kubernetes to pull, and chunky
cache images specifically for speeding up build.</p>
<p>Depending on your setup, pulling large images can get <em>seriously</em> expensive in a
reasonably active deployment environment - like on AWS ECS without using
PrivateLink.</p>
<blockquote>
<p>It appears the <code>moby/buildkit</code> documentation also demonstrates
<a href="https://github.com/moby/buildkit#registry-push-image-and-cache-separately" target="_blank" rel="noopener noreferrer" class="">this</a>
approach.</p>
</blockquote>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">IMAGE_NAME=us-east4-docker.pkg.dev/&lt;project-name&gt;/platform/app \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">docker buildx build \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  -t $IMAGE_NAME:latest \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  --cache-from=type=registry,ref=$IMAGE_NAME-build-cache \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  --cache-to=type=registry,ref=$IMAGE_NAME-build-cache,mode=max \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  --push \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  --progress=plain \</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  .</span><br></div></code></pre></div></div>
<p>This implies that the cache image is named with the suffix <code>-build-cache</code>:<br>
<code>us-east4-docker.pkg.dev/&lt;project-name&gt;/platform/app[-build-cache]</code>.</p>
<p>The <code>--push</code> argument tells buildx to push the resulting image to the registry.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tips">Tips<a href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx#tips" class="hash-link" aria-label="Enlace directo al Tips" title="Enlace directo al Tips" translate="no">​</a></h2>
<p><strong>Clearing the local cache</strong></p>
<p>As mentioned before, buildx has its own cache and in order to clear the cache
while debugging and readying a Dockerfile for remote building you'll probably
need to reach for <code>docker buildx prune</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="closing-thoughts">Closing thoughts<a href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx#closing-thoughts" class="hash-link" aria-label="Enlace directo al Closing thoughts" title="Enlace directo al Closing thoughts" translate="no">​</a></h2>
<p>Using buildx has been a really pleasant experience, having personally attempted
using it a few times over the last 3 years; the most recent one being the first
time I felt confident getting it into production. As with any sufficiently
flexible build tooling, the errors and issues you can run into range from
complete gibberish, genuinely concerning inconsistencies to architectural
choices that you haven't fully caught up on; requiring an ever growing list of
changes you need to make to your own build process.</p>
<p>Our initial observations have been great, reasonable changes on our build have
gone from 28 minutes to around 9 minutes.</p>
<p>While I have encountered a few confusing cache invalidations, especially when
building locally, exporting the cache to a repository and then having CloudBuild
use the image cache. And occasionally locally having what feels like <em>really</em>
aggressive caching on intermediate steps, leading me to pruning the local cache.</p>
<p>But overall, these issues aren't necessarily buildx issues and more likely a
combination of building docker images in general except with many more steps
accounted for by the cache.</p>
<p>It's kinda hard to see now what the exact issues I had with it in the past, but
hey!</p>
<p>Buildx has given me what I 'expected' with docker multi-stage builds, and having
the cache in a repository completely side-steps having to attach a shared volume
or copying from a storage bucket.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="resources">Resources<a href="https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx#resources" class="hash-link" aria-label="Enlace directo al Resources" title="Enlace directo al Resources" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://pythonspeed.com/articles/faster-multi-stage-builds/" target="_blank" rel="noopener noreferrer" class="">Multi-stage builds #3: Speeding up your builds</a>
<a name="ref1"><sup>1</sup></a></li>
<li class=""><a href="https://docs.docker.com/buildx/working-with-buildx/" target="_blank" rel="noopener noreferrer" class="">Docker Buildx</a></li>
<li class=""><a href="https://github.com/docker/buildx/blob/master/docs/reference/buildx_build.md#buildx-build" target="_blank" rel="noopener noreferrer" class="">buildx build reference</a></li>
<li class=""><a href="https://github.com/moby/buildkit#registry-push-image-and-cache-separately" target="_blank" rel="noopener noreferrer" class="">mody/buildkey Registry cache exporter</a></li>
</ul>]]></content>
        <author>
            <name>Stuart Corbishley</name>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="docker" term="docker"/>
        <category label="ci/cd" term="ci/cd"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Wrapping my head around jobs]]></title>
        <id>https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs</id>
        <link href="https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs"/>
        <updated>2021-07-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Jobs are business processes turned into functional-style scripts. What does that]]></summary>
        <content type="html"><![CDATA[<p>Jobs are business processes turned into functional-style scripts. What does that
mean, how should you approach writing jobs?</p>
<p>First, this is how <em>I</em> think about jobs and what we do at Open Function Group to
try to make our job code as readable, future-proof, and concise as possible.
There are a million different ways to approach writing jobs. This is one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="it-all-starts-with-state">It all starts with <code>state</code><a href="https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs#it-all-starts-with-state" class="hash-link" aria-label="Enlace directo al it-all-starts-with-state" title="Enlace directo al it-all-starts-with-state" translate="no">​</a></h2>
<p>If a job is a set of instructions for a chef (a recipe?) then the initial
<code>state</code> is all of the ingredients they need tied up in a perfect little bundle.
It usually looks something like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"configuration"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"hostUrl"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"https://moh.kenya.gov.ke/dhis2"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"username"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"taylor"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"password"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"very-secret"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"data"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"type"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"registration"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"patient"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"age"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">24</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"gender"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"M"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"nationalId"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"321cs7"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>This might be the initial <code>state</code> for a real-time, message-triggered job. Some
source system generated a new patient payload and sent that payload to OpenFn.
The data from our source system will wind up in <code>state.data</code>. Now if my job is
meant to take this new patient registration information and use it to create a
new record in the national health record system, I'll also need to provide my
robot-chef here with a credential so they can access that system. The credential
I've specified will get put into <code>state.configuration</code> and now our "raw
ingredients" are all ready for our robot chef.</p>
<p>Note that even if this job was initiated by a cron trigger (e.g., "Hey chef,
prepare this recipe every Tuesday at 7pm") or by a flow/catch trigger (e.g.,
"Hey chef, prepare this recipe only when you <em>fail</em> to make banana pancakes") it
will have an initial state.</p>
<p><strong>Every job, and every operation inside that job (think "step" in a recipe) is
called with <code>state</code> and returns <code>state</code> when it's done.</strong></p>
<p>Initial state for a cron triggered job might look like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"configuration"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"hostUrl"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"https://moh.kenya.gov.ke"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"apiKey"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"abc123"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"data"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"lastProcessedId"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">321</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>And for a fail triggered job like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"configuration"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"hostUrl"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"https://moh.kenya.gov.ke"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"apiKey"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"abc123"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"data"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"lastProcessedId"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">321</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"error"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(195, 232, 141)">"Required field missing"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"Patient Surname"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"Line 43"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>No matter what, jobs start with state. See
<a class="" href="https://docs.openfn.org/es/documentation/jobs/state">"Initial and final state for runs"</a> for a detailed
breakdown.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="it-ends-with-state-too">It ends with <code>state</code> too<a href="https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs#it-ends-with-state-too" class="hash-link" aria-label="Enlace directo al it-ends-with-state-too" title="Enlace directo al it-ends-with-state-too" translate="no">​</a></h2>
<p>Now that we've got it in our heads that <code>state</code> is the raw ingredients you hand
to your chef when you ask them to prepare a recipe, let's look at the recipe.
Boiled down (excuse the pun) a job for loading those patients into the national
health record system might look like this:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/insuranceRegistrations'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/patients'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">someData </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/visits'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">someData </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>We're telling our chef to take those raw ingredients (login info for our
national health system and a chunk of information about a newly registered
patient) and do the following:</p>
<ol>
<li class="">Find out whether this person already has a national health insurance number</li>
<li class="">Add this person to the patient registry (making use of some insurance data
from step 1)</li>
<li class="">Add a visit record with information about this initial visit (making use of
patient registry data from step 2)</li>
</ol>
<p>When all of this is done, we'll not only have a new patient and visit logged in
the national health registry, but we'll also return a final <code>state</code> object with
information about what we've done that can be used in subsequent jobs. Imagine
that we want to make a cash transfer to this patient so that they can take a cab
to the next visit—we might create a job with the Mpesa adaptor that takes the
final state of this first job as its <em>initial state</em>. In this way, jobs are
composable.</p>
<p>But what about the complexity inside our job—in order to complete step 2, we
need some data from the insurance registry and we only get that data in step 1.
Crucially, each operation (again, think "step" in a recipe) takes state and
returns state. In effect, the OpenFn execution pipeline simply calls all of your
action methods <em>with state</em>, passing it along from one operation to the next,
waiting for each to finish and using the output from the first as the input for
the second.</p>
<p>While you may write your <code>get</code>, <code>post</code>, <code>post</code> job as it's show above, the way
it's handled by OpenFn is actually more like:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/insurance'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">useDataFromState </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">then</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">state2</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/patients'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">useDataFromState2 </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">state2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">then</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">state3</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/visits'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">useDataFromState3 </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">state3</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Each of these operations returns a function which <em>takes state</em> and returns
state. This means that <em>within</em> a job, you are essentially modifying <code>state</code>,
creating/manipulating records in external systems, and returning <code>state</code>.</p>
<p>It opens up a really interesting world of possibility for data manipulation,
cleaning, or transformation. Consider what we might do <em>after</em> we get data from
the insurance registry but <em>before</em> we create that patient in the national
patient registry:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/insuranceRegistrations'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">fn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(255, 203, 107)">console</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">log</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// let's look at the response from the insurance API.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">people</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">p</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> p</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access maybe-class-name">HasActiveInsurance</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// and modify the payload to only retain those with active insurance</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// before returning state for our create patients operation.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/patients'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">someData </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/visits'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">someData </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>We might even need to do some manipulation <em>before</em> we send a <code>get</code> request to
the insurance registry. That's no problem:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">fn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">registrationType</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">age</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">18</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Adult'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Minor'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// before returning state for our create patients operation.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/insuranceRegistrations'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">query</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token literal-property property">type</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'registrationType'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">fn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">people</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">p</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> p</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access maybe-class-name">HasActiveInsurance</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/patients'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">someData </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/visits'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">someData </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Here, we've added a step to modify the initial <code>state</code> before we send that first
<code>get</code> request to the insurance API. We determine if the new patient is a minor,
and then use that newly calculated data to apply a query to the insurance API
request.</p>
<p>Using <code>fn(state =&gt; state)</code> or <code>alterState(state =&gt; state})</code> is incredibly
useful, because it allows us to separate our data manipulation, calculation, and
raw Javascript (which will be harder for low-tech users to understand) from our
external actions. Let's explore that some more.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="keeping-external-actions-clean">Keeping external actions clean<a href="https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs#keeping-external-actions-clean" class="hash-link" aria-label="Enlace directo al Keeping external actions clean" title="Enlace directo al Keeping external actions clean" translate="no">​</a></h2>
<p>Inside each operation we could do some data manipulation... all of these
operations, across the many different language packages, allow for inline data
manipulation like this:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/insuranceRegistrations'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(130, 170, 255)">query</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token console class-name" style="color:rgb(255, 203, 107)">console</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">log</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"I'm doing some fancy stuff here."</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token literal-property property">type</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">age</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">18</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Adult'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Minor'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/patients'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">body</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function-variable function" style="color:rgb(130, 170, 255)">name</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token template-string interpolation">state</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">data</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">firstName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token template-string interpolation">state</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">data</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">lastName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>But if you're interacting with both technical and non-technical users, it makes
for harder to read jobs. Consider the following instead:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Perform calculations...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">fn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Create several new calculated attributes...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token spread operator" style="color:rgb(137, 221, 255)">...</span><span class="token plain">state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token literal-property property">type</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">age</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">18</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Adult'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Minor'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token literal-property property">fullName</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token template-string interpolation">state</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">data</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">firstName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token template-string interpolation">state</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">data</span><span class="token template-string interpolation punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token template-string interpolation property-access">lastName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Get insurance data...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/insuranceRegistrations'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token literal-property property">query</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token literal-property property">type</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'type'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Create new patient...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">post</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'/api/patients'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token literal-property property">body</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token literal-property property">name</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'fullName'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Since we often have non-developers creating the external operations like <code>get</code>
and <code>post</code> above, this pattern makes our handoff easier. The business analyst
can say "I need to have a registration <code>type</code> field available for use when
querying the insurance registry." A developer might respond, "Great! How do you
want to calculate it... I've got all of Javascript at my fingertips." That dev
can then make as many API calls as they'd like, perform as many
<code>map.reduce(...)</code> calls as their heart desires to complete that calculation...
so long as they make sure the hand off <code>state</code> to the business analyst's
operation with a valid <code>state.data.type</code> attribute.</p>
<p>A final benefit of this approach is that it becomes much easier to generate job
scripts from Google Sheets. Our implementation team frequently works with
non-technical clients to generate field maps that look like this:</p>
<table><thead><tr><th>Path to Source Data</th><th>Destination Field</th><th style="text-align:right">Auto-generated syntax (using concat)</th></tr></thead><tbody><tr><td>patient.fullName</td><td>name</td><td style="text-align:right">field('name', dataValue('patient.fullName')),</td></tr><tr><td>patient.age</td><td>age</td><td style="text-align:right">field('age', dataValue('patient.age')),</td></tr><tr><td>???</td><td>type</td><td style="text-align:right">plz help us calculate 'type' based on x, y, z</td></tr><tr><td>patient.sex</td><td>gender</td><td style="text-align:right">field('gender', dataValue('patient.sex')),</td></tr></tbody></table>
<p>We can then copy and paste the syntax generated in that final column directly
into OpenFn and update the bits that need some sort of custom code, writing an
<code>fn(state)</code> block or an <code>alterState(state)</code> block before the external action.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrapping-up">Wrapping up<a href="https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs#wrapping-up" class="hash-link" aria-label="Enlace directo al Wrapping up" title="Enlace directo al Wrapping up" translate="no">​</a></h2>
<p>Some key takeaways here:</p>
<ol>
<li class="">
<p>Jobs start and end with <code>state</code> — some raw ingredients that will be used in a
recipe.</p>
</li>
<li class="">
<p>Jobs are lists of <code>operations</code> — steps in a recipe that <em>each</em> take <code>state</code>,
<em>do some stuff</em>, and then return <code>state</code>.</p>
</li>
<li class="">
<p>As you move through the steps in a job, you are modifying <code>state</code>. Each
subsequent step begins with the final state from the previous step.</p>
</li>
<li class="">
<p>It may be useful to keep all your custom Javascript data cleaning,
manipulation, etc., in a separate operation (e.g., <code>fn(state)</code> or
<code>alterState(state)</code>) so that your external actions are clean and easy to
follow.</p>
</li>
</ol>
<p>Finally, taking a close look at how developers write those <code>fn(state)</code> steps
tells us a lot about what the job execution pipeline is really doing:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// here, "fn" is a function that takes state and returns state</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">fn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(255, 203, 107)">console</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token method function property-access" style="color:rgb(130, 170, 255)">log</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"I'm doing some cool stuff."</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">//  I might create some new attribute...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">myNewThing</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// And ALWAYS return state for the next operation to use...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>I hope this gives you sense of how I think about structuring jobs and building
data pipelines or automation flows on OpenFn. We recognize that this stuff is
complex, and are pushing our new documentation regularly, so please do get in
touch if you think there are ways we could improve this type of
walk-through/helper article.</p>
<p>Happy integrating,</p>
<p>Taylor</p>]]></content>
        <author>
            <name>Taylor Downs</name>
            <email>taylor@openfn.org</email>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
        <category label="jobs" term="jobs"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Forms and Cases: CommCare and event-based integration]]></title>
        <id>https://docs.openfn.org/es/articles/2021/05/24/commcare-events</id>
        <link href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events"/>
        <updated>2021-05-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This is a quick one, but I just got off an exciting call with an organization]]></summary>
        <content type="html"><![CDATA[<p>This is a quick one, but I just got off an exciting call with an organization
that's going to set up some jobs to move data into Salesforce from CommCare and
realized that despite this being one of our more common integration
requirements, we haven't done a 'tips' article for this type of project. Until
now.</p>
<p>So here goes. While this is by no means an exhaustive project planning template,
here are a few things to keep in mind if you're planning to implement a CommCare
to Salesforce integration on your own.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="most-people-use-data-forwarding-in-commcare">Most people use "Data Forwarding" in CommCare<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#most-people-use-data-forwarding-in-commcare" class="hash-link" aria-label="Enlace directo al Most people use &quot;Data Forwarding&quot; in CommCare" title="Enlace directo al Most people use &quot;Data Forwarding&quot; in CommCare" translate="no">​</a></h2>
<p>First, most people make use of CommCare's "Data Forwarding" feature to send form
submissions and changes in cases (creation, update, closure, etc.) to OpenFn in
real-time. You can read about that
<a class="" href="https://docs.openfn.org/es/adaptors/commcare#webhook-or-data-forwarding-setup-commcare-to-openfn">here</a>
but the key consideration at this planning stage is <em>when</em> you'll be performing
operations—<code>create(...)</code>, <code>update(...)</code>, <code>upsert(...)</code>, <code>query(...)</code>,
<code>(bulk(...)</code>, etc.—in Salesforce and what data you'll have access to.</p>
<p>Each time a form submission comes into CommCare, we'll get a copy of that
submission at OpenFn and can use that data to create or modify some records in
Salesforce.</p>
<p>Likewise, each time a case gets updated (or created or closed) we'll get a copy
of the case with all the case "properties" and we can use that data to <em>do some
stuff</em> in Salesforce.</p>
<p>If you are using "Form Forwarding", the <code>trigger</code> you'd create in OpenFn might
look like this <code>{"form":{"@name":"ART Adherence Self-Reporting Tool"}}</code> and it
would trigger your <code>job</code> any time an "ART Adherence Self-Reporting Tool"
submission arrived from CommCare, giving that job access to all of the data
inside that submission.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="working-with-the-data-that-comes-from-commcare">Working with the data that comes from CommCare<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#working-with-the-data-that-comes-from-commcare" class="hash-link" aria-label="Enlace directo al Working with the data that comes from CommCare" title="Enlace directo al Working with the data that comes from CommCare" translate="no">​</a></h2>
<p>Assuming you're using making use of case management, the data that arrives from
CommCare will look something like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"__query_params"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"app_id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"some-long-id"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"app_id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"some-long-id"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"archived"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"attachments"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"1621866020043.jpg"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"content_type"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"image/jpeg"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"length"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">16423</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"url"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"https://www.commcarehq.org/a/your-project/api/form/attachment/some-uuid/1621866020043.jpg"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"form.xml"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"content_type"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"text/xml"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"length"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2727</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"url"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"https://www.commcarehq.org/a/your-project/api/form/attachment/some-uuid/form.xml"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"build_id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"0ec83881cd0e420dad5c24ed3a5452fe"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"domain"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"your-project"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"edited_by_user_id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token null keyword" style="font-style:italic">null</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"edited_on"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token null keyword" style="font-style:italic">null</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"form"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"#type"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"data"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"@name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"ART Adherence Self-Reporting Tool"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"@uiVersion"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"1"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"@version"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2783"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"@xmlns"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"http://openrosa.org/formdesigner/59E1207B-969F-402D-9EEE-675504036F78"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"administrative"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"coach_verification"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"check_here"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"visit_notes"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"vist_notes_to_save"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"case"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"@case_id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"1ec51ee9-5aef-4bd2-b7eb-7599856251bc"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"@date_modified"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:20:28.693000Z"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"@user_id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"332e893dcd1b413686621bd80aae0cd3"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"@xmlns"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"http://commcarehq.org/case/transaction/v2"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"update"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token property">"consent_received"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"yes"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token property">"home_visit_notes"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"meta"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"@xmlns"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"http://openrosa.org/jr/xforms"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"appVersion"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"CommCare Android, version \"2.51.2\"(463994). App v2798. CommCare Version 2.51.2. Build 463994, built on: 2021-03-17"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"app_build_version"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2798</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"commcare_version"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2.51.2"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"deviceID"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"commcare_a39f55a5-c744-4e33-8e01-d17e7698894f"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"drift"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"geo_point"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token null keyword" style="font-style:italic">null</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"instanceID"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"130c68c5-7d17-4086-8a85-27d7d7da2216"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"timeEnd"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:20:28.693000Z"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"timeStart"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:18:46.856000Z"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"userID"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"332e893dcd1b413686621bd80aae0cd3"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"username"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"some-chw"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"participant_information"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"participant_id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"007"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"taylor downs"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"gender"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"male"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"guardian_information"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token property">"guardians_name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"Fake Data"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token property">"guardians_phone_number"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"8675309"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token property">"guardians_signature"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"1621866020043.jpg"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token property">"relationship_to_participant"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"father"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"current_medications"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"generic-1"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token property">"active"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"fakelyn-notrealiol"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token property">"active"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">false</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"sasstra-zenica"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token property">"active"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">false</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"ibuprofen"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token property">"active"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"tested_for_hiv_status_tested_for_hiv"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"OK"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"visit_information"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"consent_given"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"yes"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"date_consent_given"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-23"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token property">"visit_date"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-23"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"130c68c5-7d17-4086-8a85-27d7d7da2216"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"indexed_on"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:20:39.045971"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"initial_processing_complete"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"is_phone_submission"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"metadata"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"appVersion"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"CommCare Android, version \"2.51.2\"(463994). App v2798. CommCare Version 2.51.2. Build 463994, built on: 2021-03-17"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"app_build_version"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2798</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"commcare_version"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2.51.2"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"deviceID"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"commcare_a39f55a5-c744-4e33-8e01-d17e7698894f"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"drift"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"geo_point"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token null keyword" style="font-style:italic">null</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"instanceID"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"130c68c5-7d17-4086-8a85-27d7d7da2216"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"location"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token null keyword" style="font-style:italic">null</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"timeEnd"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:20:28.693000Z"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"timeStart"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:18:46.856000Z"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"userID"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"332e893dcd1b413686621bd80aae0cd3"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">"username"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"some-chw"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"problem"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token null keyword" style="font-style:italic">null</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"received_on"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:20:37.976363Z"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"resource_uri"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"server_modified_on"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2021-05-24T14:20:38.111789Z"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"type"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"data"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"uiversion"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"1"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token property">"version"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"2783"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>This is a big blob of <code>JSON</code>—the body of the message that's received at OpenFn
when this particular form ("ART Adherence Self-Reporting Tool") is submitted in
CommCare—will be handed off to the job to start processing. The question is,
what should we do?</p>
<p>When setting up for a self-service implementation on OpenFn, the most important
thing you can do at this moment is carefully enumerate the data entry process
that you'd like a real human to follow. You can translate it to a job script
later.</p>
<p>You'll need to write this up for your own case, but in this fictional example,
here's the data entry process.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-instructions-for-our-worker">The instructions for our worker<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#the-instructions-for-our-worker" class="hash-link" aria-label="Enlace directo al The instructions for our worker" title="Enlace directo al The instructions for our worker" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>consejo</div><div class="admonitionContent_BuS1"><p>Right from the start, notice that we're being incredibly explicit with these
instructions! We're using the "API Name" (instead of just the "label", which
might be ambiguous) of every field we want filled out in Salesforce and we're
using the specific "path" to the data we want this person to enter from
CommCare.</p><p><strong>Why are we being so specific?</strong> Because eventually, a computer will need to
interpret this—and they're <em>terrible</em> with ambiguity!</p></div></div>
<ol>
<li class="">Every time a messaged is received with
<code>{"form":{"@name":"ART Adherence Self-Reporting Tool"}}</code> in the body (this is
our trigger)</li>
<li class="">Log into Salesforce and create a new participant with the <code>participant_id</code>
you find in the <code>form.participant_information</code> section as their
<code>Participant_Code__c</code>. (If one already exists in Salesforce with that code,
then update the existing record instead.)</li>
<li class="">Fill out the following fields in Salesforce based on the CommCare data in
this message:<!-- -->
<ul>
<li class=""><code>Name__c</code> with the data from <code>form.participant_information.name</code></li>
<li class=""><code>Sex__c</code> with the data from <code>form.participant_information.gender</code></li>
<li class=""><code>CommCare_Case_ID__c</code> with the data from <code>form.case.@case_id</code></li>
</ul>
</li>
<li class="">After you've created (or updated) this participant in Salesforce, create a
record of the visit with the <code>instanceID</code> from the <code>metadata</code> section as the
unique identifier <code>Visit_Code__c</code>. (Again, if there's already a visit with
that ID please update the existing record.)</li>
<li class="">Fill out the following fields for the visit with data from CommCare"<!-- -->
<ul>
<li class=""><code>Date__c</code> with <code>form.visit_information.visit_date</code>.</li>
<li class=""><code>Consented__c</code> with <code>form.visit_information.consent_given</code>.</li>
<li class="">Always set <code>Test_Status__c</code> to <code>true</code>, regardless of what's in the message
from CommCare.</li>
<li class="">And relate this record with the <code>Community_Health_Worker</code> by their username
in <code>form.metadata.username</code>.</li>
</ul>
</li>
<li class="">Finally, add a record for each medication listed in the
<code>form.participant_information.current_medications</code> array—matching on a unique
ID formed by a combination of the medication <code>name</code> and the <code>participant_id</code>
so that we can update existing medication records if they're present.</li>
<li class="">Fill out the following fields for the medication:<!-- -->
<ul>
<li class=""><code>Generic_Name__c</code> with <code>name</code></li>
<li class=""><code>Status__c</code> with <code>active</code></li>
<li class="">And relate this record with the participant you created or updated in step
2 via the <code>participant_id</code> field.</li>
</ul>
</li>
</ol>
<p>Phew... that's the task. It's just a fictional example and things could be much
more straightforward, or much more complicated than this, but it's important to
remember that if you can get to this level of <strong>precision and granularity</strong> in
your data entry process, a tool like OpenFn can automate this for you in a
flash.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="translating-this-into-an-openfn-project">Translating this into an OpenFn project<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#translating-this-into-an-openfn-project" class="hash-link" aria-label="Enlace directo al Translating this into an OpenFn project" title="Enlace directo al Translating this into an OpenFn project" translate="no">​</a></h2>
<p>If you're streaming data in from CommCare and you've got your Salesforce system
all set up so that this data entry person can complete the above steps (are all
the objects and fields created? are the right fields marked as "unique" and set
to be used as an "external id" in the Salesforce administration section? have
you turned on data forwarding in CommCare?) then it's time to turn them into an
OpenFn project!</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>consejo</div><div class="admonitionContent_BuS1"><p>A quick plug: <strong>Did you know that there's an
<a href="https://community.openfn.org/" target="_blank" rel="noopener noreferrer" class="">OpenFn community forum</a></strong> where you can post
stuff like the "steps" above and get help from other OpenFn users and staff
converting these steps into a real, working, OpenFn job?</p><p>Well, you do know! Check it out at
<a href="https://community.openfn.org/" target="_blank" rel="noopener noreferrer" class="">community.openfn.org</a></p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-a-salesforce-credential">Create a Salesforce credential<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#create-a-salesforce-credential" class="hash-link" aria-label="Enlace directo al Create a Salesforce credential" title="Enlace directo al Create a Salesforce credential" translate="no">​</a></h3>
<p>We don't need a CommCare credential, since they'll send data to us. Create a
Salesforce credential that will allow the OpenFn worker to log into your
Salesforce system.</p>
<p>Read more about credentials <a class="" href="https://docs.openfn.org/es/documentation/build/credentials">here</a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-a-message-filter-trigger">Create a message-filter trigger<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#create-a-message-filter-trigger" class="hash-link" aria-label="Enlace directo al Create a message-filter trigger" title="Enlace directo al Create a message-filter trigger" translate="no">​</a></h3>
<ul>
<li class="">Select <code>Message Filter</code> for the <code>type</code></li>
<li class="">Enter <code>{"form":{"@name":"ART Adherence Self-Reporting Tool"}}</code> for the
<code>inclusion criteria</code></li>
</ul>
<p>Read more about triggers <a class="" href="https://docs.openfn.org/es/documentation/build/triggers">here</a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-the-job">Create the job<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#create-the-job" class="hash-link" aria-label="Enlace directo al Create the job" title="Enlace directo al Create the job" translate="no">​</a></h3>
<ul>
<li class="">Give it a name</li>
<li class="">Select the trigger you just created</li>
<li class="">Select the <code>salesforce</code> adaptor</li>
<li class="">Select the credential you just created</li>
</ul>
<p>And convert the instructions above to "operations" by using the inline help
provided by the Salesforce adaptor:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Use upsert to create or update a participant based on their participant code.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">upsert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token string" style="color:rgb(195, 232, 141)">'Participant__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token string" style="color:rgb(195, 232, 141)">'Participant_Code__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token string" style="color:rgb(195, 232, 141)">'Participant_Code__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.participant_information.participant_id'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Name__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.participant_information.name'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Sex__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.participant_information.gender'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'CommCare_Case_ID__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.case[@case_id]'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Then upsert a visit using the visit code.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">upsert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token string" style="color:rgb(195, 232, 141)">'Visit__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token string" style="color:rgb(195, 232, 141)">'Visit_Code__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Visit_Code__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'metadata.instanceID'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Date__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.visit_information.visit_date'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Consented__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.visit_information.consent_given'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Always set status to true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Test_Status__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// And related this visit to the participant we just created by their "code"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">relationship</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token string" style="color:rgb(195, 232, 141)">'Participant__r'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token string" style="color:rgb(195, 232, 141)">'Participant_Code__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.participant_information.participant_id'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// And finally for EACH mediation listed, create a medication record with a status</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">each</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">merge</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">dataPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.participant_information.current_medications[*]'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'pID'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'form.participant_information.participant_id'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">upsert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token string" style="color:rgb(195, 232, 141)">'Medication_Tx__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token string" style="color:rgb(195, 232, 141)">'Medication_Tx_ID__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token maybe-class-name">Medication_Tx_ID__c</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token parameter">state</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Here, inside the medications array we've "scoped" state so that</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// state.data, for each item in the array, looks like this:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// { pID: 007, name: "sasstra-zenica", active: false }</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// We will concatenate the participant ID with the medication name.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword control-flow" style="font-style:italic">return</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">pID</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Generic_Name__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'name'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Status__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'status'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">relationship</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'Participant__r'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Participant_Code__c'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dataValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'pID'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Now, every time this job runs (which is every time a CommCare form is submitted)
your OpenFn worker will upsert a <code>Participant</code>, upsert a <code>Visit</code>, and upsert a
whole list of <code>Medications</code> in Salesforce.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's next<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#whats-next" class="hash-link" aria-label="Enlace directo al What's next" title="Enlace directo al What's next" translate="no">​</a></h2>
<p>Well, in our little example you'd turn the job "on" (setting it to on the
inbound messages from CommCare) and let it run. Whenever there was a failure
(maybe your Salesforce admin added a new required field on the
custom<code>Medication</code> object) you'd get an email and you'd have to come back to
OpenFn to update your job, including that new field.</p>
<p>If you're in the process of designing your CommCare and Salesforce systems at
the moment, this back-and-forth will be pretty common. Keep in mind that you
want as much simplicity as possible in those end-user systems because... well
because <em>humans</em> have the interact with them every day!</p>
<p>So long as your processes are well defined, OpenFn can handle a bit of
complexity (data cleaning, transformation, complex logical flows, etc.) but you
should never make sacrifices to the user experience in CommCare and
Salesforce—that's a quick way to lose adoption.</p>
<p>So, ideally, you've designed your workflows in CommCare and Salesforce to make
your users happy and get them the information they need to do their jobs well
and <em>then</em> you come back to OpenFn and spell out our data entry instructions
like we've done above.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-final-thought">A final thought<a href="https://docs.openfn.org/es/articles/2021/05/24/commcare-events#a-final-thought" class="hash-link" aria-label="Enlace directo al A final thought" title="Enlace directo al A final thought" translate="no">​</a></h2>
<p>The two most important resources you've got at your disposal if you're setting
this all up on your own are:</p>
<ol>
<li class="">this site (docs.openfn.org), and</li>
<li class="">the <a href="https://community.openfn.org/" target="_blank" rel="noopener noreferrer" class="">forum</a> (community.openfn.org)</li>
</ol>
<p>Read through the <a class="" href="https://docs.openfn.org/es/documentation/tutorials/tutorial">"What is an integration"</a>,
<a class="" href="https://docs.openfn.org/es/documentation/get-started/terminology">"OpenFn Concepts"</a>, and
<a class="" href="https://docs.openfn.org/es/documentation/build/workflows">"Build"</a> sections if you're a thorough,
background-first kind of learner. If you crave snippets and sample job code,
head directly to the <a class="" href="https://docs.openfn.org/es/adaptors/library">Job Library</a> to see how other OpenFn
users are creating their jobs.</p>
<p>Either way, keep the community posted on your progress in the forum—you'll find
lots of helpful folks willing to lend you a hand in your integration journey.</p>]]></content>
        <author>
            <name>Taylor Downs</name>
            <email>taylor@openfn.org</email>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sync Like You Mean It: Thinking Through System “Syncing” Protocols]]></title>
        <id>https://docs.openfn.org/es/articles/2021/02/17/syncing-options</id>
        <link href="https://docs.openfn.org/es/articles/2021/02/17/syncing-options"/>
        <updated>2021-02-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[“Syncing” is getting two systems to a state of harmony. This might mean keeping]]></summary>
        <content type="html"><![CDATA[<p>“Syncing” is getting two systems to a state of harmony. This might mean keeping
a list of patients up to date, though modifications can be made in either
system. It might mean copying transactions from one system to another on a
nightly basis. It might mean a lot of things, but the key concept is that when
you sync systems, you’re asking them to work together while simultaneously
respecting both software systems’ independence.</p>
<p>In this post we’ll discuss two different syncing protocols to consider when
designing your data integration. These include:</p>
<ol>
<li class=""><strong>Real-time, or event-based, syncs</strong></li>
<li class=""><strong>Scheduled syncs</strong></li>
</ol>
<p>For a
<a href="https://www.openfn.org/spotlight/2021-02-09-interoperability-for-case-referrals" target="_blank" rel="noopener noreferrer" class="">recent project in Cambodia</a>,
OpenFn is being used by social workers to automate case referrals between the
software systems Primero and OSCaR. In the design phase, we evaluated these two
syncing options. Below, we'll explain what each one is, the differences between
them and which option we chose in the end.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-timeevent-based-syncs">Real Time/Event Based Syncs<a href="https://docs.openfn.org/es/articles/2021/02/17/syncing-options#real-timeevent-based-syncs" class="hash-link" aria-label="Enlace directo al Real Time/Event Based Syncs" title="Enlace directo al Real Time/Event Based Syncs" translate="no">​</a></h3>
<p>The first option considered for this integration was the real-time/event based
sync. This type of sync is triggered whenever a specified event takes place in a
system. With this approach, whenever a case is referred in Primero (via the user
interface, i.e., when a real case-worker clicks the “refer” button) OpenFn
receives a small payload with case data and transmits it to OSCaR and vice
versa.</p>
<p><img decoding="async" loading="lazy" alt="Real_Time_Sync" src="https://docs.openfn.org/es/assets/images/syncs1-81446bbef450166c67a52b2ef7a48b10.webp" width="1710" height="716" class="img_ev3q"></p>
<p>Because of their instantaneous nature, real time/event based syncs are great for
integrations that involve mobile payments or sms messages to recipients. Really,
anything that needs to be done “now”! Additionally, depending on your data
volumes real time syncs might save you money because you’re only using resources
when specific events take place. For instance, in the above example, a run is
triggered by a referral, so if there are only 10 case referrals/month, you'd
only process 10 runs each month.</p>
<p>This type of sync is great because it’s instantaneous, typically quite
straightforward to set up, doesn’t require any “state mangagement” on OpenFn,
and allows for the reprocessing of individual events. There are, however,
drawbacks.</p>
<p>For instance, what happens if the app that’s sending notifications to OpenFn
fails to send? What if AWS or GCP goes down, taking half of the internet with
it? If Primero “thinks” it sent the referral, OpenFn never receives it, that
case might not get referred to Oscar!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scheduled-syncs">Scheduled Syncs<a href="https://docs.openfn.org/es/articles/2021/02/17/syncing-options#scheduled-syncs" class="hash-link" aria-label="Enlace directo al Scheduled Syncs" title="Enlace directo al Scheduled Syncs" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Schedule_Dependent_Sync" src="https://docs.openfn.org/es/assets/images/syncs2-7e3c3eacc4048afa5abfffc4aa1a188a.webp" width="1710" height="946" class="img_ev3q"></p>
<p>The second option considered, a bi-directional schedule dependent sync, solves
for the issue discussed above. On a scheduled basis (every 5 minutes, for
example) OpenFn checks with Primero and Oscar to see if case referrals need to
be transmitted between the two systems and then refers the case if required. In
the unlikely event that any of the software systems involved crash, the
stability provided by the bi-directional sync means that all data is preserved
and eventually makes it to its destination safely.</p>
<p>The major drawback here is complexity. We had to use 4 jobs instead of 2, and
the job that is responsible for “pulling” data that’s been updated since the
time of the last successful sync has to keep “state”—or some sort of working
memory of what it’s done in the past. When pulling modified cases from Primero,
OpenFn now only pulls cases modified on or after <code>YYYY-MM-DD HH:MM:SS</code> where
<code>YYYY-MM-DD HH:MM:SS</code> is the time of the last successful, round-trip
synchronization. OpenFn has built-in functionality to handle exactly this
requirement, but not all ETL systems do and it’s a design implication that must
be considered.</p>
<p>Ultimately, for the project in Cambodia, we decided that this sync option is the
right choice because data integrity is more important than the speed of this
data flow. That’s a crucial point to understand—the organizations operating in
Cambodia decided that for this particular use case, being able to guarantee
eventual syncing was more important than having real-time syncing.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="both-sync-options-have-their-pros-and-cons">Both Sync Options Have Their Pros and Cons<a href="https://docs.openfn.org/es/articles/2021/02/17/syncing-options#both-sync-options-have-their-pros-and-cons" class="hash-link" aria-label="Enlace directo al Both Sync Options Have Their Pros and Cons" title="Enlace directo al Both Sync Options Have Their Pros and Cons" translate="no">​</a></h3>
<p>Both options definitely have their use-cases and OpenFn's platform versatility
enables your team to decide which type of sync is right for your project.</p>
<p>As always, we are here to help with any questions as you think through which
sync option makes the most sense for your project.</p>]]></content>
        <author>
            <name>Jed Goldstein</name>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Our Servers or Yours: Thinking through deployment options]]></title>
        <id>https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment</id>
        <link href="https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment"/>
        <updated>2021-02-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Zandile is a program manager at an iNGO and she needs to use CommCare, DHIS2,]]></summary>
        <content type="html"><![CDATA[<p>Zandile is a program manager at an iNGO and she needs to use CommCare, DHIS2,
and OpenFn for an upcoming public health project. She understands that all three
pieces of software can be deployed locally, or accessed as SaaS (Software as a
Service).</p>
<p>Essentially, Zandile needs to decide if she would like to run the software on
someone else’s servers (SaaS), or on her organization’s own servers (deployed
locally). Before making a decision she outlines the basic, non-technical
considerations for both options.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-saas">What is SaaS?<a href="https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment#what-is-saas" class="hash-link" aria-label="Enlace directo al What is SaaS?" title="Enlace directo al What is SaaS?" translate="no">​</a></h2>
<p>SaaS is software that is installed and <em>runs</em> on computers maintained by
software professionals, rather than on your own computer. While those computers
might be anywhere in the world, typically you'll access and <em>use</em> this software
via the Internet.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="some-benefits-of-saas">Some benefits of SaaS<a href="https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment#some-benefits-of-saas" class="hash-link" aria-label="Enlace directo al Some benefits of SaaS" title="Enlace directo al Some benefits of SaaS" translate="no">​</a></h3>
<p>With SaaS, the software vendor is responsible for the expenses of managing and
monitoring all of the technical components and issues associated with the
software. This means that Zandile’s iNGO will not be responsible for updating
the software to ensure compliance with new security regulations, maintaining the
servers, backing up the data, purchasing and managing uninterrupted power
supplies, and providing a team of physical security guards to protect the
computers and data therein against physical theft.</p>
<p>Going the SaaS route is often faster and more secure, because you do not need to
develop expertise in "DevOps" or hire IT and physical security specialists. This
option also provides the greatest amount of flexibility &amp; scalability– because
the SaaS provider is able to deliver more or less computing power, storage, and
bandwidth—right when it’s needed.</p>
<p>Having smaller setup costs (you don't have to grow a software delivery company
of your own) often makes this a more economical choice for many, though SaaS
will always come with some sort of ongoing fee—a price per month or year that
goes to the vendor to compensate for the time and money they'll spend to ensure
your software works properly.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-local-deployment">What is Local Deployment?<a href="https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment#what-is-local-deployment" class="hash-link" aria-label="Enlace directo al What is Local Deployment?" title="Enlace directo al What is Local Deployment?" translate="no">​</a></h2>
<p>Unlike the SaaS option, local deployment means installing and running software
on your own computers—typically on your organization’s servers.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="some-benefits-of-local-deployment">Some benefits of Local Deployment<a href="https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment#some-benefits-of-local-deployment" class="hash-link" aria-label="Enlace directo al Some benefits of Local Deployment" title="Enlace directo al Some benefits of Local Deployment" translate="no">​</a></h3>
<p>If a SaaS provider doesn't offer hosting in your country and your government
doesn't allow your data to reside on foreign servers (i.e., you're not allowed
to use things like Gmail, WhatsApp or Facebook for communicating sensitive
information) then local deployment allows you to use tools like CommCare, DHIS2,
and OpenFn while adhering to government data sovereignty regulations.</p>
<p>Local deployment also provides your organization with complete ownership of the
end-to-end system. Your IT team will be personally responsible for ensuring that
the software works, is maintained, is secure, etc. If your organization does not
already have an IT team in place, then this can become a costly headache, but
for a large organization with embedded IT experience, local deployment often
makes sense.</p>
<p>Ultimately, being able to directly hire and fire the people who are responsible
for your software's proper functioning can be very useful. It means you have
complete responsibility for whether or not the solution succeeds.</p>
<p>If you've already got the teams in place (security, DevOps, etc.) then this
option can be more economical in the long run. With a very good DevOps team,
maintaining an extra piece of software might only occupy 20% of a
full-time-employee's salary. For your security guards, if the software is
installed in the same physical location it's possible that your costs won't
increase at all. While there will be very high setup costs, over time you may
realize cost savings by running an efficient software delivery unit within your
organization that spreads its focus around a number of projects.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zandiles-decision">Zandile's Decision<a href="https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment#zandiles-decision" class="hash-link" aria-label="Enlace directo al Zandile's Decision" title="Enlace directo al Zandile's Decision" translate="no">​</a></h2>
<p>In this fictional case, data residency is a concern—her data is sensitive or
contains PII—and CommCare, DHIS2 and OpenFn do not provide hosting in the
country she's located. Zandile's organization has a large, experienced IT team
that has managed high-availability software projects for many years... they're
pros. While they anticipate that the setup costs will be quite high (around
$60000 and several months for this set of deployments) they plan on using this
software for the next 5 years and have determined that they'll recoup a
significant portion of that setup cost by not having to pay license fees for
SaaS. They go with local deployment.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="which-deployment-option-is-best-for-your-organization">Which Deployment Option is Best for your organization?<a href="https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment#which-deployment-option-is-best-for-your-organization" class="hash-link" aria-label="Enlace directo al Which Deployment Option is Best for your organization?" title="Enlace directo al Which Deployment Option is Best for your organization?" translate="no">​</a></h2>
<p>The answer is: "it depends", but if your organization has never managed local
software deployments, then we recommend going the SaaS approach. SaaS systems,
like the one OpenFn and CommCare offer, are simply going to be more secure, more
stable, and more scalable for the money.</p>
<p>Crucially, you can always start with SaaS (most tools even offer a free tier)
and then decide later to invest in the big startup costs of a local deployment
if the license fees for the SaaS feel high enough to make local deployment more
economical over the long term. After a few months or years on the SaaS, you'll
likely be in a better position to know if you want to continue using the
software for 5-10 years.</p>
<p>Should you need any help with your decision though please do not hesitate to
contact OpenFn.</p>]]></content>
        <author>
            <name>Jed Goldstein</name>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Tracked entity instances in DHIS2]]></title>
        <id>https://docs.openfn.org/es/articles/2020/12/09/upsert-in-dhis2</id>
        <link href="https://docs.openfn.org/es/articles/2020/12/09/upsert-in-dhis2"/>
        <updated>2020-12-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[tl;dr: Lots of our users want to upsert tracked entity instances in dhis2, but]]></summary>
        <content type="html"><![CDATA[<p>tl;dr: Lots of our users want to upsert tracked entity instances in dhis2, but
upserts aren’t supported by a standard DHIS2 API endpoint. We built one in our
dhis2 adaptor: it’s composed of existing APIs and a bit of logic 🤔. Now you can
<code>upsert</code> tracked entity instances to DHIS2 👍 ✅.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-bit-more">A bit more...<a href="https://docs.openfn.org/es/articles/2020/12/09/upsert-in-dhis2#a-bit-more" class="hash-link" aria-label="Enlace directo al A bit more..." title="Enlace directo al A bit more..." translate="no">​</a></h2>
<p>An “UPSERT” is a portmanteau of the database functions UPDATE and INSERT. It’s
critical to handle upserts properly when integrating systems. As of version 35
of the API, DHIS2 does not allow for an administrator to upsert tracked entity
instances (“TEIs”). OpenFn’s own
<a href="https://github.com/chaiwa-berian" target="_blank" rel="noopener noreferrer" class="">Chaiwa Berian</a> has come up with a solution
that highlights the utility of helper functions in our dhis2 adaptor. By
combining various DHIS2 APIs through an upsertTEI function in OpenFn, DHIS2
users can now perform upserts to TEIs.</p>
<p>If you’re curious, check out his implementation
<a href="https://github.com/OpenFn/language-dhis2/blob/master/src/Adaptor.js#L347" target="_blank" rel="noopener noreferrer" class="">here</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="even-more">Even more!<a href="https://docs.openfn.org/es/articles/2020/12/09/upsert-in-dhis2#even-more" class="hash-link" aria-label="Enlace directo al Even more!" title="Enlace directo al Even more!" translate="no">​</a></h2>
<p>A tracked entity instance in DHIS2 is a type of entity that can be tracked
through the system. It can be anything from a person to a commodity like a
medicine. If I am a database administrator presiding over two different systems
that are connected to one another, let’s call them “System A” and “System B,” I
would like for any updates made to the TEI of a user named “Jim Smith” in System
A to also appear in Jim’s record in System B. Before upserts came about, doing
so was difficult because of the possibility of duplicate record creation.
Because an upsert simultaneously UPDATES and INSERTS, it prevents duplicates.</p>
<p>Upserts are important and good because they cut down on the risk of duplicate
data entry and they also allow for transactions to be retried over and over to
ensure data integrity. That last bit is called “idempotency” and you can read
about it <a class="" href="https://docs.openfn.org/es/articles/2020/07/02/allow-yourself-to-fail">over here</a>.</p>
<p>Please don’t hesitate to reach out to one of OpenFn’s implementation specialists
if you’d like to learn more.</p>
<p>— Taylor</p>
<p><a href="https://openfn.org/register" target="_blank" rel="noopener noreferrer" class="">Sign up</a> to set up a project today, absolutely
free.</p>
<p><a href="mailto:admin@openfn.org" target="_blank" rel="noopener noreferrer" class="">Reach out</a> for more information.</p>]]></content>
        <author>
            <name>Taylor Downs</name>
            <email>taylor@openfn.org</email>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Product News: Enhanced Scheduled/Periodic Job Control]]></title>
        <id>https://docs.openfn.org/es/articles/2020/07/14/cron-is-better-than-a-timer</id>
        <link href="https://docs.openfn.org/es/articles/2020/07/14/cron-is-better-than-a-timer"/>
        <updated>2020-07-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Hi all, this is a quick one from the product team at]]></summary>
        <content type="html"><![CDATA[<p>Hi all, this is a quick one from the product team at
<a href="https://openfn.org/" target="_blank" rel="noopener noreferrer" class="">OpenFn</a> — we've made a major upgrade to how timed/period
jobs work.</p>
<p>In the past, if you weren't using OpenFn to drive some real-time (or
"event-based") automation, you'd need to set up an "interval trigger." Like the
photo above, this was essentially a sand timer. Set your trigger to <code>10</code> seconds
and your job fetches data from DHIS2, some regional public health data set, or
whatever, then cleans, transforms, and loads it into some other system.</p>
<p>For the most part, this has got the job done for the last 5 years, but as our
NGO and government clients came up with increasingly specific requirements on
not only how often but <em>when</em> a crucial job gets executed, we began finding
ourselves creating little customizations for them on a once-off basis. We're
happy to annouce that as of <code>v1.75</code> (released today), you can now schedule jobs
to run based on <code>cron</code> expressions, giving you incredible control over when your
tasks get executed.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scheduling-is-better-than-timing">Scheduling is better than timing.<a href="https://docs.openfn.org/es/articles/2020/07/14/cron-is-better-than-a-timer#scheduling-is-better-than-timing" class="hash-link" aria-label="Enlace directo al Scheduling is better than timing." title="Enlace directo al Scheduling is better than timing." translate="no">​</a></h3>
<p>Using <code>cron</code>, you can choose to run a job every minute by typing <code>* * * * *</code>.</p>
<p>Or maybe you've got a batch sync that you want to take place while your users
are asleep—why not run it every night at 11pm with <code>23 * * * *</code>.</p>
<p>What if you've got to submit reuqests for medical inventory only during the
onset of flu season? Simply type <code>0 0 1 2-4 *</code> and your job will run at midnight
the 1st of the month, from February through April.</p>
<p>You can still run jobs at the click of a button and create timers with
expressions like <code>*/10 * * * *</code> for "every 10 minutes", but scheduling with cron
gives OpenFn.org users so much more control over how they run their
organizations. (And that's a good thing.)</p>
<p>If you're keen on learning by doing but don't have an OpenFn account yet,
<a href="https://www.openfn.org/signup" target="_blank" rel="noopener noreferrer" class="">sign up for free</a> or mess around with cron
expressions at <a href="https://crontab.guru/" target="_blank">crontab.guru</a>,
a brilliant site to quickly build complex cron expressions.</p>
<p>That's all from product for today. Speak soon.</p>
<p>Taylor</p>]]></content>
        <author>
            <name>Taylor Downs</name>
            <email>taylor@openfn.org</email>
        </author>
        <category label="annoucement" term="annoucement"/>
        <category label="tips" term="tips"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Allow Yourself to Fail]]></title>
        <id>https://docs.openfn.org/es/articles/2020/07/02/allow-yourself-to-fail</id>
        <link href="https://docs.openfn.org/es/articles/2020/07/02/allow-yourself-to-fail"/>
        <updated>2020-07-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Hi all, this is a very short post with a simple message: design for failure.]]></summary>
        <content type="html"><![CDATA[<p>Hi all, this is a very short post with a simple message: design for failure.
Even if you've never heard of
<a href="https://www.microsoft.com/en-us/sql-server" target="_blank" rel="noopener noreferrer" class="">MSSQL</a> (or
<a href="https://azure.microsoft.com/en-us/" target="_blank" rel="noopener noreferrer" class="">Azure</a>, or Microsoft?), I want to talk for
one moment about the importance of upserts and a funny developer term called
"idempotence."</p>
<p>We just extended our
<a href="https://github.com/OpenFn/language-mssql" target="_blank" rel="noopener noreferrer" class="">language-mssql adaptor</a> with a custom
function that allows upserts (an <code>upsert</code> is when you either insert a new record
or update an existing record based on some identifier). Before, you'd need to
write something tedious like:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">sql</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">query</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token template-string string" style="color:rgb(195, 232, 141)">MERGE my_table AS [Target]</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token template-string string" style="color:rgb(195, 232, 141)">          USING (SELECT '8675309' AS some_unique_id, 'writing_blog_posts' AS skill) AS [Source]</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token template-string string" style="color:rgb(195, 232, 141)">          ON [Target].some_unique_id = [Source].some_unique_id</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token template-string string" style="color:rgb(195, 232, 141)">          WHEN MATCHED THEN</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token template-string string" style="color:rgb(195, 232, 141)">            UPDATE SET [Target].some_unique_id=8675309, [Target].skill='writing_blog_posts'</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token template-string string" style="color:rgb(195, 232, 141)">          WHEN NOT MATCHED THEN</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token template-string string" style="color:rgb(195, 232, 141)">            INSERT (some_unique_id, skill) VALUES ([Source].some_unique_id, [Source].skill);</span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>whereas now you can simply write:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">upsert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'my_table'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'some_unique_id'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">some_unique_id</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8675309</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">skill</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'writing blog posts'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>For an operation to be idempotent means that it can be repeated time and time
again without producing an unintended result. This is SUPER important for
creating S3 (<strong>S</strong>ecure, <strong>S</strong>table and <strong>S</strong>calable—more on that
<a href="https://openfn.org/trust" target="_blank" rel="noopener noreferrer" class="">here</a>) integrations because it provides you with two
"get-out-of-jail-free" cards.</p>
<ol>
<li class="">
<p>If a destination application fails, if a connection times out, or if (for
whatever reason) you're not sure if the <code>job</code> was completed (say... making a
payment to CHW) then an idempotent operation can be RETRIED without fear of
making a double-payment.</p>
</li>
<li class="">
<p>If you make some change to how your <code>job</code> works, make some modification to
one of your destination systems, or just because you want to be <em>extra extra
sure</em> that all the data in a 9 month survey made it to the national public
health reporting system, you can <em>REPROCESS</em> every single message that's come
through OpenFn at the click of a button, without having to worry about
duplicates.</p>
</li>
</ol>
<p>So... when clients let me mess around with their jobs, I <em>always</em> recommend we
design for idempotence. It's common sense when you're passing messages between
two different systems that are bound to evolve, go offline, have a bad day, etc.</p>
<p>— Taylor</p>
<p><a href="https://openfn.org/register" target="_blank" rel="noopener noreferrer" class="">Sign up</a> to set up a project today, absolutely
free.</p>
<p><a href="mailto:admin@openfn.org" target="_blank" rel="noopener noreferrer" class="">Reach out</a> for more information.</p>]]></content>
        <author>
            <name>Taylor Downs</name>
            <email>taylor@openfn.org</email>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[To Automate or Not to Automate? Ask Yourself These 3 Questions.]]></title>
        <id>https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask</id>
        <link href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask"/>
        <updated>2020-06-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Automation can save time, unlock critical resources, and enable scale–but it]]></summary>
        <content type="html"><![CDATA[<p>Automation can save time, unlock critical resources, and enable scale–but it
typically requires investment to set up. Wondering whether you should automate
your processes? Ask yourself these 3 questions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="our-partners-use-openfn-automation-solutions-to-drive-efficiency-and-scale-their-processes-delivering-integrated-digital-systems-that-work-better-faster-and-together">Our partners use <a href="https://openfn.org/" target="_blank" rel="noopener noreferrer" class="">OpenFn</a> automation solutions to drive efficiency and scale their processes, delivering integrated digital systems that work better, faster, and together.<a href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask#our-partners-use-openfn-automation-solutions-to-drive-efficiency-and-scale-their-processes-delivering-integrated-digital-systems-that-work-better-faster-and-together" class="hash-link" aria-label="Enlace directo al our-partners-use-openfn-automation-solutions-to-drive-efficiency-and-scale-their-processes-delivering-integrated-digital-systems-that-work-better-faster-and-together" title="Enlace directo al our-partners-use-openfn-automation-solutions-to-drive-efficiency-and-scale-their-processes-delivering-integrated-digital-systems-that-work-better-faster-and-together" translate="no">​</a></h3>
<p>To date, we have worked with 43 social sector organizations that operate across
sectors–from health, education, and agriculture, to livelihoods and emergency
response. Over the last 6 years, OpenFn has been implemented worldwide for a
wide range of use cases, including building real-time data monitoring systems,
streamlining data cleaning pipelines, securely exchanging sensitive information,
and automating routine processes like uploading indicator results, sending
SMS/email alerts, making mobile payments,
<a href="https://openfn.org/solutions" target="_blank" rel="noopener noreferrer" class="">and more</a>.</p>
<p>By connecting any app, OpenFn can integrate and automate all apps within a
digital ecosystem. However, a question that we frequently ask our partners is:</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="just-because-you-can-automateshould-you">Just because you <em>can</em> automate—<em>should</em> you?<a href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask#just-because-you-can-automateshould-you" class="hash-link" aria-label="Enlace directo al just-because-you-can-automateshould-you" title="Enlace directo al just-because-you-can-automateshould-you" translate="no">​</a></h4>
<p>While integration and automation have the potential to enable scale and save
time and money (we’ve learned this from our
<a href="https://docs.openfn.org/es/articles/2020/06/24/openfn.org/clients" target="_blank" rel="noopener noreferrer" class="">partners</a>), solutions require investment to set up
and maintain. These costs sometimes outweigh expected efficiency gains and
service outcomes. Therefore, when evaluating the cost-benefit of investing in
automation and integration solutions, we at Open Function Group typically ask 3
key questions.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-security--will-automation-limit-the-exposure-of-sensitive-data">1. Security — Will automation limit the exposure of sensitive data?<a href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask#1-security--will-automation-limit-the-exposure-of-sensitive-data" class="hash-link" aria-label="Enlace directo al 1. Security — Will automation limit the exposure of sensitive data?" title="Enlace directo al 1. Security — Will automation limit the exposure of sensitive data?" translate="no">​</a></h4>
<p>Can the exposure of sensitive data be limited by integrating with secure API
endpoints (rather than relying on human beings to interact with those data, for
example)? Or by automating a data cleaning process?</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-accuracy--will-automation-increase-data-accuracy-and-reduce-data-entry-errors">2. Accuracy — Will automation increase data accuracy and reduce data entry errors?<a href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask#2-accuracy--will-automation-increase-data-accuracy-and-reduce-data-entry-errors" class="hash-link" aria-label="Enlace directo al 2. Accuracy — Will automation increase data accuracy and reduce data entry errors?" title="Enlace directo al 2. Accuracy — Will automation increase data accuracy and reduce data entry errors?" translate="no">​</a></h4>
<p>Can the process take place more reliably by limiting the opportunity for human
error (in automating data manipulation or simple algorithmic work, for example)?</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-speed--will-automation-increase-the-speed-of-impact">3. Speed — Will automation increase the speed of impact?<a href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask#3-speed--will-automation-increase-the-speed-of-impact" class="hash-link" aria-label="Enlace directo al 3. Speed — Will automation increase the speed of impact?" title="Enlace directo al 3. Speed — Will automation increase the speed of impact?" translate="no">​</a></h4>
<p>Can the process be done more quickly via automation and is there value in having
it done faster? (The answer to the first part is almost always yes, but
sometimes there's not actually lots of value generated by doing something
faster.)</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="if-you-find-yourself-answering-yes-to-these-questions-it-may-be-time-to-consider-automating-critical-processes-at-your-organization">If you find yourself answering “yes” to these questions, it may be time to consider automating critical processes at your organization.<a href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask#if-you-find-yourself-answering-yes-to-these-questions-it-may-be-time-to-consider-automating-critical-processes-at-your-organization" class="hash-link" aria-label="Enlace directo al If you find yourself answering “yes” to these questions, it may be time to consider automating critical processes at your organization." title="Enlace directo al If you find yourself answering “yes” to these questions, it may be time to consider automating critical processes at your organization." translate="no">​</a></h4>
<p>Tasks that meet these 3 criteria, take a lot of time to complete or are very
repetitive, and/or involve moving data between apps are typically great
candidates for automation.</p>
<p>If you find yourself answering “no”, then it may not be worth the investment in
automation... at least not yet. This is especially true if these processes are
still in flux or require a lot of human involvement to complete. That said, now
may be a good time to refine your existing workflows, think about how your
processes might change at scale, and consider what <em>new</em> processes, services, or
outcomes could be unlocked by automation.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="delegate-your-busywork-to-openfn-and-try-it-today">Delegate your busywork to OpenFn, and try it today!<a href="https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask#delegate-your-busywork-to-openfn-and-try-it-today" class="hash-link" aria-label="Enlace directo al Delegate your busywork to OpenFn, and try it today!" title="Enlace directo al Delegate your busywork to OpenFn, and try it today!" translate="no">​</a></h3>
<p>If you want to try out automation for your organization,
<a href="https://www.openfn.org/signup" target="_blank" rel="noopener noreferrer" class="">sign up</a> for OpenFn, free of charge. Check out
<a href="https://docs.openfn.org/" target="_blank" rel="noopener noreferrer" class="">our documentation</a> and
<a href="http://www.openfn.org/" target="_blank" rel="noopener noreferrer" class="">website</a> to learn how to get started.</p>
<p>Having trouble setting up your first automation "job"? Email us at
<a href="mailto:admin@openfn.org" target="_blank" rel="noopener noreferrer" class="">admin@openfn.org</a> for support. Our team is always
happy to assist and help you evaluate the total cost of ownership of automation
solutions.</p>]]></content>
        <author>
            <name>Aleksa Krolls</name>
            <email>aleksa@openfn.org</email>
        </author>
        <category label="tips" term="tips"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[How Information Is Organized... In Organizations]]></title>
        <id>https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized</id>
        <link href="https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized"/>
        <updated>2020-06-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Does your organization's information have an underlying structure? Try this exercise using boxes and crow's feet.]]></summary>
        <content type="html"><![CDATA[<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="does-your-organizations-information-have-an-underlying-structure-try-this-exercise-using-boxes-and-crows-feet">Does your organization's information have an underlying structure? Try this exercise using boxes and crow's feet.<a href="https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized#does-your-organizations-information-have-an-underlying-structure-try-this-exercise-using-boxes-and-crows-feet" class="hash-link" aria-label="Enlace directo al Does your organization's information have an underlying structure? Try this exercise using boxes and crow's feet." title="Enlace directo al Does your organization's information have an underlying structure? Try this exercise using boxes and crow's feet." translate="no">​</a></h4>
<p>This article was originally posted by Taylor Downs, Head of Product, on
<a href="https://medium.com/@taylordowns2000" target="_blank" rel="noopener noreferrer" class="">The OpenFn Founder's blog</a> as "The power
of crow's feet."</p>
<p>It’s Saturday Morning in Cape Town and I’ve just spent an hour talking about how
a non-profit is organized. I thought I was getting into a technical
discussion—I’ve been doing system architecture discussions for years—but what we
ended up talking about was how this NGO thinks.</p>
<p>This engagement is largely about mapping an already existing “people &amp; paper”
based system to technology. Vera Solutions will build a system for this client
using Open Data Kit for field data collection and Salesforce.com for the
management “back-end”. Because we’re not explicitly being asked to help redesign
processes at this organization, the client is “telling us how things are”, then
expecting us to create a relational database model that facilitates
business-as-usual, only in a more efficient, digital way. Seems reasonable.</p>
<p>This organization runs multiple programs focusing on a handful of strategic
objectives. They coordinate various activities in their target communities and
report on those activities against numerous (sometimes overlapping) indicators.
Sound familiar? As we get to the 3rd explanation of these programs, and the 11th
iteration of the system schema, it hits me…</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="drawing-a-relational-object-model-sounds-technical-but-its-actually-an-exercise-in-clear-communication-and-everyone-can-benefit-from-it">Drawing a "relational object model" sounds technical, but it's actually an exercise in clear communication... and everyone can benefit from it.<a href="https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized#drawing-a-relational-object-model-sounds-technical-but-its-actually-an-exercise-in-clear-communication-and-everyone-can-benefit-from-it" class="hash-link" aria-label="Enlace directo al Drawing a &quot;relational object model&quot; sounds technical, but it's actually an exercise in clear communication... and everyone can benefit from it." title="Enlace directo al Drawing a &quot;relational object model&quot; sounds technical, but it's actually an exercise in clear communication... and everyone can benefit from it." translate="no">​</a></h3>
<p>When we—human beings—wrestle with complex problems (like managing lots of
programs, other humans, community stakeholders, etc.) we have the capacity to
trick ourselves into thinking that we have wrapped our heads around a system
(for clinic registration, for after-school education, etc.) when, in fact, we’re
engaging in mental hand-waving and are simply ‘papering-over’ sections which are
secretly not just complicated, but totally incongruous with other parts of the
system. We can make ourselves believe that our logic is sound because we want it
to be sound, when in reality the organization might be held together by good
people, not good, clear, defined processes. By learning a couple of key
concepts, it’s possible for non-technical people to articulate their thoughts
clearly using “boxes” and “crow’s feet” and see whether or not there is an
underlying structure to their organization’s information.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="by-forcing-yourself-to-reduce-complex-systems-to-sketches-containing-only-two-elements-youll-be-able-to-detect-important-conflicts-and-confusions-in-how-you-think-about-your-organization-that-you-might-otherwise-miss">By forcing yourself to reduce complex systems to sketches containing only two elements, you’ll be able to detect important conflicts and confusions in how you think about your organization that you might otherwise miss.<a href="https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized#by-forcing-yourself-to-reduce-complex-systems-to-sketches-containing-only-two-elements-youll-be-able-to-detect-important-conflicts-and-confusions-in-how-you-think-about-your-organization-that-you-might-otherwise-miss" class="hash-link" aria-label="Enlace directo al By forcing yourself to reduce complex systems to sketches containing only two elements, you’ll be able to detect important conflicts and confusions in how you think about your organization that you might otherwise miss." title="Enlace directo al By forcing yourself to reduce complex systems to sketches containing only two elements, you’ll be able to detect important conflicts and confusions in how you think about your organization that you might otherwise miss." translate="no">​</a></h3>
<p>If you can’t diagram the information structure in your organization using boxes
and crow’s feet, it’s a smell that something isn’t quite right (or at least that
something isn’t easily scalable… more on this later!). Let me show you the tools
in the toolbox and then wrap up by waxing poetic on people, processes, and
technology.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="boxes-and-crows-feet">Boxes and crow's feet<a href="https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized#boxes-and-crows-feet" class="hash-link" aria-label="Enlace directo al Boxes and crow's feet" title="Enlace directo al Boxes and crow's feet" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,UklGRjwZAABXRUJQVlA4WAoAAAAQAAAAfwcA1AIAQUxQSAEKAAABHMVt2zjS/mvner9XREyAJ0kedPYnkwcjbTMd11BAA/eTjvt/dtvorzwwcTXddNFUARshYmu2JLAPA2ZriM2+zcYt5whi4SNIcJfd9Qlo3sMnCPZlpHYV4FtwOFTolQEBFBIREyC32f8n7rD8s37X0bmzrnTA2WxyGzizBatcBxILMANbXH2SA5XjBOn3D+iJiAnI+J//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//pXT28rVSbw6CSr1+OZvgm73Sb6PF+4PgInqrX80m916cmMU3Fz/8chD84eKbhTl5MbE3O3l3dnV98+H2tk3+t7cfbq6vzt6dzCb1Zq/M2fXd/V8Pnw6CD3/d312fmVezKb2XenF19/GRg+Hjx7urhX45pff67TfX94+fDwefH++vv3n7ekpPRRc3f3FQ/OvmIlKTeosfPjwcFh4+/LCY1Hvz/pfbT4eFT7e/vH/zlS1/9ny+jA/rZP8wvnz2/Dnd97Ia+q5pk3zT9cPqZfpvHJZ1dXmd5C+rejmM038Pfb0oi6PjBH9UlIu6f5j+W3dVmYc4S/Ax5GXVrY8ANJdFyBJ9KC6bKYD2+iimunh03U4DHM9S3eyY//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP//iP/yYZjmKqi0fTAM1lEVJdKC6bKYCuKvMQZwk+hrysugmAh75elMXRcYI/KspF3T9M/43Dsq4ur5P8ZVUvh3H672U19F3TJvmm64fVy/Tf58v4sE72D+PL53Tfbc+fwk+3X9miFj98eDgsPHz4YaEm9aKLm78OC3/dXESTeq/ffnN9//j5cPD58f76m7evp/Re6sXV3cfHw8Hjx7urhX45pTd7Zc6u7+7/evh0EHz46/7u+sy8mk3pyezk3dnV9c2H29s2+d/efri5vjp7dzKTaf0XJ2bxzcUPvxwEf7j4ZmFOXsjU/uyVfhst3h8ED3d38rCR/X9/9vK1Um8OgjFubW5k/M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///M///H8cMk7TVD9z9JEYNU9XqzRN53qIA+xzJlo12Z7S51VVVfnxiLihf2OeTWp5CbCvarruaERBcPY8UqeX+PeVOy6RALg8y5xrae3zqKZ/X+kkK45IALWWHT9fANoyrveXiNjjERZQsus4yzL9LIldEYmIOw6TgZPn+lcgqfl8voL1vDfMzv1qFypKq6aqTpWImc/nc9NR867yqbk3QM29qqPTy8tUh+nTy6ppqtVS7TNzelk1m+p8OTazqjbry3S+A5NWa9bVqQnT867xmXlXT/pFDFd9qqHX7sA29J6Lo7sW0XitJ8Lfp/FbEV3RbU4D1Ir+Jt1XdtXQX9kxmTW9az3AVPSvdECGv+gU+JMpv2wHps+wO3VO6Lr2IBKHJYPigKShN+8xDcFrtZcsA814YkKb06CY4Cbqcz11Z9tTTvnpsh2wLSSw2O6sBCgzmxQtvXUiooo2RJftAFW0vgJgm+Ut4DMN0BZZFGclwFrto7hT51lkkwJo1FgagDqP47wGyAJigG2R2KzYAsQ9pvbUthPVntpM+XkzcLJ7t4MIqI10VenJpN/1eYO8wAZoExHRNbVHN0ChxGsdcLmPJCtiLX7TQjYWoI3Fm7SA6YkAMvGqHGiMTyQGGiPeqIHWyPT/2FQDTklvNjqg1tJVVnkKIJbAAoj3UXgGbjxbLb26hXXPFloj/RZY94lpoTGdGGiNHF3IodUSWI+vUBJugFxCVQ2bfWehGY+VwAhIPAkQSWgGxH1iWiAWiYHWyPEFB5mE2tFlMjSBVsINoPeX1T5GU0hwCc5TQynhDsoAMS0Qx0Br5AgDYINkbIkMdrBJBwLxXrKXFd71ekw2zELT0YAdkAAhYlq8rZEjDBpQYW5kdljNTrM9ZCsGjkaFaUCJiAXUAAuoEDFtp1ZyjMEA8r/CbvP9EzN4NDIQsL5GhgI2SIpOIkcZBLBh7VOrYeuG758IwMVWiSgbFU/K+BiiARNU4I2PM9QQB2memoNSnvDuzNxrRtNCa6XfjsmEWUBExABmQARIaAHUNXB+lKGAjQopn1wC/B/VeLOxWMDI04jDYnAdaSEekEMdUgC1UjWwOsZggDwg4kvocWgg///ReFs1lgxKCY3G1KgQ1UDuKaBRQQbIAwqgViKqBlZHGCQDzpUvbnaQ9JlmHFIA8YD4VD252JfJ04ibMXEZUkKrPRqogmpodV8B1EpERNXA6giD1MB6aURHl3hDMqh6koaR6Baa0xBdQfbkSs9WRmuh0T3qnO54OFc+tQIS8efAperRFZCJX62BQvyqBlbTfnqZpmkFm9QfqT59mno3sEq9UZ+pCW23YRGwEhG1rPCmS+Uxp2maAqvUbwLEAGwi3THLCiAfgV6m3Q1UaXepwlpPPB7Vwtp2zHmDN03nX0gv05WHTaREVLQBnPSqGmiWRkTMaQOU4k/WQJMuVUctzxvg8nTKb8vgom/LDuMeUXlAbVyYlABVQ7elW3Qsw02ARC3eao3fqRHUDC6DLN2tjDiiu17jbfFGX8bRrWu6mw3dWvWJqvFWeJ3yFfQWnYLe/ChBu4usT8QWdcfFIkNUS78znrIT7yAKEeM8vdtYxrgd5oJyjx2T5B1/m0W++MvUHqeKjj+TYJV7/Ln0BpQddwzAFm5grvts4QYnKqRrtXSHiCo9bWlFnHMu1x1JShdeJjLQlj3bIpJx2twNzE1Q3XEybuN8dabEOOdcmciXNblzzuUitvRsSyNDTdl62kJLv8qcN9cdnTtvpib8vsRBImIja+UJzuen83mefYmKrh2ZiNgoslqeorJRpGS31sZ2O5u83EI8ZP/HnVKO5eaAfm4UHX0sJwacPDdboJBjt3bePa2A1jw7gFYfudENga2RZ2dMG8mR24TA2sgxbuV8rUvkq/+/+p//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+Z//+f+/+ABWUDggFA8AAJBeAZ0BKoAH1QI+bTaZSaQjoqEhnCgAgA2JaW7hd/0A9Pej+VZF+FWORY79SP989LroP88Huy/rP7s/k4/gD+AfgB9fvf4BaYLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixYsWLFixTEXyzZY/AVjaISDgSx+ArG0QkHAlj8BWNohIOBLH4CsbRCQcCLB+cmTJkyZMmTJkyZMmTJkyZMmTJkyZMmTJkyZMmTJkyZMmTJkyZMmTJeTap1YponnS8AzzpeAZ50vAGFNidOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnRIVIsCiFt/XGjcx4Nt/XGjcx4Nt/XGjcx4Nt/XGjcx4Nt/WZpLRCFqKPHjx48ePHjx48ePHjx48ePHjx48ePHjx48ePHjx48ePHjx48ePHjwzRSC2c4cOHDhw4cODa53psTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06JCrBwu1IkSJEiRIkQGDV8aRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkR9hVgxQQkSJEiRIkSIDBq+NIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSI+wqwYoISJEiRIkSJEBg1fGkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEfYVYMUEJEiRIkSJEiAwavjSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiPsKsGKCEiRIkSJEiRAYNXxpEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRH2FWDFBCRIkSJEiRIgMGr40iRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIj7CrBighIkSJEiRIkQGDV8aRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkR9hVgxQQkSJEiRIkSIDBq+NIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSI+wqwYoISJEiRIkSJEBg1fGkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEfYVYMUEJEiRIkSJEiAwavjSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiPsKsGKCEiRIkSJEiRAYNXxpEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRH2FWDFBCRIkSJEiRIgMGr40iRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIj7CrBighIkSJEiRIkQGDV8aRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkR9hVgxQQkSJEiRIkSIDBq+NIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSI+wqwYoISJEiRIkSJEBg1fGkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEfYVYMUEJEiRIkSJEiAwavjSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiPsKsGKCEiRIkSJEiRAYNXxpEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRH2FWDFBCRIkSJEiRIgMGr40iRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIj7CrBighIkSJEiRIkQGDV8aRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkR9hVgxQQkSJEiRIkSIDBq+NIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSI+wqwYoISJEiRIkSJEBg1fGkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEfYVZLVjaISDgSx+ArG0QkHAlj8BWNohIOBLH4CsbRCQcFrJi3w4cOHDhw4cOHDhw4cOHDhw4cOHDhw4cOHDhw4cOHDhw4cOHDhw4cODWa21I8QkHAlj8BWNohIOBLH4CsbRCQcCWPwFY2iEg4EsfgKxs+FSGxz+8KFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFChQoUKFCacrdSGrXEIfEJQWAr4CsbRCQb476BSloCKJDASLRqSWJfYNCrU/jJAG70eN8d9AyRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSF1FTsChRpe5zBLJl5WcOgeosbpAz5WfgPImQrhnYmovp3OFRcETR7WbiP2SijC2O4b1JLBcDVACE6dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnTp06dOnI2kW+S6vre5HnRFzU/m5q/14Mo2Zxi9SaXTa3B8c4fLBIoB+UTGO8phf0Fs+VOfH9ZceFbBjF7i1kkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkbi0ipv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/fv379+/bgAAP79bK/5lhyUTf/jnx346eQAAAAAAAAAAAAAAAAAAAAAAAAGTsf0Bt/7G3WO2u4OjrREa0RGtERrREa0RGtERrREa0RGtERrREa0RGtERrREa0RGtERrREa0RGtERrREa0RGtERrREa06A1v6ShRAAOHKdevAY6/g4LGnPLpgmRXqJt0qKbWDcp+tOhtuWYqWp00RXPLMpRza4j0hr/WUcg22iCfGoUnEWINDISsKlqgPtYS8emNuzBB7e3mIXIIQq87OQAAjASlKlDgq15pIAAAxOrVMQPPBGwAAYJywx4REAAABNqwTENEAAAdysEhj1AAAA61YJI+bAABvKwSR82AADeVgkj5sAAG8rBJHzYAAN5WCSPmwAAbysEkfNgAA3lYJI+bAABvKwSR82AADeVgkj5sAAG8rBJHzYAAN5WCSPmwAAbysEkfNgAA3lYJI+bAABvKwSR82AADeVgkj5sAAG8rBJHzYAAN5WCSPmwAAbysEkfNgAA3lYJI+bAABvLAAfLmRZfPKbRWltwE+mLAy1H/R/0f9H/R/0f9H/R/0f9H/R/0f9H/R/0f9H/R/0f9IExoHZGfUu+JSAAClAAHaxmunEErymmAFmNSPTOQdEuAAAAAAAAAAAAAAAAAAAAAARZtgdVThS/77O3SSkLNgjtYRTjwnzJYBBAO/0KyRhFOSOzH6+l3ic944/ocxITUuknJ03tO2I2skCNJzy7vmsecUlE3B0eW2v361tIATY2JAcLbtYcjpDrxq4YWbHYcEQuofvfUvHQCopcHtzbpcpEwHuHW4uKeFfkVui8IPbnhdQsXGJ/FzzgQdVLgAzmArhSzHnxVF1QU///AHFZtUPG+jD9sKagLNN6yUQecX4uPdsczWpAJ/WNjQ8hAdo3Ukg5JkAAYDQwPbdbsAbCHqyHX5fNQrlQXN+xBh8XF4CPSPIdFCF1ElWRwRLk7oItzKplTw41vZ2iisnT5XYdFr4EWC5AzE+BFguxK6FATWnyR8XpInzXUMrIoyUgQcdWe5WLPs1nl90Ypp3aq58LVuhudVWFixmrMBgZO85yi74Q8mFf/BetllG6os5PS1Xr4v8yZxleD2yAfpcNbT6T5RxwpbC/vvVVl8/Xtr9zL4+DKGjA7UBKDAr1dpkT+8dpG6/JdW5PWZ0lMtkCxfB35n233pDd5kJZPyvhWQPagP3snXA50aMItshDqFdzJsfrfRjrSB2kVev8D+ipRkerkBKxvYHeUyElX7R2ymLTTFqyzXMAAADc5DN3UBv9OhO8n4fqdOtnt/Kb/7//gP/8+C792hIVKyKCT36aN/NQwmJQST2tPwRXgzWoFyY/37TwFy6UMARIyV3c+2XDnhKqSnuKAAAAAAAAAAAAAAAAAAAAAAAAA" width="1920" height="725" class="img_ev3q"></p>
<p>The box is my favorite. It represents an entity in your data system. Entities
(like <code>teachers</code>) have attributes (like <code>name</code>, <code>phone number</code>, <code>date of birth</code>,
<code>gender</code>, etc.) Some people like thinking of entities as simple forms. The
“Teacher Registration Form” will ask for the teacher’s name, phone number,
gender, etc. These are the fields on your teacher entity. By submitting one of
these forms, you’ll add a new teacher to your database. If you’re an Excel
person, the attributes are columns in your <code>teachers</code> table.</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,UklGRpAaAABXRUJQVlA4WAoAAAAQAAAAfwcAbQIAQUxQSHIKAAABHMZtGzmS+i97c969+0TEBPhbBlQSiPTVbQ0N3Fi6tbWFkaTfw8TDFCYubssVvgIgAUUgUiAIKQ8dknuGENRQtT1OzdmImAC1jaRGZPRfMQWQPfZExAQE//M///M///M///M///M///M///M///M///M///M///M///M///M/8das5z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+4z/+43/+53/+53/+53/+53/+53/+53/+53/+53/+53/+B1W/LIv+zZhJ/ytGTUuMy7JMuicB9tfiNu67+1tKe9V8FO2WbV2Wn0l9/fIXz4f5Lemd5039JVG1H0QtNO7q9oo0h1+Svmi99F/RVpPPqTLNtpnb/ek0A6Q1hJQKxf6OVAaSUyI2AfmvKFTKB9mAkkJYU4JTNTshLycga3nx7yUAXuozsP4NzZX0OSwQlXxKwH5xsoCSV/sQgv6NqAuCPK/A35CtbJ8jQJbP+eUpQJLftoWiGgQwf0G6Ej5HgvAPBwNBWhPMf0FS8d/m1DRNEfL02GanunqFcst+7fuPEjHTNE3mpqa7qqmp2qCmqrrpZdsW3aZ/tv269ujVKOK8asqfwfxs+3Xsqx/MrHu+9nUxImIa9FSt2GWPP6pN+bhf1x6dalBTtUVPd9123uwr1M+2H8e+/ijRahA9TVOGONVNj1v3g33z+gX6Z9s59nVqU9M0AT/T81clR796UheP9gX24nGVxD2LaKq24qg/aepWRO/cr58GFXm+llE6FWDfzcaL590OZHeed32yPXjqImIP7odtCRePh3/Q1K+nSH1tSjfdpyLPxwZ6iJXu3OQOHq9FdajI824bLP3HN6XwAvNkeJ1aac25gohvm7t8w3zxuD6Yi+as3mCFIm9u6TTDzPReD/FJrTy7B5Vp3lTFP2Ae0kNqWm/SbTKdfojUV1oizdk0mYvm5Wl+AeqLkt5KxxmlMZ4v2wC2YOdYeMyziIqlRW+lQ8VSiwBnWAtQMxdQYnA+bABZDWeA8G7+ltfg7ByBSw0SANI6Wx9ShQez1VQGSCECqaYyQArOhwSQ1U3FcjuDPNpUSbYpALmvAOc6WxtiJQxhU0+eGyJAXr0LWwEu02AuoGzBzmsGiA96Kz3nLN+WAyR5fXqBA7KRu9oqQZ7TU7WpChxAmUVEZ3JFX0BUUrUJ2EYzF5zy9iF6LXVTIIzhgOKkbnPT3QIHcFoRsYW1loFspGpPIN5ENKCkfYMg7Q7YugwQpa7iMNUEQfpXoDip6gRk9aAvYFNSnQvga1XAyvfm0dQFScljGA7IWu7KqkoEvDRGwI9lLijm/doDpDFOOLU8q/wKICq5aytVD0R5VhvgKlLA3uyy/FQKuA45wXUFyNJ4vp0GspLnAKwPEfDyrE+49JeuFYqWxjxeVNJugFVaVYZjKHMBTj6rhWuIGbDS6l4zS2+BLK2qwFHbINw2QIuIBlSPDk5ecbSsYN5rg6KlNQG6YoBVWi0Qv3QlCNJqhwvSO0ORdgPogcwFePmcVtcYIkGS9kLqKk56HWCaxAKmEiCJiAaYRcTBKQMaIHv1oGYvb6WAWZpVgbmywqmaJMD1pQuwTTLaLN0JjqUT8OOYC/DyGe22U815FMB12KC7jHRHSNJZIFQsXCIy37KIrBBHkMj92uMyaRn7JR6Qzg1S5YQg7QawX7k0oNrSYLYv89IwjLkALx/R7nSOYADT0XuT/gRrT4K1IoARyTeMSIJ5CIm3el7UmwXIPQFyBXAdArivXAaQj8Jr11HMBXj5iJ7uESwgbzH3BEi1DF40UGAVAcwYYrfyAJd5u63HAk+mJ0H4yiWAbSvvluFM/aO4C/DyER1A8laJKOvihws9EbbaClFmOGc4xAAyrrZzSCfAZd4t9XgoT7bnhPk7VwbfpHm3BJu8qQeKlRebqWqGKVCsPNtBNGDH22DryRBqHg7JsGrAzJAGqpsV2N9qhqtnhVQpMHcowH7ninColu3tZuBNPFCMvDpTDaNYwMhwUsCPN8PRoQBX04ABjGRYI6wjqLjpBhEPmHcygO7IsFY2iB0OUN+5DLA2OP6EHkMD61sEoBh5taZa1CgBNml1o2xwqA696D+mgdC2AaomBRKcIjMcGdwIDmKTnODeSU7Y22bAVTxgm9QBWb5zSQBWVfPXC+Ync40hEfAd/kcNEIFi5NmaDl8L8h7+GsUCa5u9OP6YRMC2OGCVx437KqKp6hEC5CYF2LfyQGjRF2Spn3CplhVw37okA9kb0W6j2hJgf5gvBtEFrp8WvUP4cxHISp49uLatcsqwFi79oFbuQ0gCVvWkFuD8c7rA5Z7mC4p6ChUjIul2yiD4lgjIW0kGlid7APbBAdk8qBVI0pogNqhl91+TtF+WZYdjqTv1pH+W6gFxqbonk2ktZ5sDoogov1NdvKqYn2VZgLjUTYMYgMPpm/E7wPrHLMC6NO4Q2krFj6MKZHsz60V1WaYBdAEOp29mvQBczS7LEoGl/qMbxANskxJR0w5g5NneThERf9uGYXeq4g9gHcL8LMtywL7UpwZTgOy0iNgVYJbnCLBMIqLdAZyqaQUWdTN+A66vSSfd8enkhf5B1NqQTWqTDWC/uBfu8WbpNw3iCtU9U09qjN42y/2UgR33nKkWqu7PiSlU94NqMVINdF8t4qkeB/fipfUWbuo2j3Pf94t7khE1/f5JTOF+ZaqrtEaq+eJ+GmlWGeDYD+rxC1R5RXgSsTHfkhfpUYXnZCrbzb/AtYhJlcfTy5//Y2vFjiTrrV6Cq/kBRKdKvQQl9diHahF73uqnkeYM6JtsgB3CwVl5jOrtRKdbvXhpn8utnpR0mkLrOcvXZBtT56qfbEzds2q5Wy33HlFbpWxWJKWUVn2TeUvt2yyddns4o5MRTXqhb8q3JGObVMtBiUkppW2WMX16SF4aVUid0UlnyLXspXNOKUrVpZRkzHlVOpaHqGVQv6XOWbWIuFQ7g5JeFc/aZqVfhVI7o5Pv2l0iYp218obWemu1fETF3Q4mItY5q+UNlbXOKhldW+eskg9qnbNKPqey1lstrzXWWyuvNtZbq+Tr9gm+57/f3zb5V+4K6N9GvOl/5XggyW+zAFH+dWun+88OFPPrAIr+x42+aCxGfp2e4uQftzON2ci/uFWqlTTL//7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7nf/7n/7ckVlA4IPgPAAAQQQGdASqAB24CPm02m0mkIyKhIjG4EIANiWlu4XfyGmP7A3RfmPKB6Fmazd7p/qY21vPN+oXecOic9aj/Y5MR4s/l34YeCv+M6NNMG/PtMf3q9dAExWnrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMlbFKRriy7eu2/sTybOdK1xZdvXbf2J5NnOla4su3rtv7E8mznStcWXb1239ieTZzpWuLLt67b+xPJs50rVZoMStYMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YJFYItYMnL2nrBk5e09YMnL2nrBk5eyCGF4vTPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOWJDo8vaesGTl7T1gycvaesGTl7T1gxPYPisy8XpnrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09XjsXWeApRjPWDJy9p6wZOXtPWDJy9pbFCxemesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvJin4e09YMnL2nrBk5e09YMnL2nrBksO48BSjGesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gyWHfDzLxemesGTl7T1gycvaesGTl7TwA2sZ6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p4Aca6Z6wZOXtPWDJy9p6wZOXtPWDJwtKeXi9M9YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk4Wlq1y9p6wZOXtPWDJy9p6wZOXtPWCRVrrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBIrBFrBk5e09YMnL2nrBk5e09YMnL2QQwvF6Z6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJyxIdHl7T1gycvaesGTl7T1gycvaesGJ7B8VmXi9M9YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nq8di6zwFKMZ6wZOXtPWDJy9p4EABx/P6LIemSJfeb8LBtYz1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTl7TwA410z1gycvaesGTl7T1gycJCKWUHsjcFZduHgsT/Bi7HZG1d4oLWKFi9M9YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5eTFPw9pZBK/T8QNRPMZtcgVgaieYza5ArA1E8xm1yBWBqJ5iwgfl5c6/nOdwHNGeY4xaXd53sAQ8vFuIYXi9M9YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5YkOjy9jxB11esRRPMZtcgVgaieYza5ArA1E8xm1yBWBqJ5jNmEGzi71SxocC2XHle4xQRHzPHGesEirXWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPWCRWCLWDJy9p6wZOXtPWDJy8hm4N9ugcBOY/ZifTTSV6oC/mJnrBiewfFZl4vTPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6vHYus8BSjGesGTl7T1gycvad9hzkMvOAS1JWCVF3lcGThaU8vF6Z6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJwtLVrl7T1gycvaesGTl7T1gycvaesEirXWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPWCRWCLWDJy9p6wZOXtPWDJy9p6wZOXsghheL0z1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTliQ6PL2nrBk5e09YMnL2nrBk5e09YMT2D4rMvF6Z6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPV47F1ngKUYz1gycvaesGTl7T1gycvaWxQsXpnrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnLyYp+HtPWDJy9p6wZOXtPWDJy9p6wZLDuPAUoxnrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMlh3w8y8XpnrBk5e09YMnL2nrBk5e08ANrGesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvaeAHGumesGTl7T1gycvaesGTl7T1gycLSnl4vTPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOFpatcvaesGTl7T1gycvaesGTl7T1gkVa6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6wSKwRawZOXtPWDJy9p6wZOXtPWDJy9kEMLxemesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycsSHR5e09YMnL2nrBk5e09YMnL2nrBiewfFZl4vTPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6vIJxZdvXbf2J5NnOla4su3rtv7E8mznStcWXb1239ieTZzpWuLLt67b+xPJs50rXFl29dt/Ynk2c6VriyqOKzLxemesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTl7T1gycvaesGTlgtVa3nxogx5lRGegB8wfFZl4vSdnPy3iC8XTVwSmSVo/uXH9PMvF6Z6wZOXtPWDJy9p6wZOXtPWDJy9p6wZOXtPWDJy9p6vErqZjLkhusSMNmFH+ShWc6NbmT4j6QKqJTMTgmZ3DrtXjN+VTslPdTYDsz71ysxMcvn5HusX56KJCrXeXKACSabiXuOS5cJOZGzoSVl7cQY8y8XpnrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nqF9EC6QqTH6Q6WJMVIXUkAKkPtbD+M5ckJbDMqh2IYegy/Zl4BM81A/UqlYI9YgyKSSQAqRFs4AGU8jotpCjQCjIqRQAbWM9YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2QfrydZLQuLz/R+Y88HJ1q711U7VAPJ163an/Gp3rbe5+ydZNn7Ju1P9tLm9uTr11k3aqDZfyZri9M9YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnL2nrBk5e09YMnCAA/v+o3v4m7xFXf/F3L3LzIAAAAAAAAAAAAAOmzNHpWZfqMC3tvjwBw+ct/YAAPvjlhQAAAFCzAAAJwKAAATjMAAAnAoAABOMwAACcCgAAE4zAAAJwKAAATjMGapYALMeRL7VZvwvcj59tpRv3ktVEsxQ7YITCAbIN65qLTgh6qjf73jFb5CSqhcWjboP4ZiRGMAACWChpaBRbOqP953n7tp06BdRm+blI2isz139CbdkUJFVjBOw6LgUDYtHEySghf0soRELXr55nJgXphRfHlQzdi5mHA2mc1QFWR9mI0c6EI1V7tflWgngBXSjlGwNPWqHP8wyWaAAPBp5/7QeeswNB3mdgk6qJIdp0CxL4o5qbch4CvGtXrvwcnnM7CSdUp1g2IfH2k3FHfTcNQoaYqBd3r/ijHG8OE93QfdSEnlkjKbOYXKtu/8aJS9fMsQ2zPFIGl/woRwAAACFDegPvdX3Tw5Cmp/zc1PwDV0Cecs5fhOPYUIdT/5kAebz8wQa/ILptDPTePcWUM0I/n/oSQP5aOJDkc/MvkUof9WctvZZaCp1gAAM5mE3j8hepmWNbEXvtL7mmnBOEIgDEnBsa/B970b6xwgdFjJkzgtcQ9yyTbxN3zWzLkjjt9O2A10aiAAmAoIBjnwEb1q2a+HZB9CttMCtoMTiyUKoHW7Dc5P67JjsBlM4LHQkL8JX2kt9esi4AAwmYAAAhwoAABOMwAACcCgAAE4zAAAJwKAAATjMAAAnAoAABOMwAACcCgAAAAAAAAAACH32pdW192TIZxZqSloqJBfSZUAiiNuHw8mOo76gSpACcjyFgnmnwWYQdJRwRpACNWYOtDkW3eOBb3agAAgN7vBIU64XDGaEhkEAYxnLQHOHy8cQOH5vzuvRH2SR6DlxcFfOvRJkZjuOLoTPBr7h43Lc+Rq2n/zd7fBf4LMrGwwcsMY02RdL7orIz4i/eYuBdw8NDIhMtd01Otn/92seTey9qi6YZaqZWjGc+RwUzx8ICqNcvgNFxa5T12kjwJ9f5OdLL0Nwp1gZUGV1BaSdQc17ipll9ta/BL6qfWKlwLh5UEhVaZfdvXH1DM+FYl/6gQm9aIkxdjg1/7yj11I66+VTdfg/hnYRZNyZKOtXN6NirY9dgi5MEaP2B97lcytlfo1fayUwHuIKJDLcUEpMFyFpeMsYScQK2Aj2qfY8prDUbSaDM35x6Shwo9StAwhLJu/ifGEQtUk2SP//wRTS0KVwoCBgsV/wUuyb36okaKL0+gnqia8m8VYoq5fxhEqywpWGwcGhZq8htD5QCBfbQ0aiBIanyn/7tYjT9XSrAtGiql2qOwcQeznOPCwIoFj/Zuqf/BS80VBumpJ89WQogYtSV9I/EaeqgGVz44levDWW0fF/kZZWmcijT0gwXAAADl/7zA04r7UZcotzMZw/bayZ1QnSZ8ARxAFKLtQENqwsQD1MFr2pCyw89c2xCHFudkcsYgJXtCohL72hUcddLKRZ8fxTHURi8Ez4l2o60xFnjcXkigBMpy8XyBUid0mNmQp8X4vU6yJXSdB+Hk3SxHpvVlZloVDqHh5VyL25rn8z0tqEWRtlsd/uhPKA+MQoOR+yLoWvulw2FAvM4PFa+DucXxkf//gB3qX2blKHkf/13//gjBRKjZmtWPFxYwH+V9Y50K4FqxbyEcZi5Ul2tUxnQjiLnf3wQjDvDBmFUnPihM8DqRa1w4BsdH3ekpzRg4H0SHyRkuW+lEKnkEMiZayjyRlpu726Y2bB8THYjU9DC5XXpNcInXJyZqSuVKCss2ZI15+1gXZ29ijpQwSuVkg9F1drPlJqxjs32435bTbMorqtjNrmiiYGryHGbk7kWrjSX2ow541EI64ZPgHBpKna+UPTieitIIrMjcBBzu/2vefky5XibUu6uhI7Pzmmbd3K+W7QMH4Akr1LJpSLxbecivfhhfWmvRZNY1jAAAAHL3L8BmCC91nVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" width="1920" height="622" class="img_ev3q"></p>
<p>The crow’s foot is my second favorite. It’s used to show relationships between
entities. We know that teachers are related to the sessions that they conduct.
(And <code>session</code> might be another entity, with fields like <code>date</code>,
<code>subject taught</code>, and <code>venue</code>, to name just a few.) The crow’s foot allows us to
specify exactly how they are related. On that session entity, we’ll need to
specify the name (or ID) of the teacher who led it. On the teacher entity,
however, there won’t be a field to specify the name or ID of the session…
because a single teacher can lead MANY sessions. This is a one-to-many
relationship. The crow’s foot (that little three-pronged fella) denotes the
many. One teacher can have many sessions. One session, however, can only have
one teacher. See the diagram below.</p>
<p><img decoding="async" loading="lazy" src="https://docs.openfn.org/es/assets/images/objectmodel3-f8773ce38dd6bda67c896f6546ded810.webp" width="1920" height="1316" class="img_ev3q"></p>
<p>If we focus just on <code>teacher</code> and <code>session</code> and think back to MS Excel, we can
envision a <code>teachers</code> table and a <code>sessions</code> table. Let’s put them on different
sheets in the same workbook. On the <code>teachers</code> table, there is no column for
<code>session</code>, but on the <code>sessions</code> table, there <em>is</em> a column for <code>teacher ID</code>.
We’ve just established a one-to-many relationship.</p>
<p>Next time, we’ll talk about what’s going with the <code>attendance</code> entity above.
It’s sometimes called a “junction object” or a “join table”, and it’s what
allows MANY students to be related to MANY sessions. I’ll write more on this
next time, but there is no magic going on, no technicalities here. The way that
many students are related to many sessions is through this Real World Concept
that we call <code>attendance</code>. Attendance is what happens when a student shows up at
a session. It’s so important to get the language right in these discussions, and
make sure that you’re talking about real-world concepts.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="relational-object-models-with-lots-of-confusing-terms-are-not-technical-they-are-bad">Relational object models with lots of confusing terms are not "technical". They are "bad."<a href="https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized#relational-object-models-with-lots-of-confusing-terms-are-not-technical-they-are-bad" class="hash-link" aria-label="Enlace directo al Relational object models with lots of confusing terms are not &quot;technical&quot;. They are &quot;bad.&quot;" title="Enlace directo al Relational object models with lots of confusing terms are not &quot;technical&quot;. They are &quot;bad.&quot;" translate="no">​</a></h3>
<p>Remember that as you start to put pen to paper. And allow yourselves time (and
multiple drafts) to get the boxes and terminology right. Understanding
relational object modelling is an incredibly powerful way to organize a company.
As I said before, if you can’t model it with boxes and arrows, it’s a smell that
something might not be conceptually sound.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-disclaimer-and-some-thoughts-on-scaling">A disclaimer and some thoughts on scaling:<a href="https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized#a-disclaimer-and-some-thoughts-on-scaling" class="hash-link" aria-label="Enlace directo al A disclaimer and some thoughts on scaling:" title="Enlace directo al A disclaimer and some thoughts on scaling:" translate="no">​</a></h3>
<p>Some organizations do amazing work without good conceptual systems. They rely on
humans, instinct, improvisation, nouse, and other not-so-clearly-defined things.
They might do really great work. They might get the job done. But they need to
be well aware of their condition and face it head on. If you can’t systematize
your program implementation processes, then you need to focus tremendous effort
on finding and retaining the right people.</p>
<p>A friend once told me that “people are not scalable.” I couldn’t agree more, and
defend my earlier stance that if your organization’s information structure can’t
be defined with boxes and crow’s feet, it may be very hard for you to scale
responsibly. However, if you can create a ruthlessly efficient, world-class
“people operations” system (recruitment, training, management, compensation, HR,
etc.) that ensures you’ve always got the right people to figure things out you
might be better off than those operating a well defined assembly-line with
interchangeable parts. Alas, the middle way is probably the best.</p>
<p>That’s all for now. More soon.</p>
<p><em>Need help organizing or scaling your organization's information or process
flows? Contact our team of ICT4D specialists at
<a href="mailto:support@openfn.org" target="_blank" rel="noopener noreferrer" class="">support@openfn.org</a>.</em></p>]]></content>
        <author>
            <name>Taylor Downs</name>
            <email>taylor@openfn.org</email>
        </author>
        <category label="how-to" term="how-to"/>
        <category label="tips" term="tips"/>
    </entry>
</feed>