<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Data Gojo]]></title><description><![CDATA[Data Gojo]]></description><link>https://shivam096.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 12 Oct 2026 16:54:36 GMT</lastBuildDate><atom:link href="https://shivam096.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Guide written  by a novice creator on how to migrate Airbyte connector to Low-code]]></title><description><![CDATA[This is a guide written by a novice developer who embarked on the journey to explore the open-source community with the support of his fellow comrades.
Hola folks,
This is Shivam Agarwal, and I work as a data engineer. I'm excited to present to you a...]]></description><link>https://shivam096.hashnode.dev/guide-written-by-a-novice-creator-on-how-to-migrate-airbyte-connector-to-low-code</link><guid isPermaLink="true">https://shivam096.hashnode.dev/guide-written-by-a-novice-creator-on-how-to-migrate-airbyte-connector-to-low-code</guid><category><![CDATA[#AirbyteHackathon ]]></category><category><![CDATA[airbyte ]]></category><category><![CDATA[connector-hackathon]]></category><dc:creator><![CDATA[Shivam Agarwal]]></dc:creator><pubDate>Wed, 30 Aug 2023 14:21:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1693406141174/edddeb06-faee-4f93-b23a-716f9acd24b7.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><code>This is a guide written by a novice developer who embarked on the journey to explore the open-source community with the support of his fellow comrades.</code></p>
<p>Hola folks,</p>
<p>This is Shivam Agarwal, and I work as a data engineer. I'm excited to present to you a guide that also encapsulates my personal experience. This guide chronicles my journey of attempting to migrate an existing Airbyte source connector from Python CDK to Low-Code CDK, with Openweather being my chosen starting point. But before delving into the specifics of my endeavor, let's first gain a clear understanding of what Airbyte is.</p>
<h2 id="heading-what-is-airbyte">What is Airbyte?</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693378555603/49b76888-ba67-4c55-adde-958cd7c9db62.png" alt class="image--center mx-auto" /></p>
<p>So as per the official website :</p>
<blockquote>
<p>Airbyte is an open-source data integration engine that helps you consolidate your data in your data warehouses, lakes and databases.</p>
</blockquote>
<p>What I believe Airbyte to be :</p>
<blockquote>
<p>Airbyte is a tool that helps data engineers and data professionals to cut out the stress associated with handling data.</p>
</blockquote>
<p>Now we have a gist of the tool (I hope the one-liners did the job :P). Let's delve into the focal points of this discussion. My aim is to offer a concise breakdown of the content that lies ahead. I'll be guiding you through the process of migrating a <strong>"simple"</strong> API connector to Low-code CDK.</p>
<h1 id="heading-lets-start-with-the-basics">Let's start with the basics :</h1>
<h2 id="heading-prerequisites">Prerequisites:</h2>
<ul>
<li><p>python3 (better to have python 3.10 )</p>
</li>
<li><p>docker ( If you don't have it installed, use this link to <a target="_blank" href="https://docs.docker.com/get-docker/?_gl=1*qzombw*_ga*MTU4NzQ3MDA3Mi4xNjkzMzE1Njc0*_ga_XJWPQMJYHQ*MTY5MzM3OTM0NS4yLjEuMTY5MzM3OTM1Mi41My4wLjA.">set it up</a> )</p>
</li>
<li><p>API key of your source</p>
</li>
</ul>
<h2 id="heading-local-setup-of-airbyte">Local setup of Airbyte:</h2>
<h3 id="heading-step-1">Step 1:</h3>
<p>Fork the <a target="_blank" href="https://github.com/airbytehq/airbyte">Airbyte</a> repository and make a local clone of it in your system. If you dont know the commands refer to this - <a target="_blank" href="https://docs.github.com/en/get-started/quickstart/fork-a-repo">https://docs.github.com/en/get-started/quickstart/fork-a-repo</a></p>
<p>Clone Command -</p>
<pre><code class="lang-plaintext">git clone https://github.com/&lt;your username&gt;/airbyte.git
</code></pre>
<h3 id="heading-step-2">Step 2 :</h3>
<p>Once you have the repository cloned on your local system, cd into the airbyte root directory</p>
<pre><code class="lang-plaintext">cd airbyte
</code></pre>
<p>and run the following command</p>
<pre><code class="lang-plaintext">./run-ab-platform.sh
or
sudo ./run-ab-platform.sh --if you dont have proper priviledges set for docker
</code></pre>
<p>This will set up the Airbyte platform on your system. To validate the setup check for the Airbyte banner. Once you are able to see it visit <a target="_blank" href="http://localhost:8000"><strong>http://localhost:8000</strong></a><strong>.</strong> By default, the username is <code>airbyte</code> and password <code>password</code> (love the creativity here :P)</p>
<h3 id="heading-step-3-optional">Step 3 : (optional)</h3>
<p>If you are not able to make changes to the files like saving or updating. Use this command in the airbyte's root directory to change the permission. This is for Linux based OS like Ubuntu or macOS as well</p>
<pre><code class="lang-plaintext">sudo chown username:username -R .
</code></pre>
<p>We are done with the formalities. Let the migration of connectors begin !!!!</p>
<h1 id="heading-getting-started-with-the-templating">Getting started with the templating</h1>
<p>We need to first set up the new connector source to be migrated.</p>
<p>Before we start, we need to make the backup of the connector we want to migrate.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693400418924/9ea0ddff-55ed-4132-9e70-9ef1315112d3.gif" alt class="image--center mx-auto" /></p>
<p>For my use case, I did it by updating the source directory name and adding a "-backup" to it (something that was suggested by @bktcodedev). <strong>Make sure you do not commit this directory.</strong> This backup will help you go through the functionalities that are supported by the connector</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693380144149/e93a819b-3e51-4864-801a-822058dc4af2.png" alt class="image--center mx-auto" /></p>
<p>Now you can create the new source.</p>
<p>In order to do so first move into the template generator directory and execute the <code>./generate.sh</code> bash file. Commands for this would be as follows :</p>
<pre><code class="lang-plaintext">cd airbyte-integrations/connector-templates/generator
./generate.sh
</code></pre>
<p>This will bring up an interactive helper application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693380655494/a485f8d8-600c-4d3e-9a1b-6085f816d3ad.png" alt class="image--center mx-auto" /></p>
<p>Use the arrow keys to pick a template from the list. Select the <code>Configuration Based Source</code> template and then input the name of your connector.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693380703434/de50840a-7f96-434a-ad8d-b1d42c938f14.png" alt class="image--center mx-auto" /></p>
<p>The application will create a new directory <code>airbyte/airbyte-integrations/connectors/</code> with the name of your new connector. The generator will create a new module for your connector with the name <code>source-&lt;connector-name&gt;</code> for me it was <code>source-openweather</code>.</p>
<p>Now you have the source configured. What next? Adding the dependencies and creating a virtual environment.</p>
<h1 id="heading-creating-a-virtual-environment-and-installing-the-dependencies">Creating a Virtual Environment and installing the dependencies</h1>
<p>Let's start by creating a python virtual environment for our source. The provided command assumes that the <code>python</code> reference corresponds to a <code>python</code> version that is 3.9.0 or newer. In certain instances, <code>python</code> might direct to a Python2 setup, while <code>python3</code> indicates Python3. If this situation applies to your system, replace <code>python</code> within the command. The following <code>python</code> usages will then employ the virtual environment established for the connector. (Personal note: In my case, the Python version is 3.10)</p>
<pre><code class="lang-plaintext">cd ../../connectors/source-exchange-rates-tutorial
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
</code></pre>
<p>These steps create an initial Python environment and install the dependencies required to run an API Source connector.</p>
<p>Let's verify everything works as expected by running the Airbyte <code>spec</code> operation:</p>
<pre><code class="lang-plaintext">python main.py spec
</code></pre>
<p>This output is an indication that we are moving in the righteous path. Let's GOOO!!!!</p>
<pre><code class="lang-plaintext">{"type": "SPEC", "spec": {"documentationUrl": "https://docsurl.com", "connectionSpecification": {"$schema": "http://json-schema.org/draft-07/schema#", "title": "Python Http Tutorial Spec", "type": "object", "required": ["TODO"], "additionalProperties": false, "properties": {"TODO: This schema defines the configuration required for the source. This usually involves metadata such as database and/or authentication information.": {"type": "string", "description": "describe me"}}}}}
</code></pre>
<p>This is a simple sanity check to make sure everything is wired up correctly. More details on the <code>spec</code> operation can be found in <a target="_blank" href="https://docs.airbyte.com/connector-development/cdk-python/basic-concepts">Basic Concepts</a> and <a target="_blank" href="https://docs.airbyte.com/connector-development/cdk-python/schemas">Defining Stream Schemas</a>.</p>
<p>Now the next step would be to go through the directory structure and list out the important files. Files that we would add details to or update.</p>
<h1 id="heading-files-of-interest-where-the-doughs-at">Files of interest (where the doughs at )</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693393913204/3219a6c1-5b76-4a8b-ab3d-c0cc8d6d8aef.gif" alt class="image--center mx-auto" /></p>
<p>We are prepared to commence the implementation of the connector.</p>
<p>Throughout this tutorial, we will be modifying several files that were produced by the code generator:</p>
<ol>
<li><p><code>source-openweather/source_openweather/manifest.yaml</code>: This document serves as the connector's manifest. It outlines how data should be extracted from the API source and specifies the configurable inputs for the connector.</p>
</li>
<li><p><code>source-openweather/integration_tests/configured_catalog.json</code>: This file represents the connector's catalog. It elucidates the available data within a source.</p>
</li>
</ol>
<p>In addition, we will be generating the following files:</p>
<ol>
<li><p><code>source-openweather/secrets/config.json</code>: This configuration file will be employed for testing the connector. Its structure must adhere to the specifications outlined in the spec file.</p>
</li>
<li><p><code>source-openweather/integration_tests/invalid_config.json</code>: An invalid configuration file, which follows the schema defined in the spec file, will also be created for testing purposes.</p>
</li>
<li><p><code>source_openweather/schemas/onecall.json</code>: This file contains the schema definition for the stream we are about to implement. The JSON schema of the API response received.</p>
</li>
</ol>
<h2 id="heading-starting-with-the-big-guns">Starting with the big guns:</h2>
<h3 id="heading-manifestyaml"><code>manifest.yaml</code> :</h3>
<p>This is the file that controls it all. The command center for the connector. It has generally 2 parts:</p>
<ul>
<li><p><code>definitions</code>: This hosts all the details for different components like record selectors, retrievers, and requesters.</p>
</li>
<li><p><code>spec</code>: This section hosts the details configuration required for the integration.</p>
</li>
</ul>
<p><code>definitions</code> :</p>
<p>There are a few components that are present in the <code>definitions :</code></p>
<ol>
<li><p><code>record selector</code>: This component allows the connector to define how to retrieve the data. The selector type <code>RecordSelector</code> allows us to extract the data from the API response. The definition for the record selector can be found <a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/understanding-the-yaml-file/record-selector">here</a>. Point to note here is that the selector is used to define the path from the data. Say you want a specific part within your API response to be returned instead of all the jargon that is attached to it. We do not want our responses to be too confusing. To make sure we are extracting only the crème de la crème we use a record selector</p>
</li>
<li><p><code>requesters</code> : This component is used to prepare HTTP requests to send to the source API. Presently, there exists a sole implementation known as HttpRequester, which is characterized by</p>
<ol>
<li><p><code>url_base</code>: The root of the API source</p>
</li>
<li><p><code>http_method</code>: the HTTP method to use (GET or POST)</p>
</li>
<li><p><a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/understanding-the-yaml-file/request-options#request-options-provider">A request options provider</a>: Defines the request parameters (query parameters), headers, and request body to set on outgoing HTTP requests</p>
<ol>
<li><p>For Openweather we need to send longitude, latitude, and appid as request parameters. For that we used <code>request_parameters</code>. This option allows the definitions of request parameters that are required.</p>
<p> Fun fact: In order to implement conditional selections, one can use Jinja's conditional statements. For Eg: I used the below condition to make sure that the latitude values are always within the range of -90 to 90</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">lat:</span> <span class="hljs-string">"{% if -90.00 &lt;= config['lat']|float &lt;= 90.00 %}<span class="hljs-template-variable">{{ config['lat'] }}</span>{% else %} WRONG LATITUDE{% endif %}"</span>
</code></pre>
</li>
</ol>
</li>
<li><p><code>authenticator</code>: Defines how to authenticate to the source</p>
</li>
<li><p><code>error_handler</code>: Defines how to handle errors.</p>
</li>
</ol>
</li>
<li><p><code>retriever</code>: The data retriever defines how to read the data for a Stream and acts as an orchestrator for the data retrieval flow</p>
</li>
<li><p><code>stream_definitions</code>: This section is used to define the streams that you want to include within your connector. For Openweather, there is only one stream i.e. <code>onecall</code> . In order to define the stream you need to have a <code>base_stream</code> which is defined using DeclarativeStream and can be referenced by multiple streams. For my <code>onecall_stream</code> , we have to define the configuration related to the stream here like <code>path(The specific endpoint to fetch data from for a resource)</code> and <code>name</code> .</p>
</li>
</ol>
<p>There are other sections namely <a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/understanding-the-yaml-file/pagination"><code>pagination</code></a> and <a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/understanding-the-yaml-file/incremental-syncs"><code>incremental sync</code></a> which are not implemented in this connector since this connector does not require them. ( *sigh*)</p>
<p>The <code>definitions</code> section that I used for openweather source:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">definitions:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">RecordSelector</span>
    <span class="hljs-attr">extractor:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">DpathExtractor</span>
      <span class="hljs-attr">field_path:</span> []


  <span class="hljs-attr">requester:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">HttpRequester</span>
    <span class="hljs-attr">url_base:</span> <span class="hljs-string">"https://api.openweathermap.org/data/3.0/"</span>
    <span class="hljs-attr">http_method:</span> <span class="hljs-string">"GET"</span>
    <span class="hljs-attr">request_parameters:</span>
      <span class="hljs-attr">lat:</span> <span class="hljs-string">"{% if -90.00 &lt;= config['lat']|float &lt;= 90.00 %}<span class="hljs-template-variable">{{ config['lat'] }}</span>{% else %} WRONG LATITUDE{% endif %}"</span>
      <span class="hljs-attr">lon:</span> <span class="hljs-string">"{% if -180.00 &lt;= config['lon']|float &lt;= 180.00 %}<span class="hljs-template-variable">{{ config['lon'] }}</span>{% else %}WRONG LONGITUDE{% endif %}"</span>
      <span class="hljs-attr">appid:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ config['appid'] }}</span>"</span>
      <span class="hljs-attr">lang:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ config.get('lang')}}</span>"</span>
      <span class="hljs-attr">units:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ config.get('units')}}</span>"</span>
    <span class="hljs-attr">error_handler:</span>
      <span class="hljs-attr">response_filters:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">http_codes:</span> [ <span class="hljs-number">500</span> ]
            <span class="hljs-attr">action:</span> <span class="hljs-string">RETRY</span>
  <span class="hljs-attr">retriever:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">SimpleRetriever</span>
    <span class="hljs-attr">record_selector:</span>  
      <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/definitions/selector"</span>
    <span class="hljs-attr">paginator:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">NoPagination</span>
    <span class="hljs-attr">requester:</span>
      <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/definitions/requester"</span>
  <span class="hljs-attr">base_stream:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">DeclarativeStream</span>
    <span class="hljs-attr">retriever:</span>
      <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/definitions/retriever"</span>

  <span class="hljs-attr">onecall_stream:</span>
    <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/definitions/base_stream"</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">"onecall"</span>
    <span class="hljs-attr">retriever:</span>
      <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/definitions/retriever"</span>
    <span class="hljs-string">$parameters:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">"onecall"</span>
</code></pre>
<p><a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/understanding-the-yaml-file/reference#/definitions/Spec"><code>spec</code></a> :</p>
<p>This section is used to define source specification made up of connector metadata and how it can be configured. It hosts details like connection_specification, properties etc.</p>
<p>There are a few components that are present in the <code>spec :</code></p>
<ol>
<li><p><code>documentation_url</code> : The link to the documentation for the configured source</p>
</li>
<li><p><code>connection_specifications</code>:</p>
<ol>
<li><p><code>required</code> : This will define all the properties that are required for integration. All the properties mentioned here can't be skipped and one must define those.</p>
</li>
<li><p><code>properties</code>: This is a list of all the properties. It can host required as well as optional properties.</p>
</li>
<li><p><code>additionalProperties</code>: This is a boolean value that specifies that there might be additional properties apart from what has already been mentioned</p>
</li>
</ol>
</li>
</ol>
<p>This section can be directly copied from the existing <code>spec.json</code> file in the original source connector. Using any available <a target="_blank" href="https://www.json2yaml.com/">online converter</a> for JSON to YAML, we can get this converted to YAML and directly use that.</p>
<p>The <code>spec</code> section that I used for openweather source:</p>
<pre><code class="lang-yaml">
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">Spec</span>
  <span class="hljs-attr">documentation_url:</span> <span class="hljs-string">https://docs.airbyte.com/integrations/sources/openweather</span>
  <span class="hljs-attr">connection_specification:</span>
    <span class="hljs-attr">title:</span> <span class="hljs-string">Openweather</span> <span class="hljs-string">Spec</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
    <span class="hljs-attr">required:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">lat</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">lon</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">appid</span>
    <span class="hljs-attr">additionalProperties:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">properties:</span>
      <span class="hljs-attr">lat:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-attr">pattern:</span> <span class="hljs-string">"^[-]?\\d{1,2}(\\.\\d+)?$"</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">"Latitude, decimal (-90; 90). If you need the geocoder to automatic convert city names and zip-codes to geo coordinates and the other way around, please use our Geocoding API"</span>
        <span class="hljs-attr">examples:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"45.7603"</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"-21.249107858038816"</span>
      <span class="hljs-attr">lon:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-attr">pattern:</span> <span class="hljs-string">"^[-]?\\d{1,2}(\\.\\d+)?$"</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">"Longitude, decimal (-180; 180). If you need the geocoder to automatic convert city names and zip-codes to geo coordinates and the other way around, please use our Geocoding API"</span>
        <span class="hljs-attr">examples:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"4.835659"</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"-70.39482074115321"</span>
      <span class="hljs-attr">appid:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">"API KEY"</span>
        <span class="hljs-attr">airbyte_secret:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">units:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">"Units of measurement. standard, metric and imperial units are available. If you do not use the units parameter, standard units will be applied by default."</span>
        <span class="hljs-attr">enum :</span> 
          <span class="hljs-bullet">-</span> <span class="hljs-string">standard</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">metric</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">imperial</span>
        <span class="hljs-attr">examples:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"standard"</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"metric"</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"imperial"</span>
      <span class="hljs-comment"># dt:</span>
      <span class="hljs-comment">#   type: string</span>
      <span class="hljs-comment">#   description : "Date in UNIX format"</span>
      <span class="hljs-comment">#   example:</span>
      <span class="hljs-comment">#     - '1693023447'</span>
      <span class="hljs-attr">only_current:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
        <span class="hljs-attr">description :</span> <span class="hljs-string">"True for particular day"</span>
        <span class="hljs-attr">example:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">'true'</span>
      <span class="hljs-attr">lang:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">You</span> <span class="hljs-string">can</span> <span class="hljs-string">use</span> <span class="hljs-string">lang</span> <span class="hljs-string">parameter</span> <span class="hljs-string">to</span> <span class="hljs-string">get</span> <span class="hljs-string">the</span> <span class="hljs-string">output</span> <span class="hljs-string">in</span> <span class="hljs-string">your</span> <span class="hljs-string">language.</span>
          <span class="hljs-string">The</span> <span class="hljs-string">contents</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">description</span> <span class="hljs-string">field</span> <span class="hljs-string">will</span> <span class="hljs-string">be</span> <span class="hljs-string">translated.</span> <span class="hljs-string">See</span> <span class="hljs-string">&lt;a</span> <span class="hljs-string">href="https://openweathermap.org/api/one-call-api#multi"&gt;here&lt;/a&gt;</span>
          <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">supported</span> <span class="hljs-string">languages.</span>
        <span class="hljs-attr">enum:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">af</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">al</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ar</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">az</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">bg</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ca</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">cz</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">da</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">de</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">el</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">en</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">eu</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">fa</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">fi</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">fr</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">gl</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">he</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">hi</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">hr</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">hu</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">id</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">it</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ja</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">kr</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">la</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">lt</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">mk</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">'no'</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">nl</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">pl</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">pt</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">pt_br</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ro</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ru</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">sv</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">se</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">sk</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">sl</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">sp</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">es</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">sr</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">th</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">tr</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ua</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">uk</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">vi</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">zh_cn</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">zh_tw</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">zu</span>
        <span class="hljs-attr">examples:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">en</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">fr</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">pt_br</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">uk</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">zh_cn</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">zh_tw</span>
</code></pre>
<h2 id="heading-coming-to-the-essentials">Coming to the essentials</h2>
<h3 id="heading-configuredcatalogjson"><code>configured_catalog.json</code></h3>
<p>The catalog is used to test the connection as well as read data from the stream.The catalog is structured as a list of <code>AirbyteStream</code>. In the case of a database a "stream" is analogous to a table. This configuration is used to define the properties that aid to the reading of data.</p>
<p>Let's walk through what each field in a stream means.</p>
<ul>
<li><p><code>name</code> - The name of the stream.</p>
</li>
<li><p><code>supported_sync_modes</code> - This field lists the type of data replication that this source supports. The possible values in this array include <code>FULL_REFRESH</code> (<a target="_blank" href="https://docs.airbyte.com/understanding-airbyte/connections/full-refresh-overwrite">docs</a>) and <code>INCREMENTAL</code> (<a target="_blank" href="https://docs.airbyte.com/understanding-airbyte/connections/incremental-append">docs</a>).</p>
</li>
<li><p><code>json_schema</code> - This field is a <a target="_blank" href="https://json-schema.org/understanding-json-schema">JsonSchema</a> object that describes the structure of the data. Notice that each key in the <code>properties</code> object corresponds to a column name in our database table.</p>
</li>
</ul>
<p><code>configured_catalog</code> for Openweather :</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"streams"</span>: [
    {
      <span class="hljs-attr">"stream"</span>: {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"onecall"</span>,
        <span class="hljs-attr">"json_schema"</span>: {},
        <span class="hljs-attr">"supported_sync_modes"</span>: [<span class="hljs-string">"full_refresh"</span>]
      },
      <span class="hljs-attr">"sync_mode"</span>: <span class="hljs-string">"full_refresh"</span>,
      <span class="hljs-attr">"destination_sync_mode"</span>: <span class="hljs-string">"overwrite"</span>
    }
  ]
}
</code></pre>
<p>Once the entries are populated. We need to check if the connection is getting established with the source properly. For that, we would need to run the following command</p>
<pre><code class="lang-plaintext">python main.py check --config secrets/config.json
</code></pre>
<p>A successful run would return the below output :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693398987488/b3e52543-8bc4-4324-8954-ddc525b33c0d.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-schemajson"><code>schema.json</code> <strong>:</strong></h3>
<p>Think of schema files like those trusty old sneakers you've got tucked away in the closet. You only really dig them out when your funky dance moves suddenly become cool again. Since the API we want to use has not changed the API response format. We will use the existing schema file as it is with a minor addition - <code>"additionalProperties": true</code>. This property would allow the addition of extra properties without specific definitions. In case you do not have the schema handy, use <a target="_blank" href="https://chat.openai.com/">ChatGPT</a> to get the API response created in the following <a target="_blank" href="https://docs.airbyte.com/connector-development/schema-reference/">format</a>. (This AI is making me lazy LOL! )</p>
<p>Schema I used :</p>
<p><code>source_openweather/schemas/onecall.json</code></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"$schema"</span>: <span class="hljs-string">"http://json-schema.org/draft-07/schema#"</span>,
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
  <span class="hljs-attr">"additionalProperties"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"properties"</span>: {
    <span class="hljs-attr">"lat"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
    },
    <span class="hljs-attr">"lon"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
    },
    <span class="hljs-attr">"timezone"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
    },
    <span class="hljs-attr">"timezone_offset"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
    },
    <span class="hljs-attr">"current"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
      <span class="hljs-attr">"additionalProperties"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"properties"</span>: {
        <span class="hljs-attr">"dt"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"sunrise"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"sunset"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"temp"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"feels_like"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"pressure"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"humidity"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"dew_point"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"uvi"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"clouds"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"visibility"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"wind_speed"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"wind_deg"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
        },
        <span class="hljs-attr">"weather"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>
        },
        <span class="hljs-attr">"rain"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
          <span class="hljs-attr">"additionalProperties"</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">"properties"</span>: {
            <span class="hljs-attr">"1h"</span>: {
              <span class="hljs-attr">"type"</span>: <span class="hljs-string">"number"</span>
            }
          }
        }
      }
    },
    <span class="hljs-attr">"minutely"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>
    },
    <span class="hljs-attr">"hourly"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>
    },
    <span class="hljs-attr">"daily"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>
    },
    <span class="hljs-attr">"alerts"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>
    }
  }
}
</code></pre>
<h3 id="heading-secretsconfigjson"><code>secrets/config.json</code>:</h3>
<p>This file contains the access keys and the required properties that are needed for the connector to function properly. Any values defined here won't be committed to the git as this is a git-ignored file.</p>
<p><code>config.json</code> for Openweather :</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"lat"</span>: <span class="hljs-string">"33.44"</span>,
  <span class="hljs-attr">"lon"</span>: <span class="hljs-string">"-94.04"</span>,
  <span class="hljs-attr">"appid"</span>: <span class="hljs-string">"api-key"</span>,
  <span class="hljs-attr">"lang"</span>: <span class="hljs-string">"fr"</span>,
  <span class="hljs-attr">"units"</span>: <span class="hljs-string">"standard"</span>
}
</code></pre>
<h3 id="heading-integrationtestsinvalidconfigjson"><code>integration_tests/invalid_config.json:</code></h3>
<p>This file contains invalid properties that are needed for the integration tests.</p>
<p><code>invalid_config.json</code> for Openweather :</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"lat"</span>: <span class="hljs-string">"12.1"</span>,
  <span class="hljs-attr">"lon"</span>: <span class="hljs-string">"-43.1"</span>,
  <span class="hljs-attr">"appid"</span>: <span class="hljs-string">"wrongkey"</span>
}
</code></pre>
<p>Now we are done with the basic configurations. It is time to harvest what we have sown. Get ready for the big battle.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693397639709/15c9c52e-5296-4e4f-b5b6-5ed3e69ada0c.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-does-the-connector-return-data">Does the connector return data?</h2>
<p>To test if we can read the data from the source, we need to define our <code>configured_catalog.json</code> , <code>schema.json</code> and the <code>manifest.yaml</code>. For our purposes, we have already configured them above.</p>
<p>Now to validate the run we need to execute the following command</p>
<pre><code class="lang-plaintext">python main.py read --config secrets/config.json --catalog integration_tests/configured_catalog.json
</code></pre>
<p>Here the <code>read</code> operation reads the data from the source and it looks something like this -</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693398062455/4680c71d-342d-4a63-82af-20d9a0aaab19.png" alt class="image--center mx-auto" /></p>
<p><em>"Houston, We have secured connection! I repeat we have a connection! "</em></p>
<p>Based on the above screenshot we can confirm that the data is being read successfully. If you are facing any issues try to debug or use <a target="_blank" href="https://www.kapa.ai/">Kapa</a> - the saviour on Slack.</p>
<h2 id="heading-whats-left">What's left ???????</h2>
<h3 id="heading-testing-the-connector"><s>Testing the connector</s></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693398466620/233e929c-e242-4f18-a1a5-a6b320aa9dfb.gif" alt class="image--center mx-auto" /></p>
<p>So testing would make you believe in God again, you might find yourself wishing for his return to Earth, hoping that he would assist you in successfully passing the acceptance tests. But then again if everything is in order it would be a smooth ride. To configure tests, you need to update the <code>acceptance-test-config.yml</code> file. This will host the tests you want your connector to pass. Some types of tests are</p>
<ul>
<li><p>Test Connection: Verify that a check operation issued to the connector with the input config file returns a successful response</p>
</li>
<li><p>Test Discovery: Verifies when a <code>discover</code> operation is run on the connector using the given config file, a valid catalog is produced by the connector</p>
</li>
<li><p>Test Basic Read: Configuring all streams in the input catalog to full refresh mode verifies that a read operation produces some RECORD messages</p>
</li>
</ul>
<p>You could reuse the original <code>acceptance-test-config.yml</code> from the back. Just make sure the formatting is proper and the tests are valid.</p>
<p>The <code>acceptance-test-config.yml</code> for Openweather:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">connector_image:</span> <span class="hljs-string">airbyte/source-openweather:dev</span>
<span class="hljs-attr">acceptance_tests:</span>
  <span class="hljs-attr">connection:</span>
    <span class="hljs-attr">tests:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">config_path:</span> <span class="hljs-string">"secrets/config.json"</span>
        <span class="hljs-attr">status:</span> <span class="hljs-string">"succeed"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">config_path:</span> <span class="hljs-string">"integration_tests/invalid_config.json"</span>
        <span class="hljs-attr">status:</span> <span class="hljs-string">"failed"</span>
  <span class="hljs-attr">discovery:</span>
    <span class="hljs-attr">tests:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">config_path:</span> <span class="hljs-string">"secrets/config.json"</span>
        <span class="hljs-attr">backward_compatibility_tests_config:</span>
            <span class="hljs-attr">disable_for_version:</span> <span class="hljs-string">"0.1.6"</span>
  <span class="hljs-attr">basic_read:</span>
    <span class="hljs-attr">tests:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">config_path:</span> <span class="hljs-string">"secrets/config.json"</span>
        <span class="hljs-attr">configured_catalog_path:</span> <span class="hljs-string">"integration_tests/configured_catalog.json"</span>
  <span class="hljs-attr">incremental:</span> 
    <span class="hljs-attr">bypass_reason:</span> <span class="hljs-string">"This connector does not implement incremental sync"</span>
</code></pre>
<p>Once you have this configured start with the prayers, I mean with the next steps. Starting with the creation of a docker image for the source. Run the following command in the source root directory</p>
<pre><code class="lang-plaintext">docker build . -t airbyte/source-openweather:dev
</code></pre>
<p>Make sure to name your docker image properly. Once this is built successfully, we need to run the test script. There are two ways to run it, either by running it through the docker or through the terminal. To run it through the terminal execute the following command</p>
<pre><code class="lang-plaintext"> python -m pytest integration_tests -p integration_tests.acceptance
</code></pre>
<p>It should return something of this sorts</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693399901483/94251b23-f1cb-4960-8a31-f93ecb23be3e.png" alt class="image--center mx-auto" /></p>
<p>Or you could use the docker way. For that, you need to run the following command from the connector root</p>
<pre><code class="lang-plaintext">./acceptance-test-docker.sh
</code></pre>
<p>Once you run the code and the tests pass. You could thank the heavens. You have successfully migrated a connector.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693400075886/aa67413e-2ad0-4294-82e5-8a1368efef9c.gif" alt class="image--center mx-auto" /></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>With minimal coding experience and a knack for finding ways to cut corners, you have successfully migrated the connector to Low-code CDK. Following this step-by-step guide/tutorial/personal experience, you have learned how to migrate an existing source connector to Low-code. You have learned how to create and configure the <code>manifest.yml</code> file, reuse the existing <code>schema.json</code> file with minor changes and defining the <code>acceptance-test-config.yml</code> .</p>
<h1 id="heading-places-to-look-for-references"><strong>Places to look for references</strong></h1>
<ul>
<li><p><a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/low-code-cdk-overview"><strong>Airbyte Docs</strong></a> <strong>(holy grail)</strong></p>
</li>
<li><p><a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/tutorial/getting-started"><strong>Low-Code CDK Tutorial</strong></a></p>
</li>
<li><p><a target="_blank" href="https://docs.airbyte.com/connector-development/config-based/understanding-the-yaml-file/yaml-overview"><strong>Low-Code CDK YAML overview</strong></a> <strong>(holy grail version 2)</strong></p>
</li>
<li><p><a target="_blank" href="https://docs.airbyte.com/connector-development/tutorials/cdk-tutorial-python-http/getting-started"><strong>Python CDK Tutorial</strong></a></p>
</li>
<li><p><a target="_blank" href="https://slack.airbyte.io/?_gl=1*1r7uqy2*_ga*MjQ4MzQ4MzI2LjE2ODUwMzA5ODM.*_ga_HDBMVFQGBH*MTY5MTI0NjkyOS4yNC4wLjE2OTEyNDY5MjkuMC4wLjA.&amp;_ga=2.117952269.506652050.1690873152-248348326.1685030983"><strong>Airbyte-slack</strong></a></p>
</li>
</ul>
<h3 id="heading-special-mentions">Special mentions :</h3>
<p>Thank you for your help :)</p>
<ul>
<li><p><strong>Avinash Gour</strong></p>
</li>
<li><p><strong>bktcodedev</strong></p>
</li>
<li><p><strong>Mikhail Masyagin</strong></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693394021023/2ad2b527-b1ab-491f-9cff-c8a03649c3c5.gif" alt class="image--center mx-auto" /></p>
<p>  <code>Disclaimer: This guide is written by a developer who has tried his hands on the migration for the first time. Please do reach out for suggestions and advice</code></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>