{
    "version": "https://jsonfeed.org/version/1",
    "title": "OpenFn Help Articles",
    "home_page_url": "https://docs.openfn.org/es/articles",
    "description": "OpenFn/docs Blog",
    "items": [
        {
            "id": "https://docs.openfn.org/es/articles/2022/09/19/auth-security",
            "content_html": "<p>In order to ensure that Global Goods and Digital Public Goods can enable impact\nwithout doing any harm, it is crucial that their authentication and\nauthorization systems are secure from the outset.</p>\n<p>We’ve put together a resource to help other Digital Public Goods and Global\nGoods achieve just this. You can view and comment on the resource\n<a href=\"https://docs.google.com/document/d/1QvOcOdk2iZCWnAVNiBbmhh-6Q2gapUhsMCUzJQNWqEU/edit?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>,\nor read on to learn why.</p>\n<p>Digital health technologies have a critical role to play in both improving the\ndelivery of health services <em>and</em> making health systems more robust and\nadaptable. Whilst this has been the consensus for a while, various factors such\nas bureaucracy, lack of capacity, authority/ownership, budget or simply lack of\nprioritization have hindered the adoption and implementation of such\ntechnologies.</p>\n<p>However, the COVID-19 pandemic has brought digital health systems back to the\ntop of the agenda, having forced governments to adapt their health systems and\nprocesses in a short amount of time and confronted them with the fragility and\ninflexibility of some of their existing systems. This has led to an increase in\nfunding for both new and existing health technologies.</p>\n<p>At the same time, the recent pandemic also saw an increase in data breaches and\ndata leaks in the healthcare sector. Whilst these are a serious matter in any\nsystem, the nature of the healthcare technologies means that the data they are\nstoring or managing almost always includes Personally Identifiable Information\n(PII) which, if leaked, can have dramatic consequences for those they identify.</p>\n<p>The higher the number of technologies and quantity of data stored in these, the\nhigher the risk of vulnerabilities and negative impact of a data breach or data\nleak. If Global Goods are to make the most of this renewed interest and increase\nin funding for digital health technologies, it is crucial for them to keep\nsecurity as a top priority to ensure that they ‘do no harm’, a\ncore<a href=\"https://digitalpublicgoods.net/standard/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"> Digital Public Good</a> principle.</p>\n<p>Several organizations have already put some thought into documenting security\nbest practices and standards. However, none situate these security decisions\nwithin the wider context of a product development timeline or roadmap. This\nrequires individuals to regularly scan through long lists of security\nrecommendations to pick out and prioritize their implementation, increasing the\nlikelihood of an oversight or early-made product decisions which make it harder\nto implement such security features down the line.</p>\n<p>As product manager at Open Function Group, I myself have recently been reviewing\nand analyzing numerous resources to identify and prioritize features required to\nget to fully-secure authentication and authorization for our newest product\n<a class=\"\" href=\"https://docs.openfn.org/es/documentation#openfn-v2-lightning-\">Lightning</a>–and make sure that nothing\nfalls through the cracks. I’ve also had the opportunity to gather insights from\nDigital Public Good community forums (OpenMRS, DHIS2, OpenLMIS, etc) and speak\nto other product managers and engineers from the Digital Public Goods community\nto ask them about their learnings, and any tips they might have: Austin and\nMorten from DHIS2, Dev and Biyeun from Dimagi.</p>\n<p><strong>To more widely share these learnings, we have drafted this resource\ndocumenting the specific security measures that should be considered at each\nstage of product development _specifically with regards to authentication and\nauthorization. _</strong>Our hope is that this article will help other product managers\nin the Global Good (GG) and Digital Public Good (DPG) communities consider these\nfeatures into their backlog/roadmap from the outset and make sure they are\nfollowing a ‘secure by design’ approach. To inform our analysis, we analyzed\nlearnings from the wider DPG community, as well as recommendations from other\nsecurity experts and international standards, including GovStack, OpenHIE,\nOWASP, NIST, and more.</p>\n<p><strong>The resource can be found\n<a href=\"https://docs.google.com/document/d/1QvOcOdk2iZCWnAVNiBbmhh-6Q2gapUhsMCUzJQNWqEU/edit?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</strong></p>",
            "url": "https://docs.openfn.org/es/articles/2022/09/19/auth-security",
            "title": "Secure by design: a roadmap to secure authentication and authorization",
            "summary": "In order to ensure that Global Goods and Digital Public Goods can enable impact",
            "date_modified": "2022-09-19T00:00:00.000Z",
            "author": {
                "name": "Amber Rignell"
            },
            "tags": [
                "security",
                "authentication",
                "authorization"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2022/06/07/workflow-automation",
            "content_html": "<p>Do you think twice when you get an automatic SMS notification because your\nprescription is ready? (Neither do I.) This seamless experience is driven by\n“workflow automation”, a key feature that OpenFn provides. The OpenFn\nIntegration Toolkit is a Digital Public Good (DPG) used by governments and NGOs\nto boost efficiency through workflow automation. The automation that OpenFn\nprovides includes automatically sending SMSs, automating stock updates across\nsupply chain systems, tracking clinical visits, and helping plan vaccine\nrollouts. We support our partners’ work by lifting the burden of manual data\ntransfers between platforms.</p>\n<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\n<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>\nas a LinkedIn article from Open Function Group.</p></div></div>\n<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>\n<p>Workflow automation is an approach to making the flow of tasks across platforms\nrun independently. A prescription SMS notification feels seamless, but it\nprobably requires complex automation across multiple systems like clinical\nregistries, pharmacy stock databases, and SMS gateways. Tools like OpenFn run\n“behind the scenes” to perform calculations and transfer relevant data to/from\ndifferent systems autonomously, minimising the risk of human error and\nultimately saving time and money that an agency would spend on manual data\ntransfers. The goal is to preserve data integrity, uphold data security, and\nenforce compliance with policies and data standards.</p>\n<p>Since 2014, OpenFn has been at the forefront of workflow automation in the\nsocial sector, providing a platform to help governments and NGOs focus on the\nhuman aspect of their work rather than wasting time trying to exchange data\nbetween systems or perform calculations manually.</p>\n<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>\n<p><a href=\"https://www.myagro.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">MyAgro</a>, an NGO helping West African farmers increase\ntheir yield, uses OpenFn to power its SMS-based savings deposit system for\nsmall-scale farmers. MyAgro distributes and tracks saving vouchers using the\n<a href=\"https://www.dimagi.com/commcare/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">CommCare</a> app. Smallholder farmers purchase\npre-paid myAgro saving scratch cards and text the one-time code to secure the\nvoucher credit in their account. Once the amount has been added to their\nbalance, a confirmation notification is sent to the farmer. MyAgro works with\nthe farmers to reach their savings goal, which they then use to pay for seeds,\nfertilisers, and training courses. OpenFn:</p>\n<p align=\"center\"><img src=\"https://user-images.githubusercontent.com/105651463/172341045-62a3eebd-98fb-4189-987f-06bbdb03ac30.png\"></p>\n<ol>\n<li class=\"\">Syncs voucher codes from CommCare to Salesforce so the relevant offices can\ntrack who purchased the saving vouchers.</li>\n<li class=\"\">Matches incoming SMSs against the database of vouchers to deposit correct\nfunds into the individual account.</li>\n<li class=\"\">Allocates savings to farmers’ accounts in Salesforce and updates the account\nstatus.</li>\n<li class=\"\">Sends a confirmation SMS with updated account balances to farmers, so they\nare aware of their credit.</li>\n</ol>\n<p>OpenFn helps myAgro reach more farmers with less administrative overhead by\nautomating these key steps. MyAgro calculated that even at their 2014 levels of\nscale, OpenFn saved them 260+ hours per year and increased customer confidence\nas they worked to serve poor farmers.</p>\n<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>\n<p><a href=\"https://sinapis.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Sinapis</a>, an NGO that empowers entrepreneurs through\ntraining courses, uses OpenFn to automate its payment collection process. Every\ntime a client’s new\n<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>\npayment is registered in <a href=\"https://kopokopo.co.ke/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Kopo Kopo</a>, OpenFn:</p>\n<p align=\"center\"><img src=\"https://user-images.githubusercontent.com/105651463/172341175-5505b647-93dc-44af-9143-00ebdd0b5bae.png\"></p>\n<ol>\n<li class=\"\">Automatically matches incoming payments with existing Sinapis accounts.</li>\n<li class=\"\">Logs new payments and updated payment status in Salesforce to track\nentrepreneurs’ charges.</li>\n<li class=\"\">Updates account balances to help staff monitor outstanding balances and\noverdue fees.</li>\n</ol>\n<p>With OpenFn handling the routine data processing, Sinapis has more time to\nsupport its entrepreneurs. Precious staff time can be spent delivering training,\nthe essential face-to-face coaching that transforms their clients’ fledgling\nenterprises. Their team does not have to worry about the accuracy of payments\nand account balances; the OpenFn implementation replaces error-prone manual data\nentry with a systematised, intelligible, fully automated process.</p>\n<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>\n<p>The <a href=\"https://digitalimpactalliance.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Digital Impact Alliance (DIAL)</a>, an\norganisation that works to overcome digital development barriers, wanted to\nprototype a “building-blocks based approach” for their upcoming GovStack.global\nproject. They configured an HR workflow solution that connects mobile data\ncollection apps, databases, and payment apps using OpenFn. Every time an\norganisation registers new workers via\n<a href=\"https://docs.opendatakit.org/collect-intro/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">ODK Collect</a>, OpenFn:</p>\n<p align=\"center\"><img src=\"https://user-images.githubusercontent.com/105651463/172341214-06a1e74b-c1e3-45e4-83c8-4a60af5a9d2d.png\"></p>\n<ol>\n<li class=\"\">Automatically registers new employees in an <a href=\"https://www.ihris.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">iHRIS</a>\ndatabase to oversee all new hires.</li>\n<li class=\"\">Makes payments to workers via\n<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>,\nensuring all wage transfers are direct.</li>\n</ol>\n<p>While it’s not in use at scale, this prototype is a powerful illustration of how\nautomation enables scalable, component-based solutions to become more than the\nsum of their parts. The processes that DIAL implement with OpenFn help unlock\nthe true potential of these powerful underlying DPGs.</p>\n<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>\n<p>OpenFn’s goal is to increase the efficiency and effectiveness of the social\nsector. Why should health and humanitarian organisations waste precious\nresources manually moving data across systems when workflow automation can do it\nmore safely and quickly? We want to ensure employees focus on using data to do\ntheir work, not transferring data between systems and running rote calculations.</p>\n<p>Saving organisations time and money is part of the picture, but we also deliver\nbetter program outcomes through efficiency and effectiveness. Suppose OpenFn\nsecurely automates the complex digital processes critical health and\nhumanitarian interventions require. Reducing error rates and security\nvulnerabilities and getting higher quality data into the hands of a doctor\nfaster actually saves lives. Responsible automation leads to better health and\nhumanitarian outcomes, enabling larger-scale intervention delivery via\nefficiency gains and improving service quality by getting accurate data into the\nhands of patients, clinicians, and policymakers when they need it.</p>\n<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>\n<p>Think about your own experiences; have you found yourself in a situation where\nmanual data processing—some rote, mindless task—has gotten in the way of doing\nthe more exciting parts of your job? Whether you are in the health and\nhumanitarian space or just want to make your life easier, OpenFn is here for\nyou. Our open-source toolkit is available for everyone to automate tasks such as\nsending SMS and email notifications or automating data cleaning and formatting\nfor faster reporting.</p>\n<p>Head to <a href=\"http://docs.openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">docs.openfn.org</a> to learn more about\nenterprise-grade automations with our DPG and check out\n<a href=\"http://community.openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">community.openfn.org</a> to connect and collaborate\nwith like-minded social-sector integrators and OpenFn staff. We hope to see you\nsoon!</p>\n<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>\n<p>Open Function Group (OFG) is a global team of integration specialists and\ndevelopers of the OpenFn Integration Toolkit. As a\n<a href=\"https://digitalpublicgoods.net/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">DPG</a>, OpenFn’s core technology is free and\nopen-source. If you want more information about the Toolkit or to sign up for a\nfree 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>",
            "url": "https://docs.openfn.org/es/articles/2022/06/07/workflow-automation",
            "title": "Workflow Automation; Why do it yourself when a program can do it for you?",
            "summary": "Do you think twice when you get an automatic SMS notification because your",
            "date_modified": "2022-06-07T00:00:00.000Z",
            "author": {
                "name": "Alexa de Vegvar"
            },
            "tags": [
                "automation",
                "solution"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs",
            "content_html": "<p>OpenFn automation happens via <a class=\"\" href=\"https://docs.openfn.org/es/documentation/build/workflows\">jobs</a> which\ndefine specific steps (\"operations\") that OpenFn should perform. They're written\nin a basic scripting language that runs on top of (and has full access to)\n<strong>Javascript</strong>. A basic understanding of Javascript will take your job writing\non OpenFn to the next level. To improve my limited knowledge of JavaScript, I\nhave been taking Codecademy's\n<a href=\"https://www.codecademy.com/learn/introduction-to-javascript\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Introduction to JavaScript Course</a>.</p>\n<p>In this post we'll discuss:</p>\n<ol>\n<li class=\"\">Arrow functions</li>\n<li class=\"\">Falsy values</li>\n<li class=\"\">Other short-hand notation</li>\n</ol>\n<p>Keep reading for all the important learnings I have taken from just the first\nfour modules!</p>\n<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>\n<p>Javascript allows us to create <em>functions</em> which take <em>arguments</em> and <em>do stuff</em>\nwith them. Most times, function declaration in Javascript looks like this:</p>\n<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>\n<p>This was simple enough for me to follow. We are declaring a function,\n<code>getAge()</code>, which has the steps to calculate a person's age and can be easily\nreused throughout the job.</p>\n<p>But other times, function writing looks like this:</p>\n<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>\n<p>In OpenFn, you're often writing unnamed functions that take <code>state</code> as their\nonly argument and you use them right away. See how concise writing and using a\nfunction can be with the arrow:</p>\n<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>\n<p>These <strong><code>arrow functions</code></strong> confused me when reading through jobs. I've learned\nthat, for the most part, they can be thought of as short-hand notation which\nremoves the need to type out <code>function</code> for every function declaration.</p>\n<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\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.\nTl;dr: \"An arrow function expression is a compact alternative to a traditional\nfunction expression, but is limited and can't be used in all situations.\"</p></div></div>\n<p>Further, we can make the code even more concise by also removing the\nparenthesis, curly braces, and the return keyword when certain criteria are met.\nIn the code above, the parenthesis are omitted because there are no parameters,\nand the curly braces and return keyword are omitted because the function body is\ncomposed of a single-line block. As you can see, there are a variety of ways to\nwrite functions and this course is helping me better recognize them. Some key\nterminology here is: <code>function expression</code>, <code>arrow function</code>,\n<code>concise function</code>, <code>anonymous function</code>, and <code>implicit returns</code>.</p>\n<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>\n<p>During the <a class=\"\" href=\"https://docs.openfn.org/es/adaptors/salesforce#mapping-and-design-considerations\">mapping</a>\nphase of integration design, we often discuss how each answer choice for\n<strong>picklist</strong> values should map from the source system to the destination system.\nSometimes the mapping is simple but other times, there is an extensive list of\npossible choices that can be found in the\n<a class=\"\" href=\"https://docs.openfn.org/es/documentation/legacy/getting-started/terminology#message\">message</a> and not all\nare relevant to the destination system. Then the question is, <strong>how should the\njob handle values which are not explicitly mapped?</strong></p>\n<p>Sometimes we hear clients say to \"ignore\" those values. <strong>But what does it\nreally mean to \"ignore\" a value?</strong> Should we set it to <code>0</code>? An empty string? How\nabout <code>null</code>, <code>undefined</code> or <code>NaN</code>? In Javascript these are all <code>falsy</code> values.</p>\n<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\n<a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Falsy\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</p></div></div>\n<p>Determining what is really meant here is important and the value selected has\ndifferent implications depending on the system.</p>\n<p>Take this sample mapping for different districts in Conakry. The value left of\nthe colon is from the source system and the value to the right is for the\ndestination system.</p>\n<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>\n<p>If the destination system is Salesforce, this mapping would not upload \"Other\"\nto Salesforce. However if \"Other\" mapped to an empty string instead, this would\nupload the empty string to Salesforce. <strong>This distinction is especially\nimportant in cases where we are overwriting existing data.</strong> For instance, if a\nstudent previously lived in Ratoma and then moved to an unknown district marked\nas \"Other\", <code>undefined</code> <em>would not</em> update the student's district in Salesforce\nbut the empty string would. Both of these are falsy values but have different\nmeanings in Salesforce.</p>\n<p>How about if the message includes a value for a district that <em>is not</em> in the\nmapping? Such as \"New York\". Should the job default to <code>undefined</code>? Or <code>null</code>?</p>\n<p>These questions are just a few examples of how understanding <code>falsy</code> values in\nJavascript can make it easier to implement the best mapping for the real-world\nuse case.</p>\n<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>\n<p><strong>Short-circuit evaluation</strong></p>\n<p>When we are syncing forms to a database, we sometimes expect different versions\nof the same form with fields present one version but not the other. One way to\nmitigate this discrepancy is by submitting a dummy value for the field whenever\nit is missing in the message. I just learned the fastest way to add this code to\nmy jobs! For example, the code below will ensure that <code>household_id</code> always has\na value in the destination system: if <code>survey_info/household_id</code> is present in\nthe message this will be the assigned value, otherwise it will assign the dummy\nvalue <code>state.data.body._id</code>.</p>\n<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>\n<p><strong>Template literals</strong></p>\n<p>Like the arrow discussed above, the \"dollar sign\" was another symbol that often\nconfused me. But template literals are actually very straightforward. They\nincrease the readability of the code and make it easier to see what the\nresulting string will be. Wrap your template in the backtick (`) symbols and\nthen each variable in <code>${}</code>.</p>\n<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>\n<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>\n<p>The most challenging module in the course covered <strong>higher-order functions</strong>.\nThese are defined as <strong>functions that accept other functions as arguments and/or\nreturn functions as output.</strong> But why are these important and how are they used\nin OpenFn jobs? It turns out we use them quite alot! The code below is an\nexample from an existing integration with the field names replaced.</p>\n<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>\n<p>This code is using a built-in JavaScript method that <strong>helps us iterate on\narrays to manipulate elements and return values.</strong> The <code>.filter()</code> method is\nbeing used to return a new array after filtering out certain elements from the\noriginal array. We have declared two variables to store the new arrays:\n<code>participantsToUpdate</code> and <code>participantsToCreate</code>. Participants whose ids are\nexisting in the destination system (or who are in the <code>idList</code> array) are added\nto <code>participantsToUpdate</code>, and any remaining participants are added to\n<code>participantsToCreate</code>. This filtering helped us perform the correct operations\non each participant type. For instance, later in the job, we use the filtered\narrays to only overwrite a participant's <code>reportNumber</code> field if it is a new\nparticipant. <code>.filter()</code> is just one of many higher-order functions that power\nOpenFn jobs.</p>\n<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>\n<p>The final lesson was on objects and key-value pairs--something used in almost\nevery OpenFn job! The result of the design and mapping phase of requirements\ngathering is almost always a mapping document which includes key-value pairs in\nplain English. These specifications are then translated to JavaScript via\n<strong><em>objects</em></strong>. The code below is a snippet of an object which captures the\nkey-value pairs for states in the US.</p>\n<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>\n<p>The value left of the colon is how the state is represented in the source\nsystem, and the value right of the colon represents how OpenFn will send the\ndata to the destination system. This mapping process is key to integration\ndesign. Learn more about mappings\n<a class=\"\" href=\"https://docs.openfn.org/es/adaptors/salesforce#mapping-and-design-considerations\">here</a>.</p>\n<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\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.\nTl;dr: \"The Object class represents one of JavaScript's data types. It is used\nto store various keyed collections and more complex entities.\"</p></div></div>\n<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>\n<p>I'm well on my way to becoming a better job reader and writer. Here are some\nnext steps:</p>\n<ol>\n<li class=\"\">\n<p>Understand <code>fn(state)</code> and how <code>state</code> can be manipulated in OpenFn jobs.</p>\n</li>\n<li class=\"\">\n<p>Explore what's available on the JavaScript docs\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">site</a>.</p>\n</li>\n<li class=\"\">\n<p>Sign up for the next level JavaScript course.</p>\n</li>\n</ol>",
            "url": "https://docs.openfn.org/es/articles/2021/10/29/how-learning-javascript-helped-me-better-understand-jobs",
            "title": "How learning JavaScript helps me better understand OpenFn jobs",
            "summary": "OpenFn automation happens via jobs which",
            "date_modified": "2021-10-29T00:00:00.000Z",
            "author": {
                "name": "Aicha Diallo"
            },
            "tags": [
                "javascript",
                "tips",
                "jobs",
                "learning"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound",
            "content_html": "<p>Have you ever struggled to layout the strategy for testing your React App? Well,\nyou are not alone! Here a few hints from the lessons I learned during my\nexperience testing a\n<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\n<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>\nbackend.</p>\n<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>\n<p>Because a React app is built on\n<a href=\"https://reactjs.org/docs/react-component.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">components</a>, the basic UI units,\nit is natural to think and organise your tests around components! And so unit\ntesting, in this case, would refer to \"component testing\", which may be\nconfusing at times, especially when the concept of unit testing is again applied\nto testing functions such as Redux <code>reducers</code> and <code>action creators</code> or any other\nJavaScript function in your application.</p>\n<p>The other challenge that I often faced was whether to write tests for each\ncomponent in isolation or write a test for a feature that encapsulates a set of\nrelated components. The later would be equivalent to writing what I would call\n\"integration tests\".</p>\n<p>Finally, one would say \"well then you could have just written the tests in a way\nthat resemble the way the application is used\"! This approach is commonly\nrecommended in the React community, but it quickly becomes really complex to\nmaintain the layers of separation between <strong><em>unit tests</em></strong>, <strong><em>integration\ntests</em></strong> and <strong><em>end-to-end tests</em></strong>.</p>\n<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>\n<p>Given a React/Redux application, here is how I would organise my testing\nstrategy:</p>\n<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>\n<ul>\n<li class=\"\">\n<p>In a React app, <strong>unit tests</strong> will largely apply to testing \"helper\nfunctions\" and not to testing components, as justified in the next section.\nHelper functions, in this case, would refer to functions that live outside the\ncomponents and are neither Redux action creators nor reducers. These functions\ncan be used inside components, action creators, reducers or other parts of\nyour application.</p>\n</li>\n<li class=\"\">\n<p>Writing unit tests for \"helper functions\" would ensure their signatures and\nexpected outputs are protected against regressions. This would also ensure\ntheir use across components or other functions is consistent and as expected.</p>\n</li>\n<li class=\"\">\n<p>Where possible, each \"helper function\" must have its own <code>unit test</code>.</p>\n</li>\n<li class=\"\">\n<p>An example of a unit test would like:</p>\n<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>\n</li>\n<li class=\"\">\n<p>Write a <em>thousand</em> of these.</p>\n</li>\n</ul>\n<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>\n<ul>\n<li class=\"\">\n<p>In the context of a React/Redux app, component tests can be thought of as\n<strong>integration tests</strong>. This is because React components are built around\nfeatures such as <code>&lt;Signup /&gt;</code>, <code>&lt;Search /&gt;</code>, etc. So one React component can\nbe a mix of other components to achieve a UI feature set.</p>\n</li>\n<li class=\"\">\n<p>To test a component, write an <strong>integration test</strong> that covers the use of a\ngiven component for a given UI feature.</p>\n</li>\n<li class=\"\">\n<p>If a component being tested dispatches a Redux <code>action</code>, this is the right\nplace to test those actions and their effect on the UI.</p>\n</li>\n<li class=\"\">\n<p>Pay attention to the concept of <em>feature isolation</em> vs <em>component isolation</em>\nas it will help you write better integration tests and also easily mock\ncomponent contexts.</p>\n</li>\n<li class=\"\">\n<p>A classic example of <strong>feature isolation</strong> is when you have a <code>&lt;UserList /&gt;</code>\ncomponent which displays a list of users and has a <code>&lt;button /&gt;</code> to add a new\nuser. Writing a test for <code>&lt;UserList /&gt;</code> would be equivalent to testing a\nfeature.</p>\n</li>\n<li class=\"\">\n<p>In this example, one would be tempted to test the action of clicking on the\n<code>&lt;AddUserButton /&gt;</code> and further test the <code>&lt;NewUser /&gt;</code> form... nope! This is\nwhere we draw the line! Only test that the <code>&lt;UserList /&gt;</code> renders the mock\n<code>users</code> in the list and that the <code>&lt;AddUserButton /&gt;</code> is present/enabled. The\n<code>&lt;UserList /&gt;</code> feature ends there, otherwise you will be sliding into\n<strong>End-to-End</strong> testing :)! The <code>&lt;User /&gt;</code> component, although it is invoked by\n<code>&lt;UserList /&gt;</code> component, it is isolated enough to be tested in its own\nintegration test.</p>\n</li>\n<li class=\"\">\n<p>Testing components this way would make \"context mocking\" easier for\ncomponents.</p>\n</li>\n<li class=\"\">\n<p>Another important benefit for isolating testing context, as in the example\nabove, is that it will be easier to mock the <code>redux actions</code> and/or api calls\nusing tools such as <a href=\"https://jestjs.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Jest</a> and\n<a href=\"https://mswjs.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Mock Service Worker</a> (or \"msw\") as explained in the\n<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>\n</li>\n<li class=\"\">\n<p>The value of writing integration tests for components, in this way, ensures\nthat a given component renders the UI consistently, given all possible\ncombinations of contexts and interactions. This will also allow you to ensure\nredux actions invoked by the component are called as expected and with the\ncorrect arguments.</p>\n</li>\n<li class=\"\">\n<p>An example component integration test would look like:</p>\n<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>\n</li>\n<li class=\"\">\n<p>Write a <em>good couple</em> of these.</p>\n</li>\n</ul>\n<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>\n<ul>\n<li class=\"\">\n<p>In a React/Redux App, this would mean testing a <em>full flow</em> of a given\nfeature. <strong>end-to-end tests</strong> would require launching the entire application,\nincluding the backend, to run a given test.</p>\n</li>\n<li class=\"\">\n<p>Note that <strong>end-to-end tests</strong> are different from <strong>integration tests</strong> as\nthey require the entire App to run and render the full flow to your component\nunder test.</p>\n</li>\n<li class=\"\">\n<p>With this understanding, consider writing <strong>e2e</strong> tests <em>per workflow</em>.</p>\n</li>\n<li class=\"\">\n<p>An example <strong>e2e workflow</strong> is the \"Viewing and adding users\" workflow.</p>\n</li>\n<li class=\"\">\n<p>The e2e test for this workflow would require a test runner to launch the app,\nlog-in, navigate to the users list page, verify existing users are in the\nlist, click on the Add New User button and confirm that the new user has been\nadded to the list.</p>\n</li>\n<li class=\"\">\n<p>As you can see, e2e tests have more dependencies and require that you setup\nyour testing environment in way that closely simulates your real application\nusage.</p>\n</li>\n<li class=\"\">\n<p>An example e2e test for a React/Redux App with a Phoenix/Elixir backend, using\n<code>Hound</code> as a test runner looks like this:</p>\n</li>\n</ul>\n<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>\n<ul>\n<li class=\"\">Write <em>only a few</em> of these.</li>\n</ul>\n<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>\n<p>There are many testing tools out there, but for a typical <em>React/Redux</em> app the\nfollowing tools should help you accomplish the above tasks:</p>\n<ol>\n<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>\nand <strong>integration</strong> tests.</li>\n<li class=\"\"><a href=\"https://testing-library.com/docs/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">React Testing Library</a> used along with\nJest as an \"assertion library\" for integration tests.</li>\n<li class=\"\"><a href=\"https://mswjs.io/docs/getting-started/install\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">MSW</a> used along with Jest as\na REST API mocking library.</li>\n<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>\ntests in Elixir/Phoenix apps.\n<a href=\"https://developers.google.com/web/tools/puppeteer\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Puppeteer</a> can also be\nused along with Jest.<!-- -->\n<ul>\n<li class=\"\">If Puppeteer is used, it will work seamlessly with Jest but only in\nheadless browser mode. It also reduces on tech stack since you will only\nneed Jest.</li>\n<li class=\"\">Hound gives you the ability to run your <strong>e2e</strong> tests both in <code>headless</code>\nand <code>browser</code> mode.</li>\n</ul>\n</li>\n</ol>\n<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>\n<p>Testing a React App can be really hard, but worth it! By building\n<code>Aria-accessible</code> components ahead of time, you save yourself 💰 and good\nhealth! A few more hints would be:</p>\n<ul>\n<li class=\"\">Build clean, isolated and plugable components for your better testing\nexperience. \"God components\" can be a <em>pain</em> to test!</li>\n<li class=\"\">Using test runners such as Jest, that use <em>emulated</em> web browsers (e.g.,\n<code>jsdom</code>) rather than a real browser come with their own challenges in\nrendering and traversing complex DOM trees, especially if you are using UI\nlibraries such as <a href=\"https://mui.com/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">MUI</a>.</li>\n<li class=\"\">If using Jest for <strong>integration tests</strong>, I would recommend the components\nunder test have as few dependencies as possible to avoid the complexity\ninvolved in mocking http requests and waiting for asynchronous DOM rendering.</li>\n</ul>\n<p>What would I do differently? Here are my few thoughts:</p>\n<ul>\n<li class=\"\">Organise and document detailed test cases for manual \"click testing\".</li>\n<li class=\"\">Identify and clearly isolate components for <strong>integration tests</strong>.</li>\n<li class=\"\">Do not <em>delete</em> slow tests, instead re-write your component to be faster.\nRespect the linter's advice, always!</li>\n<li class=\"\">Use a commonly supported frontend testing stack such as Jest, Msw, or\nPuppeteer for easier setup and community support.</li>\n<li class=\"\">Setup your test runner to use a test database. It helps, especially during\n<strong>e2e</strong> testing.</li>\n<li class=\"\">Always write <strong><em>all the three types</em></strong> of tests, whenever applicable.</li>\n</ul>\n<p>All this stuff for what?</p>\n<ul>\n<li class=\"\">Well because regressions can be much more expensive to your organisation!\nWriting high quality and thoroughly tested software will save you 💰 and help\nguarantee a maintainable codebase and a progressive software application.</li>\n</ul>\n<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>\n<li class=\"\">Swallow your pride and be humble: <em>always</em> do <strong>manual testing!</strong></li>\n<li class=\"\">Click test your way through the <strong>manual test cases</strong> for every new\ndeployment, catching regressions.</li>\n<li class=\"\"><em>Lock in</em> your fixes and new features as <strong>unit tests</strong>, <strong>integration\ntests</strong>, and <strong>end-to-end tests</strong>.</li>\n</ol></div></div>\n<p>Happy testing,</p>\n<p>Chaiwa</p>",
            "url": "https://docs.openfn.org/es/articles/2021/10/22/testing-react-app-with-jest-hound",
            "title": "Testing a React app, the blurred line between Unit, integration and E2E",
            "summary": "Have you ever struggled to layout the strategy for testing your React App? Well,",
            "date_modified": "2021-10-22T00:00:00.000Z",
            "author": {
                "name": "Chaiwa Berian"
            },
            "tags": [
                "how-to",
                "tips",
                "testing",
                "browser-testing",
                "react",
                "elixir"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1",
            "content_html": "<p>We're very happy users of Elixir and Phoenix at OpenFn, given that we've been\nusing it continuously for about 6 years - upgrades and all. Our front-end\ntoolchain, albeit far from out of date (Webpack <code>5.52.1</code> today) has left some\nroom for improvement.</p>\n<blockquote>\n<p>This is a post written on what we call 'Slow Fridays', where we explore and\nthink about stuff we're curious about. The deal is some artifact at the end of\nthe day. So while this post is not complete - I believe part two is warranted.</p>\n</blockquote>\n<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\ncited were the amount of support the team had to continuously give for a\ntoolchain that although necessary is not under the control of the framework\nteams control.</p>\n<p>Back in the day, Phoenix used brunch to do front end builds - despite not being\nthe most common bundler at the time. The objective I assume was to provide\nsomething that is easy to get going with and lowers the barrier to entry for\nPhoenix. We actually switched brunch out for webpack before webpack got into\nPhoenix in order to more easily use new libraries and newer ES syntax.</p>\n<p>It wasn't without its challenges, and still is. Webpack is wonderfully\npowerful - but it does do <em>a lot</em>, and it's really easy to stumble onto plugins\nthat are either order dependent or mutually exclusive.</p>\n<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>\n<p>It's a bundler/compiler for JS/TS projects that is written in Go, along with\nbeing faster by the simple fact it's natively compiled it also leverages\nconcurrency. To compare <em>why</em> esbuild is faster than NodeJS on face value is\nunfair in my opinion. Although having similar objectives, they are wildly\ndifferent in implementation and in features.</p>\n<p>So <strong>speed</strong> is the big selling point. But thinking back to other bundlers, what\nmade us switch (thinking of gulp, grunt, mixing in babel, browserify etc). Those\nchanges were never about speed, at least a drop in build time was nice - a bit\nof caching goes a long way. The changes were about being able to use the syntax\nand libraries we wanted with as little fuss as possible.</p>\n<p>Webpack <em>can</em> do almost anything. I'm not convinced esbuild can match that, and\nas an open-source maintainer I'd argue it shouldn't break its original goals to\nmatch Webpack.</p>\n<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>\n<p>I started breaking down what our current bundle setup actually does, after all\nif we were just bundling plain JS I'd probably be using rollup and terser. Going\nthrough our <code>webpack.config.js</code> file I can see that it:</p>\n<ul>\n<li class=\"\">Conditionally provide sourcemaps depending on the environment.</li>\n<li class=\"\">Uses babel to parse js/jsx files<br>\n<!-- -->At the same time cherry picks lodash imports\n<a href=\"https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1#lodash\"><sup>*</sup></a>.</li>\n<li class=\"\">Parse <code>import</code>s for <code>.css</code> files through <code>style-loader</code> and <code>css-loader</code><br>\n<!-- -->Injecting the styles onto the dom.</li>\n<li class=\"\">Detect <code>require</code> statements to images, copy them to an assets folder<br>\n<!-- -->and replace the statement with a url to image.</li>\n<li class=\"\">Can watch files (excluding <code>node_modules</code>) in development.</li>\n<li class=\"\">Splits files that come from <code>node_modules</code> into a <code>vendor.js</code> bundle.</li>\n</ul>\n<p>That's a lot more than just building something, there's some implicit behaviour\nhere.</p>\n<ul>\n<li class=\"\">The interaction between <code>style-loader</code> and <code>css-loader</code> results in extra\nfunctions and behaviour being introduced, it's not producing a <code>.css</code> file.<!-- -->\n<blockquote>\n<p>I'm personally not a big fan of apps injecting styles, but I think I get why\npeople do it. Maybe I'm old-school and like to have my stylesheets delivered\nin a few files (or even one).</p>\n</blockquote>\n</li>\n<li class=\"\">The splitting doesn't know about <code>app.js</code>, it puts everything that resolves to\n<code>node_modules</code> in <code>vendor.js</code>. Subtle but worth pointing out.</li>\n</ul>\n<p><a name=\"lodash\"><sup>*</sup></a> Could probably ignore this and refactor some\nfiles and check that tree-shaking is working properly.</p>\n<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>\n<p>Like I mentioned before, webpack is super versatile and it would be\nshort-sighted to get esbuild to do everything it does.</p>\n<p>So I'm approaching this with the idea that I code will need some changes, the\nless the better - I've always firmly believed the kinds of restrictions smaller\ntools provide can make things neater and more portable.</p>\n<ul>\n<li class=\"\">✅ <strong>Provide source maps</strong><br>\n<!-- -->Supported out of the box. <a href=\"https://esbuild.github.io/api/#sourcemap\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">docs</a></li>\n<li class=\"\">✅ <strong>Watching files</strong><br>\n<!-- -->Yup. <a href=\"https://esbuild.github.io/api/#watch\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">docs</a></li>\n<li class=\"\">🆗 <strong>Build our css</strong> (and vendored css)<br>\n<!-- -->Compiling CSS is supported, however it doesn't (as far as I can tell) doesn't\nreplace <code>style-loader</code> and how it injects CSS into the DOM.</li>\n<li class=\"\">✅ <strong>Copy image assets referenced in JS</strong><br>\n<!-- -->Indeed it can, using the <code>file</code> loader.\n<a href=\"https://esbuild.github.io/content-types/#external-file\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">docs</a></li>\n<li class=\"\">✅ <strong>Can split our files</strong><br>\n<!-- -->I picked a confusing green checkbox to illustrate that while esbuild does\nsupport file splitting - it appears to have some caveats, primarily that it\nonly works with <code>esm</code> output files. We don't ship ES Modules to the browser,\nbut seems like a good moment to try given the primary targets for our web\nfront 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>\n</ul>\n<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>\n<p>I've tried to go over the process as methodically as possible, after all this\nisn't a new codebase nor am I alone on this. It's always important to understand\nwho's going to be working with this, and respect the varying skill sets and\nfocuses of our peers.</p>\n<p>So we know what we want, but can any of this work? Let's give it a go with the\nsimplest of steps:</p>\n<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>\n<p>Bang! Right, that one makes sense to me, it doesn't know what JSX is yet. I'm\ngonna take a wild guess and say it won't know about the CSS or our referenced\nimages.</p>\n<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>\n<p>Nice! Two things worth mentioning here:</p>\n<ol>\n<li class=\"\">We're using some non-standard ES syntax here: <code>::</code>, let's replace that with\nthe equivalent <code>includesText.bind(this)</code>.</li>\n<li class=\"\">Some duplicate keys in our theme objects, looks like a warning but worth\ncleaning up.</li>\n</ol>\n<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>\n<p>Jeepers! 651ms! That's nuts.</p>\n<p>We can see it's copied our images and css into the build folder. Note that we're\nnot doing any bundle splitting right now and from the looks of it</p>\n<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>\n<p>That's better, wow. It's kinda difficult to not be amazed. For context, a\nminified and split production build takes about 34s with webpack and that's on\nmy i7 desktop machine, and 197s (3+ mins) on CI/CD.</p>\n<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>\n<p>So our 'can we actually do this' seems to have gone pretty well so far. I'm\nreally excited about what this will give us in the end.</p>\n<p>But a shell command doth not a replacement for webpack make. We still need to:</p>\n<ol>\n<li class=\"\">Get Phoenix to use esbuild and watch our files as we work.</li>\n<li class=\"\">Ensure that our html templates serve the correct files in dev &amp; production.<br>\n<!-- -->Including our CSS that is no longer injected into the DOM.</li>\n<li class=\"\">Split at least our vendored modules into their own bundle.</li>\n<li class=\"\">Make sure sourcemaps generate correctly for when we upload them to Sentry.</li>\n<li class=\"\">Have some kind of cache-busting naming scheme for production builds.</li>\n</ol>\n<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>\n<ul>\n<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>\n<a name=\"ref1\"><sup>1</sup></a></li>\n<li class=\"\"><a href=\"https://caniuse.com/es6-module\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">JavaScript modules via script tag</a>\n<a name=\"esm\"><sup>2</sup></a></li>\n</ul>",
            "url": "https://docs.openfn.org/es/articles/2021/10/15/webpack-to-esbuild-part1",
            "title": "Moving from Webpack to esbuild on Phoenix",
            "summary": "We're very happy users of Elixir and Phoenix at OpenFn, given that we've been",
            "date_modified": "2021-10-15T00:00:00.000Z",
            "author": {
                "name": "Stuart Corbishley"
            },
            "tags": [
                "how-to",
                "js",
                "webpack",
                "build",
                "phoenix",
                "ci/cd"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx",
            "content_html": "<p>So you're using docker's multi-stage builds and noticed that your build times\naren't nearly as quick as you expected?</p>\n<p>As many teams who spend more and more time using docker, it's quite common to\nget into multi-stage builds; usually resulting in significantly smaller images.</p>\n<p>However this comes with a pretty significant dilemma with caching. Even when\nusing the <code>--cache-from</code> flag when building, docker only caches the last image.</p>\n<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\nindividual stage. Coming with tight coupling between the shape of your\nDockerfile and your build process/scripts.</p>\n<p>The other solution uses Docker Buildx which the document describes as:</p>\n<blockquote>\n<p>Docker Buildx is a CLI plugin that extends the docker command with the full\nsupport of the features provided by Moby BuildKit builder toolkit. It provides\nthe same user experience as docker build with many new features like creating\nscoped builder instances and building against multiple nodes concurrently.</p>\n</blockquote>\n<p>While that sounds pretty cool, it doesn't really touch on caching. This actually\ntook me a while to find out that it would in fact do caching very differently.\nIn fact it's a very different experience using it, and has lots of really cool\nfeatures that further detach you from the local docker state allowing you to\nbuild in environments that are stateless - such as Google CloudBuild without\nhaving to wire up some kind of persistence or file caching scheme.</p>\n<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>\n<p>We're only going to scratch the surface of Buildx, and with that let's get the\nabsolute minimum working; build our image locally.</p>\n<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>\n<p>First things first we need to create a builder, and select it for use. This is\nimportant as without creating a buildx builder (and setting it as the default),\nbuildx will use the <code>docker</code> driver instead of the <code>docker-container</code> driver\nwhich we want in order to take advantage of cache exporting.</p>\n<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>\n<blockquote>\n<p>You only need to run this once, except in the case of CloudBuild where each\ninvocation is a new node.</p>\n</blockquote>\n<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>\n<p>While the <code>--cache-*</code> options aren't specifically required when running <code>build</code>,\nas <code>buildx</code> does manage its own local cache (distinct from the regular docker\ncache), it's there to emphasise the options that cache can be provided via the\nCLI options.</p>\n<p>This is about as close as you get to a regular docker build, with the\nsignificant difference being that you have to specify where to cache from and\nto.</p>\n<p>The <code>--load</code> flag is to tell buildx to set the output to the local docker\ndaemon. Without that you won't actually get a resulting image to run. However,\ndepending on your use case, this could be seen as a convenience - if you're\nwanting to run your tests inside your build; a resulting image isn't\nparticularly useful.</p>\n<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>\n<p>Now comes to the part I'm most interested in, caching in a stateless/remote\nenvironment. Multipart builds for us at OpenFn are essential, since we use\nElixir and like other compiled languages there is a lot to be gained by only\nshipping the stuff you're going to run; and no language is safe from requiring\nseveral times more 'stuff' in order to build our apps.</p>\n<p>Buildx supports a\n<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>\nof caching sources and destinations. We're going to be using the <code>registry</code>\ntype, where you point the cache at a repository reference (repo/image<!-- -->:tag<!-- -->\nstyle).</p>\n<blockquote>\n<p>One thing to note is that Google Container Registry does not support the\nmetadata/manifest format that buildx uses, so if you're using Google Cloud you\nwill need to start using Artifact Registry.</p>\n</blockquote>\n<p><strong>Inline</strong></p>\n<p>Push the image and the cache together:</p>\n<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>\n<p>This comes with the constraint that cache mode is always <code>min</code>, which only\nexports/caches the resulting layers; which is still better than the plain docker\nbuild caching but I think having the intermediary layers is generally a win. We\nwant to avoid a single line change invalidating an entire build step.</p>\n<p><strong>Registry</strong></p>\n<p>Resulting image and cache are separated:</p>\n<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>\n<p>Again coming back to the cache mode, here being <code>max</code>; all intermediary laters\nare exported to the cache image as well.</p>\n<p>I have opted to create <em>two</em> images, one for caching and another for the\nresulting image used to deploy. This gains us a much more granular cache and the\nability to more easily manage the cache image - like deleting the whole thing\nwhen wanting to invalidate the cache. Not to mention I'm fairly sure the size of\nour images that get pulled on kubernetes would get significantly larger with\nmany more layers.</p>\n<p>It feels like a safer bet to have lean images for kubernetes to pull, and chunky\ncache images specifically for speeding up build.</p>\n<p>Depending on your setup, pulling large images can get <em>seriously</em> expensive in a\nreasonably active deployment environment - like on AWS ECS without using\nPrivateLink.</p>\n<blockquote>\n<p>It appears the <code>moby/buildkit</code> documentation also demonstrates\n<a href=\"https://github.com/moby/buildkit#registry-push-image-and-cache-separately\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">this</a>\napproach.</p>\n</blockquote>\n<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>\n<p>This implies that the cache image is named with the suffix <code>-build-cache</code>:<br>\n<code>us-east4-docker.pkg.dev/&lt;project-name&gt;/platform/app[-build-cache]</code>.</p>\n<p>The <code>--push</code> argument tells buildx to push the resulting image to the registry.</p>\n<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>\n<p><strong>Clearing the local cache</strong></p>\n<p>As mentioned before, buildx has its own cache and in order to clear the cache\nwhile debugging and readying a Dockerfile for remote building you'll probably\nneed to reach for <code>docker buildx prune</code>.</p>\n<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>\n<p>Using buildx has been a really pleasant experience, having personally attempted\nusing it a few times over the last 3 years; the most recent one being the first\ntime I felt confident getting it into production. As with any sufficiently\nflexible build tooling, the errors and issues you can run into range from\ncomplete gibberish, genuinely concerning inconsistencies to architectural\nchoices that you haven't fully caught up on; requiring an ever growing list of\nchanges you need to make to your own build process.</p>\n<p>Our initial observations have been great, reasonable changes on our build have\ngone from 28 minutes to around 9 minutes.</p>\n<p>While I have encountered a few confusing cache invalidations, especially when\nbuilding locally, exporting the cache to a repository and then having CloudBuild\nuse the image cache. And occasionally locally having what feels like <em>really</em>\naggressive caching on intermediate steps, leading me to pruning the local cache.</p>\n<p>But overall, these issues aren't necessarily buildx issues and more likely a\ncombination of building docker images in general except with many more steps\naccounted for by the cache.</p>\n<p>It's kinda hard to see now what the exact issues I had with it in the past, but\nhey!</p>\n<p>Buildx has given me what I 'expected' with docker multi-stage builds, and having\nthe cache in a repository completely side-steps having to attach a shared volume\nor copying from a storage bucket.</p>\n<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>\n<ul>\n<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>\n<a name=\"ref1\"><sup>1</sup></a></li>\n<li class=\"\"><a href=\"https://docs.docker.com/buildx/working-with-buildx/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Docker Buildx</a></li>\n<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>\n<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>\n</ul>",
            "url": "https://docs.openfn.org/es/articles/2021/10/08/improving-multistage-docker-builds-using-buildx",
            "title": "Improving Multistage Docker Builds using Buildx",
            "summary": "So you're using docker's multi-stage builds and noticed that your build times",
            "date_modified": "2021-10-08T00:00:00.000Z",
            "author": {
                "name": "Stuart Corbishley"
            },
            "tags": [
                "how-to",
                "docker",
                "ci/cd"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs",
            "content_html": "<p>Jobs are business processes turned into functional-style scripts. What does that\nmean, how should you approach writing jobs?</p>\n<p>First, this is how <em>I</em> think about jobs and what we do at Open Function Group to\ntry to make our job code as readable, future-proof, and concise as possible.\nThere are a million different ways to approach writing jobs. This is one.</p>\n<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>\n<p>If a job is a set of instructions for a chef (a recipe?) then the initial\n<code>state</code> is all of the ingredients they need tied up in a perfect little bundle.\nIt usually looks something like this:</p>\n<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>\n<p>This might be the initial <code>state</code> for a real-time, message-triggered job. Some\nsource system generated a new patient payload and sent that payload to OpenFn.\nThe data from our source system will wind up in <code>state.data</code>. Now if my job is\nmeant to take this new patient registration information and use it to create a\nnew record in the national health record system, I'll also need to provide my\nrobot-chef here with a credential so they can access that system. The credential\nI've specified will get put into <code>state.configuration</code> and now our \"raw\ningredients\" are all ready for our robot chef.</p>\n<p>Note that even if this job was initiated by a cron trigger (e.g., \"Hey chef,\nprepare this recipe every Tuesday at 7pm\") or by a flow/catch trigger (e.g.,\n\"Hey chef, prepare this recipe only when you <em>fail</em> to make banana pancakes\") it\nwill have an initial state.</p>\n<p><strong>Every job, and every operation inside that job (think \"step\" in a recipe) is\ncalled with <code>state</code> and returns <code>state</code> when it's done.</strong></p>\n<p>Initial state for a cron triggered job might look like this:</p>\n<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>\n<p>And for a fail triggered job like this:</p>\n<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>\n<p>No matter what, jobs start with state. See\n<a class=\"\" href=\"https://docs.openfn.org/es/documentation/jobs/state\">\"Initial and final state for runs\"</a> for a detailed\nbreakdown.</p>\n<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>\n<p>Now that we've got it in our heads that <code>state</code> is the raw ingredients you hand\nto your chef when you ask them to prepare a recipe, let's look at the recipe.\nBoiled down (excuse the pun) a job for loading those patients into the national\nhealth record system might look like this:</p>\n<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>\n<p>We're telling our chef to take those raw ingredients (login info for our\nnational health system and a chunk of information about a newly registered\npatient) and do the following:</p>\n<ol>\n<li class=\"\">Find out whether this person already has a national health insurance number</li>\n<li class=\"\">Add this person to the patient registry (making use of some insurance data\nfrom step 1)</li>\n<li class=\"\">Add a visit record with information about this initial visit (making use of\npatient registry data from step 2)</li>\n</ol>\n<p>When all of this is done, we'll not only have a new patient and visit logged in\nthe national health registry, but we'll also return a final <code>state</code> object with\ninformation about what we've done that can be used in subsequent jobs. Imagine\nthat we want to make a cash transfer to this patient so that they can take a cab\nto the next visit—we might create a job with the Mpesa adaptor that takes the\nfinal state of this first job as its <em>initial state</em>. In this way, jobs are\ncomposable.</p>\n<p>But what about the complexity inside our job—in order to complete step 2, we\nneed some data from the insurance registry and we only get that data in step 1.\nCrucially, each operation (again, think \"step\" in a recipe) takes state and\nreturns state. In effect, the OpenFn execution pipeline simply calls all of your\naction methods <em>with state</em>, passing it along from one operation to the next,\nwaiting for each to finish and using the output from the first as the input for\nthe second.</p>\n<p>While you may write your <code>get</code>, <code>post</code>, <code>post</code> job as it's show above, the way\nit's handled by OpenFn is actually more like:</p>\n<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>\n<p>Each of these operations returns a function which <em>takes state</em> and returns\nstate. This means that <em>within</em> a job, you are essentially modifying <code>state</code>,\ncreating/manipulating records in external systems, and returning <code>state</code>.</p>\n<p>It opens up a really interesting world of possibility for data manipulation,\ncleaning, or transformation. Consider what we might do <em>after</em> we get data from\nthe insurance registry but <em>before</em> we create that patient in the national\npatient registry:</p>\n<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>\n<p>We might even need to do some manipulation <em>before</em> we send a <code>get</code> request to\nthe insurance registry. That's no problem:</p>\n<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>\n<p>Here, we've added a step to modify the initial <code>state</code> before we send that first\n<code>get</code> request to the insurance API. We determine if the new patient is a minor,\nand then use that newly calculated data to apply a query to the insurance API\nrequest.</p>\n<p>Using <code>fn(state =&gt; state)</code> or <code>alterState(state =&gt; state})</code> is incredibly\nuseful, because it allows us to separate our data manipulation, calculation, and\nraw Javascript (which will be harder for low-tech users to understand) from our\nexternal actions. Let's explore that some more.</p>\n<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>\n<p>Inside each operation we could do some data manipulation... all of these\noperations, across the many different language packages, allow for inline data\nmanipulation like this:</p>\n<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>\n<p>But if you're interacting with both technical and non-technical users, it makes\nfor harder to read jobs. Consider the following instead:</p>\n<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>\n<p>Since we often have non-developers creating the external operations like <code>get</code>\nand <code>post</code> above, this pattern makes our handoff easier. The business analyst\ncan say \"I need to have a registration <code>type</code> field available for use when\nquerying the insurance registry.\" A developer might respond, \"Great! How do you\nwant to calculate it... I've got all of Javascript at my fingertips.\" That dev\ncan then make as many API calls as they'd like, perform as many\n<code>map.reduce(...)</code> calls as their heart desires to complete that calculation...\nso long as they make sure the hand off <code>state</code> to the business analyst's\noperation with a valid <code>state.data.type</code> attribute.</p>\n<p>A final benefit of this approach is that it becomes much easier to generate job\nscripts from Google Sheets. Our implementation team frequently works with\nnon-technical clients to generate field maps that look like this:</p>\n<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>\n<p>We can then copy and paste the syntax generated in that final column directly\ninto OpenFn and update the bits that need some sort of custom code, writing an\n<code>fn(state)</code> block or an <code>alterState(state)</code> block before the external action.</p>\n<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>\n<p>Some key takeaways here:</p>\n<ol>\n<li class=\"\">\n<p>Jobs start and end with <code>state</code> — some raw ingredients that will be used in a\nrecipe.</p>\n</li>\n<li class=\"\">\n<p>Jobs are lists of <code>operations</code> — steps in a recipe that <em>each</em> take <code>state</code>,\n<em>do some stuff</em>, and then return <code>state</code>.</p>\n</li>\n<li class=\"\">\n<p>As you move through the steps in a job, you are modifying <code>state</code>. Each\nsubsequent step begins with the final state from the previous step.</p>\n</li>\n<li class=\"\">\n<p>It may be useful to keep all your custom Javascript data cleaning,\nmanipulation, etc., in a separate operation (e.g., <code>fn(state)</code> or\n<code>alterState(state)</code>) so that your external actions are clean and easy to\nfollow.</p>\n</li>\n</ol>\n<p>Finally, taking a close look at how developers write those <code>fn(state)</code> steps\ntells us a lot about what the job execution pipeline is really doing:</p>\n<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>\n<p>I hope this gives you sense of how I think about structuring jobs and building\ndata pipelines or automation flows on OpenFn. We recognize that this stuff is\ncomplex, and are pushing our new documentation regularly, so please do get in\ntouch if you think there are ways we could improve this type of\nwalk-through/helper article.</p>\n<p>Happy integrating,</p>\n<p>Taylor</p>",
            "url": "https://docs.openfn.org/es/articles/2021/07/05/wrapping-my-head-around-jobs",
            "title": "Wrapping my head around jobs",
            "summary": "Jobs are business processes turned into functional-style scripts. What does that",
            "date_modified": "2021-07-05T00:00:00.000Z",
            "author": {
                "name": "Taylor Downs"
            },
            "tags": [
                "how-to",
                "tips",
                "jobs"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/05/24/commcare-events",
            "content_html": "<p>This is a quick one, but I just got off an exciting call with an organization\nthat's going to set up some jobs to move data into Salesforce from CommCare and\nrealized that despite this being one of our more common integration\nrequirements, we haven't done a 'tips' article for this type of project. Until\nnow.</p>\n<p>So here goes. While this is by no means an exhaustive project planning template,\nhere are a few things to keep in mind if you're planning to implement a CommCare\nto Salesforce integration on your own.</p>\n<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>\n<p>First, most people make use of CommCare's \"Data Forwarding\" feature to send form\nsubmissions and changes in cases (creation, update, closure, etc.) to OpenFn in\nreal-time. You can read about that\n<a class=\"\" href=\"https://docs.openfn.org/es/adaptors/commcare#webhook-or-data-forwarding-setup-commcare-to-openfn\">here</a>\nbut the key consideration at this planning stage is <em>when</em> you'll be performing\noperations—<code>create(...)</code>, <code>update(...)</code>, <code>upsert(...)</code>, <code>query(...)</code>,\n<code>(bulk(...)</code>, etc.—in Salesforce and what data you'll have access to.</p>\n<p>Each time a form submission comes into CommCare, we'll get a copy of that\nsubmission at OpenFn and can use that data to create or modify some records in\nSalesforce.</p>\n<p>Likewise, each time a case gets updated (or created or closed) we'll get a copy\nof the case with all the case \"properties\" and we can use that data to <em>do some\nstuff</em> in Salesforce.</p>\n<p>If you are using \"Form Forwarding\", the <code>trigger</code> you'd create in OpenFn might\nlook like this <code>{\"form\":{\"@name\":\"ART Adherence Self-Reporting Tool\"}}</code> and it\nwould trigger your <code>job</code> any time an \"ART Adherence Self-Reporting Tool\"\nsubmission arrived from CommCare, giving that job access to all of the data\ninside that submission.</p>\n<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>\n<p>Assuming you're using making use of case management, the data that arrives from\nCommCare will look something like this:</p>\n<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>\n<p>This is a big blob of <code>JSON</code>—the body of the message that's received at OpenFn\nwhen this particular form (\"ART Adherence Self-Reporting Tool\") is submitted in\nCommCare—will be handed off to the job to start processing. The question is,\nwhat should we do?</p>\n<p>When setting up for a self-service implementation on OpenFn, the most important\nthing you can do at this moment is carefully enumerate the data entry process\nthat you'd like a real human to follow. You can translate it to a job script\nlater.</p>\n<p>You'll need to write this up for your own case, but in this fictional example,\nhere's the data entry process.</p>\n<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>\n<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\ninstructions! We're using the \"API Name\" (instead of just the \"label\", which\nmight be ambiguous) of every field we want filled out in Salesforce and we're\nusing the specific \"path\" to the data we want this person to enter from\nCommCare.</p><p><strong>Why are we being so specific?</strong> Because eventually, a computer will need to\ninterpret this—and they're <em>terrible</em> with ambiguity!</p></div></div>\n<ol>\n<li class=\"\">Every time a messaged is received with\n<code>{\"form\":{\"@name\":\"ART Adherence Self-Reporting Tool\"}}</code> in the body (this is\nour trigger)</li>\n<li class=\"\">Log into Salesforce and create a new participant with the <code>participant_id</code>\nyou find in the <code>form.participant_information</code> section as their\n<code>Participant_Code__c</code>. (If one already exists in Salesforce with that code,\nthen update the existing record instead.)</li>\n<li class=\"\">Fill out the following fields in Salesforce based on the CommCare data in\nthis message:<!-- -->\n<ul>\n<li class=\"\"><code>Name__c</code> with the data from <code>form.participant_information.name</code></li>\n<li class=\"\"><code>Sex__c</code> with the data from <code>form.participant_information.gender</code></li>\n<li class=\"\"><code>CommCare_Case_ID__c</code> with the data from <code>form.case.@case_id</code></li>\n</ul>\n</li>\n<li class=\"\">After you've created (or updated) this participant in Salesforce, create a\nrecord of the visit with the <code>instanceID</code> from the <code>metadata</code> section as the\nunique identifier <code>Visit_Code__c</code>. (Again, if there's already a visit with\nthat ID please update the existing record.)</li>\n<li class=\"\">Fill out the following fields for the visit with data from CommCare\"<!-- -->\n<ul>\n<li class=\"\"><code>Date__c</code> with <code>form.visit_information.visit_date</code>.</li>\n<li class=\"\"><code>Consented__c</code> with <code>form.visit_information.consent_given</code>.</li>\n<li class=\"\">Always set <code>Test_Status__c</code> to <code>true</code>, regardless of what's in the message\nfrom CommCare.</li>\n<li class=\"\">And relate this record with the <code>Community_Health_Worker</code> by their username\nin <code>form.metadata.username</code>.</li>\n</ul>\n</li>\n<li class=\"\">Finally, add a record for each medication listed in the\n<code>form.participant_information.current_medications</code> array—matching on a unique\nID formed by a combination of the medication <code>name</code> and the <code>participant_id</code>\nso that we can update existing medication records if they're present.</li>\n<li class=\"\">Fill out the following fields for the medication:<!-- -->\n<ul>\n<li class=\"\"><code>Generic_Name__c</code> with <code>name</code></li>\n<li class=\"\"><code>Status__c</code> with <code>active</code></li>\n<li class=\"\">And relate this record with the participant you created or updated in step\n2 via the <code>participant_id</code> field.</li>\n</ul>\n</li>\n</ol>\n<p>Phew... that's the task. It's just a fictional example and things could be much\nmore straightforward, or much more complicated than this, but it's important to\nremember that if you can get to this level of <strong>precision and granularity</strong> in\nyour data entry process, a tool like OpenFn can automate this for you in a\nflash.</p>\n<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>\n<p>If you're streaming data in from CommCare and you've got your Salesforce system\nall set up so that this data entry person can complete the above steps (are all\nthe objects and fields created? are the right fields marked as \"unique\" and set\nto be used as an \"external id\" in the Salesforce administration section? have\nyou turned on data forwarding in CommCare?) then it's time to turn them into an\nOpenFn project!</p>\n<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\n<a href=\"https://community.openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">OpenFn community forum</a></strong> where you can post\nstuff like the \"steps\" above and get help from other OpenFn users and staff\nconverting these steps into a real, working, OpenFn job?</p><p>Well, you do know! Check it out at\n<a href=\"https://community.openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">community.openfn.org</a></p></div></div>\n<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>\n<p>We don't need a CommCare credential, since they'll send data to us. Create a\nSalesforce credential that will allow the OpenFn worker to log into your\nSalesforce system.</p>\n<p>Read more about credentials <a class=\"\" href=\"https://docs.openfn.org/es/documentation/build/credentials\">here</a>.</p>\n<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>\n<ul>\n<li class=\"\">Select <code>Message Filter</code> for the <code>type</code></li>\n<li class=\"\">Enter <code>{\"form\":{\"@name\":\"ART Adherence Self-Reporting Tool\"}}</code> for the\n<code>inclusion criteria</code></li>\n</ul>\n<p>Read more about triggers <a class=\"\" href=\"https://docs.openfn.org/es/documentation/build/triggers\">here</a>.</p>\n<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>\n<ul>\n<li class=\"\">Give it a name</li>\n<li class=\"\">Select the trigger you just created</li>\n<li class=\"\">Select the <code>salesforce</code> adaptor</li>\n<li class=\"\">Select the credential you just created</li>\n</ul>\n<p>And convert the instructions above to \"operations\" by using the inline help\nprovided by the Salesforce adaptor:</p>\n<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>\n<p>Now, every time this job runs (which is every time a CommCare form is submitted)\nyour OpenFn worker will upsert a <code>Participant</code>, upsert a <code>Visit</code>, and upsert a\nwhole list of <code>Medications</code> in Salesforce.</p>\n<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>\n<p>Well, in our little example you'd turn the job \"on\" (setting it to on the\ninbound messages from CommCare) and let it run. Whenever there was a failure\n(maybe your Salesforce admin added a new required field on the\ncustom<code>Medication</code> object) you'd get an email and you'd have to come back to\nOpenFn to update your job, including that new field.</p>\n<p>If you're in the process of designing your CommCare and Salesforce systems at\nthe moment, this back-and-forth will be pretty common. Keep in mind that you\nwant as much simplicity as possible in those end-user systems because... well\nbecause <em>humans</em> have the interact with them every day!</p>\n<p>So long as your processes are well defined, OpenFn can handle a bit of\ncomplexity (data cleaning, transformation, complex logical flows, etc.) but you\nshould never make sacrifices to the user experience in CommCare and\nSalesforce—that's a quick way to lose adoption.</p>\n<p>So, ideally, you've designed your workflows in CommCare and Salesforce to make\nyour users happy and get them the information they need to do their jobs well\nand <em>then</em> you come back to OpenFn and spell out our data entry instructions\nlike we've done above.</p>\n<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>\n<p>The two most important resources you've got at your disposal if you're setting\nthis all up on your own are:</p>\n<ol>\n<li class=\"\">this site (docs.openfn.org), and</li>\n<li class=\"\">the <a href=\"https://community.openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">forum</a> (community.openfn.org)</li>\n</ol>\n<p>Read through the <a class=\"\" href=\"https://docs.openfn.org/es/documentation/tutorials/tutorial\">\"What is an integration\"</a>,\n<a class=\"\" href=\"https://docs.openfn.org/es/documentation/get-started/terminology\">\"OpenFn Concepts\"</a>, and\n<a class=\"\" href=\"https://docs.openfn.org/es/documentation/build/workflows\">\"Build\"</a> sections if you're a thorough,\nbackground-first kind of learner. If you crave snippets and sample job code,\nhead directly to the <a class=\"\" href=\"https://docs.openfn.org/es/adaptors/library\">Job Library</a> to see how other OpenFn\nusers are creating their jobs.</p>\n<p>Either way, keep the community posted on your progress in the forum—you'll find\nlots of helpful folks willing to lend you a hand in your integration journey.</p>",
            "url": "https://docs.openfn.org/es/articles/2021/05/24/commcare-events",
            "title": "Forms and Cases: CommCare and event-based integration",
            "summary": "This is a quick one, but I just got off an exciting call with an organization",
            "date_modified": "2021-05-24T00:00:00.000Z",
            "author": {
                "name": "Taylor Downs"
            },
            "tags": [
                "how-to",
                "tips"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/02/17/syncing-options",
            "content_html": "<p>“Syncing” is getting two systems to a state of harmony. This might mean keeping\na list of patients up to date, though modifications can be made in either\nsystem. It might mean copying transactions from one system to another on a\nnightly basis. It might mean a lot of things, but the key concept is that when\nyou sync systems, you’re asking them to work together while simultaneously\nrespecting both software systems’ independence.</p>\n<p>In this post we’ll discuss two different syncing protocols to consider when\ndesigning your data integration. These include:</p>\n<ol>\n<li class=\"\"><strong>Real-time, or event-based, syncs</strong></li>\n<li class=\"\"><strong>Scheduled syncs</strong></li>\n</ol>\n<p>For a\n<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>,\nOpenFn is being used by social workers to automate case referrals between the\nsoftware systems Primero and OSCaR. In the design phase, we evaluated these two\nsyncing options. Below, we'll explain what each one is, the differences between\nthem and which option we chose in the end.</p>\n<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>\n<p>The first option considered for this integration was the real-time/event based\nsync. This type of sync is triggered whenever a specified event takes place in a\nsystem. With this approach, whenever a case is referred in Primero (via the user\ninterface, i.e., when a real case-worker clicks the “refer” button) OpenFn\nreceives a small payload with case data and transmits it to OSCaR and vice\nversa.</p>\n<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>\n<p>Because of their instantaneous nature, real time/event based syncs are great for\nintegrations that involve mobile payments or sms messages to recipients. Really,\nanything that needs to be done “now”! Additionally, depending on your data\nvolumes real time syncs might save you money because you’re only using resources\nwhen specific events take place. For instance, in the above example, a run is\ntriggered by a referral, so if there are only 10 case referrals/month, you'd\nonly process 10 runs each month.</p>\n<p>This type of sync is great because it’s instantaneous, typically quite\nstraightforward to set up, doesn’t require any “state mangagement” on OpenFn,\nand allows for the reprocessing of individual events. There are, however,\ndrawbacks.</p>\n<p>For instance, what happens if the app that’s sending notifications to OpenFn\nfails to send? What if AWS or GCP goes down, taking half of the internet with\nit? If Primero “thinks” it sent the referral, OpenFn never receives it, that\ncase might not get referred to Oscar!</p>\n<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>\n<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>\n<p>The second option considered, a bi-directional schedule dependent sync, solves\nfor the issue discussed above. On a scheduled basis (every 5 minutes, for\nexample) OpenFn checks with Primero and Oscar to see if case referrals need to\nbe transmitted between the two systems and then refers the case if required. In\nthe unlikely event that any of the software systems involved crash, the\nstability provided by the bi-directional sync means that all data is preserved\nand eventually makes it to its destination safely.</p>\n<p>The major drawback here is complexity. We had to use 4 jobs instead of 2, and\nthe job that is responsible for “pulling” data that’s been updated since the\ntime of the last successful sync has to keep “state”—or some sort of working\nmemory of what it’s done in the past. When pulling modified cases from Primero,\nOpenFn now only pulls cases modified on or after <code>YYYY-MM-DD HH:MM:SS</code> where\n<code>YYYY-MM-DD HH:MM:SS</code> is the time of the last successful, round-trip\nsynchronization. OpenFn has built-in functionality to handle exactly this\nrequirement, but not all ETL systems do and it’s a design implication that must\nbe considered.</p>\n<p>Ultimately, for the project in Cambodia, we decided that this sync option is the\nright choice because data integrity is more important than the speed of this\ndata flow. That’s a crucial point to understand—the organizations operating in\nCambodia decided that for this particular use case, being able to guarantee\neventual syncing was more important than having real-time syncing.</p>\n<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>\n<p>Both options definitely have their use-cases and OpenFn's platform versatility\nenables your team to decide which type of sync is right for your project.</p>\n<p>As always, we are here to help with any questions as you think through which\nsync option makes the most sense for your project.</p>",
            "url": "https://docs.openfn.org/es/articles/2021/02/17/syncing-options",
            "title": "Sync Like You Mean It: Thinking Through System “Syncing” Protocols",
            "summary": "“Syncing” is getting two systems to a state of harmony. This might mean keeping",
            "date_modified": "2021-02-17T00:00:00.000Z",
            "author": {
                "name": "Jed Goldstein"
            },
            "tags": [
                "how-to",
                "tips"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment",
            "content_html": "<p>Zandile is a program manager at an iNGO and she needs to use CommCare, DHIS2,\nand OpenFn for an upcoming public health project. She understands that all three\npieces of software can be deployed locally, or accessed as SaaS (Software as a\nService).</p>\n<p>Essentially, Zandile needs to decide if she would like to run the software on\nsomeone else’s servers (SaaS), or on her organization’s own servers (deployed\nlocally). Before making a decision she outlines the basic, non-technical\nconsiderations for both options.</p>\n<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>\n<p>SaaS is software that is installed and <em>runs</em> on computers maintained by\nsoftware professionals, rather than on your own computer. While those computers\nmight be anywhere in the world, typically you'll access and <em>use</em> this software\nvia the Internet.</p>\n<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>\n<p>With SaaS, the software vendor is responsible for the expenses of managing and\nmonitoring all of the technical components and issues associated with the\nsoftware. This means that Zandile’s iNGO will not be responsible for updating\nthe software to ensure compliance with new security regulations, maintaining the\nservers, backing up the data, purchasing and managing uninterrupted power\nsupplies, and providing a team of physical security guards to protect the\ncomputers and data therein against physical theft.</p>\n<p>Going the SaaS route is often faster and more secure, because you do not need to\ndevelop expertise in \"DevOps\" or hire IT and physical security specialists. This\noption also provides the greatest amount of flexibility &amp; scalability– because\nthe SaaS provider is able to deliver more or less computing power, storage, and\nbandwidth—right when it’s needed.</p>\n<p>Having smaller setup costs (you don't have to grow a software delivery company\nof your own) often makes this a more economical choice for many, though SaaS\nwill always come with some sort of ongoing fee—a price per month or year that\ngoes to the vendor to compensate for the time and money they'll spend to ensure\nyour software works properly.</p>\n<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>\n<p>Unlike the SaaS option, local deployment means installing and running software\non your own computers—typically on your organization’s servers.</p>\n<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>\n<p>If a SaaS provider doesn't offer hosting in your country and your government\ndoesn't allow your data to reside on foreign servers (i.e., you're not allowed\nto use things like Gmail, WhatsApp or Facebook for communicating sensitive\ninformation) then local deployment allows you to use tools like CommCare, DHIS2,\nand OpenFn while adhering to government data sovereignty regulations.</p>\n<p>Local deployment also provides your organization with complete ownership of the\nend-to-end system. Your IT team will be personally responsible for ensuring that\nthe software works, is maintained, is secure, etc. If your organization does not\nalready have an IT team in place, then this can become a costly headache, but\nfor a large organization with embedded IT experience, local deployment often\nmakes sense.</p>\n<p>Ultimately, being able to directly hire and fire the people who are responsible\nfor your software's proper functioning can be very useful. It means you have\ncomplete responsibility for whether or not the solution succeeds.</p>\n<p>If you've already got the teams in place (security, DevOps, etc.) then this\noption can be more economical in the long run. With a very good DevOps team,\nmaintaining an extra piece of software might only occupy 20% of a\nfull-time-employee's salary. For your security guards, if the software is\ninstalled in the same physical location it's possible that your costs won't\nincrease at all. While there will be very high setup costs, over time you may\nrealize cost savings by running an efficient software delivery unit within your\norganization that spreads its focus around a number of projects.</p>\n<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>\n<p>In this fictional case, data residency is a concern—her data is sensitive or\ncontains PII—and CommCare, DHIS2 and OpenFn do not provide hosting in the\ncountry she's located. Zandile's organization has a large, experienced IT team\nthat has managed high-availability software projects for many years... they're\npros. While they anticipate that the setup costs will be quite high (around\n$60000 and several months for this set of deployments) they plan on using this\nsoftware for the next 5 years and have determined that they'll recoup a\nsignificant portion of that setup cost by not having to pay license fees for\nSaaS. They go with local deployment.</p>\n<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>\n<p>The answer is: \"it depends\", but if your organization has never managed local\nsoftware deployments, then we recommend going the SaaS approach. SaaS systems,\nlike the one OpenFn and CommCare offer, are simply going to be more secure, more\nstable, and more scalable for the money.</p>\n<p>Crucially, you can always start with SaaS (most tools even offer a free tier)\nand then decide later to invest in the big startup costs of a local deployment\nif the license fees for the SaaS feel high enough to make local deployment more\neconomical over the long term. After a few months or years on the SaaS, you'll\nlikely be in a better position to know if you want to continue using the\nsoftware for 5-10 years.</p>\n<p>Should you need any help with your decision though please do not hesitate to\ncontact OpenFn.</p>",
            "url": "https://docs.openfn.org/es/articles/2021/02/03/hosted-or-local-deployment",
            "title": "Our Servers or Yours: Thinking through deployment options",
            "summary": "Zandile is a program manager at an iNGO and she needs to use CommCare, DHIS2,",
            "date_modified": "2021-02-03T00:00:00.000Z",
            "author": {
                "name": "Jed Goldstein"
            },
            "tags": [
                "how-to",
                "tips"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2020/12/09/upsert-in-dhis2",
            "content_html": "<p>tl;dr: Lots of our users want to upsert tracked entity instances in dhis2, but\nupserts aren’t supported by a standard DHIS2 API endpoint. We built one in our\ndhis2 adaptor: it’s composed of existing APIs and a bit of logic 🤔. Now you can\n<code>upsert</code> tracked entity instances to DHIS2 👍 ✅.</p>\n<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>\n<p>An “UPSERT” is a portmanteau of the database functions UPDATE and INSERT. It’s\ncritical to handle upserts properly when integrating systems. As of version 35\nof the API, DHIS2 does not allow for an administrator to upsert tracked entity\ninstances (“TEIs”). OpenFn’s own\n<a href=\"https://github.com/chaiwa-berian\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Chaiwa Berian</a> has come up with a solution\nthat highlights the utility of helper functions in our dhis2 adaptor. By\ncombining various DHIS2 APIs through an upsertTEI function in OpenFn, DHIS2\nusers can now perform upserts to TEIs.</p>\n<p>If you’re curious, check out his implementation\n<a href=\"https://github.com/OpenFn/language-dhis2/blob/master/src/Adaptor.js#L347\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</p>\n<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>\n<p>A tracked entity instance in DHIS2 is a type of entity that can be tracked\nthrough the system. It can be anything from a person to a commodity like a\nmedicine. If I am a database administrator presiding over two different systems\nthat are connected to one another, let’s call them “System A” and “System B,” I\nwould like for any updates made to the TEI of a user named “Jim Smith” in System\nA to also appear in Jim’s record in System B. Before upserts came about, doing\nso was difficult because of the possibility of duplicate record creation.\nBecause an upsert simultaneously UPDATES and INSERTS, it prevents duplicates.</p>\n<p>Upserts are important and good because they cut down on the risk of duplicate\ndata entry and they also allow for transactions to be retried over and over to\nensure data integrity. That last bit is called “idempotency” and you can read\nabout it <a class=\"\" href=\"https://docs.openfn.org/es/articles/2020/07/02/allow-yourself-to-fail\">over here</a>.</p>\n<p>Please don’t hesitate to reach out to one of OpenFn’s implementation specialists\nif you’d like to learn more.</p>\n<p>— Taylor</p>\n<p><a href=\"https://openfn.org/register\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Sign up</a> to set up a project today, absolutely\nfree.</p>\n<p><a href=\"mailto:admin@openfn.org\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Reach out</a> for more information.</p>",
            "url": "https://docs.openfn.org/es/articles/2020/12/09/upsert-in-dhis2",
            "title": "Tracked entity instances in DHIS2",
            "summary": "tl;dr: Lots of our users want to upsert tracked entity instances in dhis2, but",
            "date_modified": "2020-12-09T00:00:00.000Z",
            "author": {
                "name": "Taylor Downs"
            },
            "tags": [
                "how-to",
                "tips"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2020/07/14/cron-is-better-than-a-timer",
            "content_html": "<p>Hi all, this is a quick one from the product team at\n<a href=\"https://openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">OpenFn</a> — we've made a major upgrade to how timed/period\njobs work.</p>\n<p>In the past, if you weren't using OpenFn to drive some real-time (or\n\"event-based\") automation, you'd need to set up an \"interval trigger.\" Like the\nphoto above, this was essentially a sand timer. Set your trigger to <code>10</code> seconds\nand your job fetches data from DHIS2, some regional public health data set, or\nwhatever, then cleans, transforms, and loads it into some other system.</p>\n<p>For the most part, this has got the job done for the last 5 years, but as our\nNGO and government clients came up with increasingly specific requirements on\nnot only how often but <em>when</em> a crucial job gets executed, we began finding\nourselves creating little customizations for them on a once-off basis. We're\nhappy to annouce that as of <code>v1.75</code> (released today), you can now schedule jobs\nto run based on <code>cron</code> expressions, giving you incredible control over when your\ntasks get executed.</p>\n<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>\n<p>Using <code>cron</code>, you can choose to run a job every minute by typing <code>* * * * *</code>.</p>\n<p>Or maybe you've got a batch sync that you want to take place while your users\nare asleep—why not run it every night at 11pm with <code>23 * * * *</code>.</p>\n<p>What if you've got to submit reuqests for medical inventory only during the\nonset of flu season? Simply type <code>0 0 1 2-4 *</code> and your job will run at midnight\nthe 1st of the month, from February through April.</p>\n<p>You can still run jobs at the click of a button and create timers with\nexpressions like <code>*/10 * * * *</code> for \"every 10 minutes\", but scheduling with cron\ngives OpenFn.org users so much more control over how they run their\norganizations. (And that's a good thing.)</p>\n<p>If you're keen on learning by doing but don't have an OpenFn account yet,\n<a href=\"https://www.openfn.org/signup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">sign up for free</a> or mess around with cron\nexpressions at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>,\na brilliant site to quickly build complex cron expressions.</p>\n<p>That's all from product for today. Speak soon.</p>\n<p>Taylor</p>",
            "url": "https://docs.openfn.org/es/articles/2020/07/14/cron-is-better-than-a-timer",
            "title": "Product News: Enhanced Scheduled/Periodic Job Control",
            "summary": "Hi all, this is a quick one from the product team at",
            "date_modified": "2020-07-14T00:00:00.000Z",
            "author": {
                "name": "Taylor Downs"
            },
            "tags": [
                "annoucement",
                "tips"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2020/07/02/allow-yourself-to-fail",
            "content_html": "<p>Hi all, this is a very short post with a simple message: design for failure.\nEven if you've never heard of\n<a href=\"https://www.microsoft.com/en-us/sql-server\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">MSSQL</a> (or\n<a href=\"https://azure.microsoft.com/en-us/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Azure</a>, or Microsoft?), I want to talk for\none moment about the importance of upserts and a funny developer term called\n\"idempotence.\"</p>\n<p>We just extended our\n<a href=\"https://github.com/OpenFn/language-mssql\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">language-mssql adaptor</a> with a custom\nfunction that allows upserts (an <code>upsert</code> is when you either insert a new record\nor update an existing record based on some identifier). Before, you'd need to\nwrite something tedious like:</p>\n<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>\n<p>whereas now you can simply write:</p>\n<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>\n<p>For an operation to be idempotent means that it can be repeated time and time\nagain without producing an unintended result. This is SUPER important for\ncreating S3 (<strong>S</strong>ecure, <strong>S</strong>table and <strong>S</strong>calable—more on that\n<a href=\"https://openfn.org/trust\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>) integrations because it provides you with two\n\"get-out-of-jail-free\" cards.</p>\n<ol>\n<li class=\"\">\n<p>If a destination application fails, if a connection times out, or if (for\nwhatever reason) you're not sure if the <code>job</code> was completed (say... making a\npayment to CHW) then an idempotent operation can be RETRIED without fear of\nmaking a double-payment.</p>\n</li>\n<li class=\"\">\n<p>If you make some change to how your <code>job</code> works, make some modification to\none of your destination systems, or just because you want to be <em>extra extra\nsure</em> that all the data in a 9 month survey made it to the national public\nhealth reporting system, you can <em>REPROCESS</em> every single message that's come\nthrough OpenFn at the click of a button, without having to worry about\nduplicates.</p>\n</li>\n</ol>\n<p>So... when clients let me mess around with their jobs, I <em>always</em> recommend we\ndesign for idempotence. It's common sense when you're passing messages between\ntwo different systems that are bound to evolve, go offline, have a bad day, etc.</p>\n<p>— Taylor</p>\n<p><a href=\"https://openfn.org/register\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Sign up</a> to set up a project today, absolutely\nfree.</p>\n<p><a href=\"mailto:admin@openfn.org\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Reach out</a> for more information.</p>",
            "url": "https://docs.openfn.org/es/articles/2020/07/02/allow-yourself-to-fail",
            "title": "Allow Yourself to Fail",
            "summary": "Hi all, this is a very short post with a simple message: design for failure.",
            "date_modified": "2020-07-02T00:00:00.000Z",
            "author": {
                "name": "Taylor Downs"
            },
            "tags": [
                "how-to",
                "tips"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask",
            "content_html": "<p>Automation can save time, unlock critical resources, and enable scale–but it\ntypically requires investment to set up. Wondering whether you should automate\nyour processes? Ask yourself these 3 questions.</p>\n<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>\n<p>To date, we have worked with 43 social sector organizations that operate across\nsectors–from health, education, and agriculture, to livelihoods and emergency\nresponse. Over the last 6 years, OpenFn has been implemented worldwide for a\nwide range of use cases, including building real-time data monitoring systems,\nstreamlining data cleaning pipelines, securely exchanging sensitive information,\nand automating routine processes like uploading indicator results, sending\nSMS/email alerts, making mobile payments,\n<a href=\"https://openfn.org/solutions\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">and more</a>.</p>\n<p>By connecting any app, OpenFn can integrate and automate all apps within a\ndigital ecosystem. However, a question that we frequently ask our partners is:</p>\n<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>\n<p>While integration and automation have the potential to enable scale and save\ntime and money (we’ve learned this from our\n<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\nand maintain. These costs sometimes outweigh expected efficiency gains and\nservice outcomes. Therefore, when evaluating the cost-benefit of investing in\nautomation and integration solutions, we at Open Function Group typically ask 3\nkey questions.</p>\n<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>\n<p>Can the exposure of sensitive data be limited by integrating with secure API\nendpoints (rather than relying on human beings to interact with those data, for\nexample)? Or by automating a data cleaning process?</p>\n<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>\n<p>Can the process take place more reliably by limiting the opportunity for human\nerror (in automating data manipulation or simple algorithmic work, for example)?</p>\n<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>\n<p>Can the process be done more quickly via automation and is there value in having\nit done faster? (The answer to the first part is almost always yes, but\nsometimes there's not actually lots of value generated by doing something\nfaster.)</p>\n<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>\n<p>Tasks that meet these 3 criteria, take a lot of time to complete or are very\nrepetitive, and/or involve moving data between apps are typically great\ncandidates for automation.</p>\n<p>If you find yourself answering “no”, then it may not be worth the investment in\nautomation... at least not yet. This is especially true if these processes are\nstill in flux or require a lot of human involvement to complete. That said, now\nmay be a good time to refine your existing workflows, think about how your\nprocesses might change at scale, and consider what <em>new</em> processes, services, or\noutcomes could be unlocked by automation.</p>\n<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>\n<p>If you want to try out automation for your organization,\n<a href=\"https://www.openfn.org/signup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">sign up</a> for OpenFn, free of charge. Check out\n<a href=\"https://docs.openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">our documentation</a> and\n<a href=\"http://www.openfn.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">website</a> to learn how to get started.</p>\n<p>Having trouble setting up your first automation \"job\"? Email us at\n<a href=\"mailto:admin@openfn.org\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">admin@openfn.org</a> for support. Our team is always\nhappy to assist and help you evaluate the total cost of ownership of automation\nsolutions.</p>",
            "url": "https://docs.openfn.org/es/articles/2020/06/24/three-questions-to-ask",
            "title": "To Automate or Not to Automate? Ask Yourself These 3 Questions.",
            "summary": "Automation can save time, unlock critical resources, and enable scale–but it",
            "date_modified": "2020-06-24T00:00:00.000Z",
            "author": {
                "name": "Aleksa Krolls"
            },
            "tags": [
                "tips"
            ]
        },
        {
            "id": "https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized",
            "content_html": "<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>\n<p>This article was originally posted by Taylor Downs, Head of Product, on\n<a href=\"https://medium.com/@taylordowns2000\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">The OpenFn Founder's blog</a> as \"The power\nof crow's feet.\"</p>\n<p>It’s Saturday Morning in Cape Town and I’ve just spent an hour talking about how\na non-profit is organized. I thought I was getting into a technical\ndiscussion—I’ve been doing system architecture discussions for years—but what we\nended up talking about was how this NGO thinks.</p>\n<p>This engagement is largely about mapping an already existing “people &amp; paper”\nbased system to technology. Vera Solutions will build a system for this client\nusing Open Data Kit for field data collection and Salesforce.com for the\nmanagement “back-end”. Because we’re not explicitly being asked to help redesign\nprocesses at this organization, the client is “telling us how things are”, then\nexpecting us to create a relational database model that facilitates\nbusiness-as-usual, only in a more efficient, digital way. Seems reasonable.</p>\n<p>This organization runs multiple programs focusing on a handful of strategic\nobjectives. They coordinate various activities in their target communities and\nreport on those activities against numerous (sometimes overlapping) indicators.\nSound familiar? As we get to the 3rd explanation of these programs, and the 11th\niteration of the system schema, it hits me…</p>\n<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>\n<p>When we—human beings—wrestle with complex problems (like managing lots of\nprograms, other humans, community stakeholders, etc.) we have the capacity to\ntrick ourselves into thinking that we have wrapped our heads around a system\n(for clinic registration, for after-school education, etc.) when, in fact, we’re\nengaging in mental hand-waving and are simply ‘papering-over’ sections which are\nsecretly not just complicated, but totally incongruous with other parts of the\nsystem. We can make ourselves believe that our logic is sound because we want it\nto be sound, when in reality the organization might be held together by good\npeople, not good, clear, defined processes. By learning a couple of key\nconcepts, it’s possible for non-technical people to articulate their thoughts\nclearly using “boxes” and “crow’s feet” and see whether or not there is an\nunderlying structure to their organization’s information.</p>\n<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>\n<p>If you can’t diagram the information structure in your organization using boxes\nand crow’s feet, it’s a smell that something isn’t quite right (or at least that\nsomething isn’t easily scalable… more on this later!). Let me show you the tools\nin the toolbox and then wrap up by waxing poetic on people, processes, and\ntechnology.</p>\n<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>\n<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>\n<p>The box is my favorite. It represents an entity in your data system. Entities\n(like <code>teachers</code>) have attributes (like <code>name</code>, <code>phone number</code>, <code>date of birth</code>,\n<code>gender</code>, etc.) Some people like thinking of entities as simple forms. The\n“Teacher Registration Form” will ask for the teacher’s name, phone number,\ngender, etc. These are the fields on your teacher entity. By submitting one of\nthese forms, you’ll add a new teacher to your database. If you’re an Excel\nperson, the attributes are columns in your <code>teachers</code> table.</p>\n<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>\n<p>The crow’s foot is my second favorite. It’s used to show relationships between\nentities. We know that teachers are related to the sessions that they conduct.\n(And <code>session</code> might be another entity, with fields like <code>date</code>,\n<code>subject taught</code>, and <code>venue</code>, to name just a few.) The crow’s foot allows us to\nspecify exactly how they are related. On that session entity, we’ll need to\nspecify the name (or ID) of the teacher who led it. On the teacher entity,\nhowever, there won’t be a field to specify the name or ID of the session…\nbecause a single teacher can lead MANY sessions. This is a one-to-many\nrelationship. The crow’s foot (that little three-pronged fella) denotes the\nmany. One teacher can have many sessions. One session, however, can only have\none teacher. See the diagram below.</p>\n<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>\n<p>If we focus just on <code>teacher</code> and <code>session</code> and think back to MS Excel, we can\nenvision a <code>teachers</code> table and a <code>sessions</code> table. Let’s put them on different\nsheets in the same workbook. On the <code>teachers</code> table, there is no column for\n<code>session</code>, but on the <code>sessions</code> table, there <em>is</em> a column for <code>teacher ID</code>.\nWe’ve just established a one-to-many relationship.</p>\n<p>Next time, we’ll talk about what’s going with the <code>attendance</code> entity above.\nIt’s sometimes called a “junction object” or a “join table”, and it’s what\nallows MANY students to be related to MANY sessions. I’ll write more on this\nnext time, but there is no magic going on, no technicalities here. The way that\nmany students are related to many sessions is through this Real World Concept\nthat we call <code>attendance</code>. Attendance is what happens when a student shows up at\na session. It’s so important to get the language right in these discussions, and\nmake sure that you’re talking about real-world concepts.</p>\n<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>\n<p>Remember that as you start to put pen to paper. And allow yourselves time (and\nmultiple drafts) to get the boxes and terminology right. Understanding\nrelational object modelling is an incredibly powerful way to organize a company.\nAs I said before, if you can’t model it with boxes and arrows, it’s a smell that\nsomething might not be conceptually sound.</p>\n<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>\n<p>Some organizations do amazing work without good conceptual systems. They rely on\nhumans, instinct, improvisation, nouse, and other not-so-clearly-defined things.\nThey might do really great work. They might get the job done. But they need to\nbe well aware of their condition and face it head on. If you can’t systematize\nyour program implementation processes, then you need to focus tremendous effort\non finding and retaining the right people.</p>\n<p>A friend once told me that “people are not scalable.” I couldn’t agree more, and\ndefend my earlier stance that if your organization’s information structure can’t\nbe defined with boxes and crow’s feet, it may be very hard for you to scale\nresponsibly. However, if you can create a ruthlessly efficient, world-class\n“people operations” system (recruitment, training, management, compensation, HR,\netc.) that ensures you’ve always got the right people to figure things out you\nmight be better off than those operating a well defined assembly-line with\ninterchangeable parts. Alas, the middle way is probably the best.</p>\n<p>That’s all for now. More soon.</p>\n<p><em>Need help organizing or scaling your organization's information or process\nflows? Contact our team of ICT4D specialists at\n<a href=\"mailto:support@openfn.org\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">support@openfn.org</a>.</em></p>",
            "url": "https://docs.openfn.org/es/articles/2020/06/16/how-information-is-organized",
            "title": "How Information Is Organized... In Organizations",
            "summary": "Does your organization's information have an underlying structure? Try this exercise using boxes and crow's feet.",
            "date_modified": "2020-06-16T00:00:00.000Z",
            "author": {
                "name": "Taylor Downs"
            },
            "tags": [
                "how-to",
                "tips"
            ]
        }
    ]
}