<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[cdapio - Medium]]></title>
        <description><![CDATA[CDAP is a 100% open-source framework for build data analytics applications - Medium]]></description>
        <link>https://medium.com/cdapio?source=rss----52cb94a9300a---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>cdapio - Medium</title>
            <link>https://medium.com/cdapio?source=rss----52cb94a9300a---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 17 May 2026 10:05:35 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/cdapio" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Announcing CDAP 6.2.0 Release]]></title>
            <link>https://medium.com/cdapio/announcing-cdap-6-2-0-release-584f85e40775?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/584f85e40775</guid>
            <category><![CDATA[data-integration]]></category>
            <category><![CDATA[data-analytics]]></category>
            <category><![CDATA[sso]]></category>
            <category><![CDATA[cdap]]></category>
            <dc:creator><![CDATA[Edwin Elia]]></dc:creator>
            <pubDate>Mon, 01 Jun 2020 13:00:08 GMT</pubDate>
            <atom:updated>2020-06-01T15:42:34.631Z</atom:updated>
            <content:encoded><![CDATA[<p>On behalf of the CDAP community, it is my pleasure to announce the release of CDAP version 6.2.0. This release introduces Replication, an easy way to replicate changes from transactional databases into analytical data warehouses. It also enhances the Google Cloud Dataproc runtime provisioner to use the native Google Cloud Dataproc’s job APIs. Additionally, it includes a few improvements to the Pipeline Studio that enhance the user experience of building pipelines.</p><h3>Replication</h3><p>Replication allows users to create replication pipelines easily. The user interface guides users through the steps of configuring the source database and then selecting the tables and columns from the database to be replicated. Once users have done adding the target configuration, the system will run an assessment of the configuration to determine whether there is any potential issue that needs to be addressed before deploying the pipeline. An assessment stage also reports on the possible issues during replication, including data type mappings between the source and target databases.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jtTCMvw6nBRs5T3M" /><figcaption>Select tables and columns to replicate</figcaption></figure><h3>Google Cloud Dataproc Runtime Improvement</h3><p>Previously, Google Cloud Dataproc runtime was utilizing SSH for job submission. This resulted in a requirement that port 22 be open for the environment running CDAP. With this improvement, the job submission uses native Google Cloud Dataproc APIs, thus not requiring port 22 to be open anymore.</p><h3><strong>Pipeline Studio Improvements</strong></h3><p>Users can now select multiple plugins by dragging and making selections. Once the plugins are selected users can move, copy, or delete the plugins. Additionally a right click is now possible in the Pipeline Studio canvas. By right clicking, users can add a new wrangler connection or do common actions such as zooming and aligning the plugins.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ClICNq9rgrmd1EIE" /><figcaption>Right click on the canvas to open the menu</figcaption></figure><p><a href="https://cdap.io/get-started">Download CDAP 6.2.0</a> today and take it for a spin! Also consider helping us develop the platform by <a href="http://%20cdap-user@googlegroups.com">reaching out to the community</a> with any comments, feedback, suggestions, or improvements or by creating and following <a href="https://issues.cask.co/browse/CDAP">JIRA issues</a> and submitting <a href="https://github.com/cdapio/cdap">pull requests</a>.</p><p>For Hadoop distributions packages, you can build them from the following repositories:</p><ul><li><a href="https://github.com/cdapio/cm_csd">Cloudera</a></li><li><a href="https://github.com/cdapio/cdap-ambari-service">Ambari</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=584f85e40775" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/announcing-cdap-6-2-0-release-584f85e40775">Announcing CDAP 6.2.0 Release</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CI/CD and Change Management for Pipelines — Part 3]]></title>
            <link>https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-3-be2e217b897f?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/be2e217b897f</guid>
            <category><![CDATA[gcp]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[ci-cd-pipeline]]></category>
            <category><![CDATA[data-integration]]></category>
            <category><![CDATA[cdap]]></category>
            <dc:creator><![CDATA[Tony Hajdari]]></dc:creator>
            <pubDate>Wed, 08 Apr 2020 13:15:40 GMT</pubDate>
            <atom:updated>2020-04-08T13:15:39.957Z</atom:updated>
            <content:encoded><![CDATA[<h3>CI/CD and Change Management for Pipelines — Part 3</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DPTdj9QxtjhZPhQ_Z7kNMw.png" /></figure><p>Welcome to the third installment of this four part series. In the <a href="https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-1-1b4100aef66a">first article</a> I discussed some of the concepts related to continuous integration and testing. In the <a href="https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-2-a286e806c2f2">second article</a> we got into some hands-on examples for extracting pipelines from CDF/CDAP and used GitHub as a repository for storing pipelines and related artifacts.</p><p>In this article we’ll discuss the process of migrating artifacts from GitHub into a TEST, QA, or PROD environment, and explore automation options by leveraging the API more broadly.</p><p>Now that you have your pipelines checked into GitHub, deploying those pipelines onto another environment, like Cloud Data Fusion on GCP for example, is fairly straightforward. Once again, there are two ways we can accomplish this task. Either by using the UI in CDF/CDAP or by using the API. When dealing with one or two pipelines, using the UI is fairly easy and convenient, but when you have many pipelines to migrate this process can become cumbersome.</p><p>So, once again, it’s important to look at your checklist for what components a pipeline relies on, things like namespace preferences, custom plugins or UDDs, etc., and plan your migration accordingly.</p><h3>GitHub Workflow</h3><p>I’ve decided to use a migration strategy that relies on named branches in GitHub that correlate to my CDF/CDAP environments. Thus, I have branches named; development, test, qa, and so on. Any changes merged into the development branch can be merged into the test branch so that the pipelines in that branch can be tested on an environment that may resemble production.</p><h4>Merge Development to Test</h4><p>We start off by creating a pull request against the test branch so that our development work is merged to the test branch.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bVZCEQTIaHA_v9sX" /></figure><p>Next you create the pull request and provide a description for the PR.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rB6r3b9gOJipV-OM" /></figure><p>Once the PR has been created you can merge it to the test branch.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YJvGmzx3lWbsleY7" /></figure><p>Finish by clicking on the confirm merge button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*W5P19TUIncHcfDRE" /></figure><p>Once merged you will see a confirmation. The test branch now reflects all the changes in the dev branch.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VUJWJ3EG6iliP9LI" /></figure><h3>Clone Test Branch</h3><p>With the development branch merged into test, I’m now ready to clone the test branch and deploy my pipelines to the TEST environment. I’m specifying that I want to clone the test branch with the “-b test” parameter. Once the repo is cloned I can navigate into the pipelines folder and see the pipelines available for testing. My shell also provides a visual cue as to which branch I’m rooted in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*WCtTjX4kq2lRZwXd" /></figure><h3>Deploy Pipeline</h3><p>The procedure you use for deploying pipelines can vary when it comes to loading a number of pipelines in bulk, but the strategies to consider include using the CLI in combination with shell scripting, or using the REST API and writing a deployment utility in the language of your choice — similar to the extract-pipelines.py utility I wore for downloading all the pipelines from a CDAP instance, regardless of which namespace they belong to.</p><p>The CLI option is convenient because it’s included with your CDAP sandbox and you can automate the majority of functions you need for automating your deployments. But, if you really want maximum flexibility and the ability to programmatically control all aspects of the deployment, configuration, validation, and execution phases then you’ll want to use full fledged language like Python, Java, Go, or whatever programming language you fancy for working with REST APIs.</p><p>I have two pipelines that I need to deploy, based on what’s in the pipelines folder, and since I did not organize the pipelines into a namespace hierarchy I can deploy them as I like, into any namespace on the target system. But, what if I had few namespaces in my TEST or PROD environments, how would I know which namespace to deploy them to? Preferably the development environment should be configured to contain the same namespaces you intend to use in production so that the export script can create folders with the namespace name and place the associated pipelines for that namespace under the respective folder.</p><p>As and example, here’s how you can deploy a pipeline via REST:</p><blockquote>curl -X PUT “http://localhost:11015/v3/namespaces/<strong>NAMESPACE_NAME</strong>/apps/<strong>Titanic_02</strong>&quot; -d “@./Titanic_02-cdap-data-pipeline.json”</blockquote><p>If we need to loop through all the pipelines that need to be deployed then we can write a script/program to replace the namespace name and the pipeline name in the URL as necessary.</p><p>To illustrate, in the example below I deploy the pipeline to a namespace called BAR.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lzhyqUXmSG38Q7sJ" /></figure><h3>Deployment Gotchas</h3><p>As I mentioned in the previous article, you’ll want to have a checklist of all the components that a pipeline relies on when you deploy it in another environment. Here’s what to look out for — especially if you will be writing your own automation utility.</p><h4>Namespaces</h4><p>As mentioned earlier, you can use the CDAP sandbox CLI to access a remote CDAP instance. To connect to a remote instance via the CLI add the <strong><em>“ — uri [IP_ADDRESS|HOSTNAME]”</em></strong> to the cdap cli command. If you have added the CDAP executable to your path then the command would look like this:</p><blockquote>cdap cli — uri <a href="http://my_cdap_server.example.com:11015/">http://my_cdap_server.example.com:11015</a></blockquote><p>A CDF/CDAP instance can have any number of namespaces and it’s probably a good idea to validate that the namespaces match from one environment to another. Here’s how to get a list of namespaces using both the CLI and the REST API:</p><p><strong>CLI:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*y-iRMJpMdNT-3HdN" /></figure><p><strong>REST:</strong></p><blockquote>curl — request GET \</blockquote><blockquote>— url <a href="http://localhost:11015/v3/namespaces">http://localhost:11015/v3/namespaces</a></blockquote><p>Now that you know how to retrieve the list of namespaces from a CDAP environment, it’s just as easy to create a namespace. Using curl the command looks like this:</p><blockquote>curl — request PUT \</blockquote><blockquote>— url <a href="http://localhost:11015/v3/namespaces/NAMESPACE_NAME">http://localhost:11015/v3/namespaces/<strong>NAMESPACE_NAME</strong></a></blockquote><p>Make sure to replace the namespace name with your own namespace. Once again, if you are scripting this then you can loop through namespace names, either from the folder hierarchy that was created by the export pipelines utility, or if you exported the namespace names into a separate config file — which is what I’d recommend.</p><h4><strong>Preferences</strong></h4><p>The next thing to watch out for is preferences that may have been set for each namespace and/or pipeline. There are a number of levels where preferences can be set, so make sure to take a look at the official documentation <a href="https://docs.cdap.io/cdap/6.1.1/en/reference-manual/http-restful-api/preferences.html">here</a> for a better understanding. It’s important to at least capture any preferences related to a namespace as this is where global settings may have been set for macro key:value pairs associated to a namespace.</p><p>Here’s how to retrieve preferences for a namespace via REST:</p><blockquote>curl — request GET \</blockquote><blockquote>— url <a href="http://localhost:11015/v3/namespaces/NAMSPACE_NAME/preferences">http://localhost:11015/v3/namespaces/<strong>NAMSPACE_NAME</strong>/preferences</a></blockquote><h4><strong>Plugins</strong></h4><p>Probably the biggest gotcha is the absence of a plugin in the target environment when you deploy a pipeline. Unlike the convenience of the UI, the REST API does not provide a single interface for handling all the plugin related issues you may encounter with your pipelines.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Lvq_DmbxNbHcQByB" /></figure><p>It is up to you to check whether a plugin exists on the target system and if it is at the correct version given your pipeline’s generation and the version of CDF/CDAP you are deploying to. Therefore, your plugin deployment utility has to perform all of the validation tasks in order for the pipeline to work properly on a target environment.</p><p>You need to start by checking the contents of the pipeline JSON for the version of CDAP and the list of plugins used in that pipeline, along with their respective versions. Next you need to check the target system to determine what plugins it has in any given namespace — again, different namespaces can have different user scope plugins. Based on this information you can make a comparison of the source and target environments and decide how to resolve the discrepancies with versioning and missing plugins.</p><p>Missing plugins would need to be uploaded, and if the plugin versions are newer or older than the source environment, then you need to either upload the matching versions as defined in your pipeline or modify the pipeline JSON so that the plugin versions match with the versions on the target environment.</p><p>Yikes…that’s quite a lot! Don’t fret though, the documentation is available <a href="https://docs.cdap.io/cdap/6.1.1/en/reference-manual/http-restful-api/index.html">here</a>.. When it comes to a production workflow, you’ll most likely want to keep your development environment identical to the rest of the environments (TEST &amp; PROD) so that you don’t have to deal with all of the versioning issues. This way you can just focus on missing plugins, as would be the case if you are using custom built plugins or plugins from the hub. As before, you can use the CLI or REST API.</p><p>Let’s look at the CLI first. Make sure you are rooted at the correct namespace first — “<strong>use namespace NAMESPACE_NAME</strong>”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*q8wVvcqcYtoxCJWz" /></figure><p>Once in the desired namespace load the plugin artifacts:</p><blockquote>load artifact /path/to/plugin.jar config-file artifact /path/to/plugin.json</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ZJ35KVbhwf1oF1fS" /></figure><p>Example using REST API:</p><blockquote>curl -w”\n” -X POST “http://localhost:11015/v3/namespaces/FOO/artifacts/trash-plugin&quot; \</blockquote><blockquote>-H ‘Content-Type: application/octet-stream’ \</blockquote><blockquote>-H “Artifact-Extends: system:cdap-data-pipeline[6.0.0-SNAPSHOT,7.0.0-SNAPSHOT)/system:cdap-data-streams[6.0.0-SNAPSHOT,7.0.0-SNAPSHOT)/system:cdap-etl-batch[6.0.0-SNAPSHOT,7.0.0-SNAPSHOT)/system:cdap-etl-realtime[6.0.0-SNAPSHOT,7.0.0-SNAPSHOT)” \</blockquote><blockquote>— data-binary @./trash-plugin-1.2.0.jar</blockquote><h3>Testing</h3><p>The whole point of cloning the test branch was to test our pipelines, so let’s get to it. As before, we would want to automate this process as much as possible as well. So what do we need to make sure we can run a test?</p><p>First thing you need to make sure is that the data source you used in your test will be available to you in your TEST environment. We provided the source data in the repo to automate the testing so all you need to do is copy the file to the target environment if the test data is not already there. In this case the cloned repo has everything we need, if you cloned the test branch onto the TEST server that is. In most scenarios this would not be the case, as the test environment may be remote to the system orchestrating the tests.</p><p>Most likely you will be using macros to define what source data you will be working with, so it makes sense to make these values dynamic and configure them with namespace preferences — see, this is why preferences are important!</p><p>Finally, if you have multiple pipelines to test it would be far more practical to create a script to loop through all the pipelines to test rather than executing them one by one.</p><blockquote>curl -X POST “http://localhost:11015/v3/namespaces/default/apps/Titanic_02/workflows/DataPipelineWorkflow/start&quot; \</blockquote><blockquote>-d ‘{ “input.path”:”/Users/veton/code/misc/cdap_blog/cdap-pipelines”, “output.path”:”/Users/veton/code/misc/cdap_blog/cdap-pipelines” }’</blockquote><p>In the Titanic-02 pipeline I used macros, so I can pass those parameters in as a JSON object to the API call. Once again, if we were to write a program or a script to loop through all the pipeline tests we would extract the macro and preference values from a file and pass them in at runtime.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kGxjbnxB5WjbRlNQzLVsMg.gif" /></figure><p>As you can see, when we invoke the pipeline execution via the REST API the UI swings into action and gives visual feedback of the activity taking place. And of course, we can see the results of our test in the output destination specified by the macro variable.</p><h3>Conclusion</h3><p>In this article we learned how to use GitHub to promote pipeline artifacts from one branch to another to perform testing. You can repeat this process to promote your pipeline artifacts all the way through to production. Besides the git workflow for source control we discussed techniques for automating your deployment process using the CLI and REST API.</p><p>You can get as complex as you need to in order to have a fully automated system for deployment and testing, and I leave it up to you to come up with some creative ideas on how to accomplish this goal. There is a lot that can be done by leveraging the API with programming languages like Python, Java, Go, and many others, and for general automation shell scripting works great. Experiment with some of these and see how much mileage you can get out of a simpler approach first.</p><p>As we look to wrap up this series, we’ll dive a little deeper into automation and take a look at Jenkins as a way to automate some of the tasks we’ve done manually so far. Stay tuned for the next article.</p><p>Until next time, stay safe and healthy, and mind your social distancing!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=be2e217b897f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-3-be2e217b897f">CI/CD and Change Management for Pipelines — Part 3</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CI/CD and Change Management for Pipelines — Part 2]]></title>
            <link>https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-2-a286e806c2f2?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/a286e806c2f2</guid>
            <category><![CDATA[gcp]]></category>
            <category><![CDATA[data-analytics]]></category>
            <category><![CDATA[git]]></category>
            <category><![CDATA[cicd]]></category>
            <category><![CDATA[cdap]]></category>
            <dc:creator><![CDATA[Tony Hajdari]]></dc:creator>
            <pubDate>Mon, 16 Mar 2020 13:01:01 GMT</pubDate>
            <atom:updated>2020-04-06T14:19:59.762Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>CI/CD and Change Management for Pipelines — Part 2</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*KYUGcrr13eQP3B8M" /></figure><p>Welcome to the second article in this four part series. In the <a href="https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-1-1b4100aef66a">first article</a> I discussed some of the concepts related to continuous integration and testing. In this article we’ll get into some hands-on examples for extracting pipelines from CDF/CDAP and use GitHub as a repository for storing pipelines and related artifacts.</p><p>I will cover the following topics in this article:</p><ul><li>Creating a checklist of all the artifacts you will need to test in a target environment</li><li>How to set up a GitHub project to house our pipeline artifacts</li><li>How to export pipelines from CDF/CDAP using the export tools in the UI and how to do it via the ReST API</li><li>How to check in our development pipelines to GitHub</li><li>How to deploy a pipeline to an alternate environment using both the UI and the ReST API</li></ul><h3><strong>Prepare a Checklist</strong></h3><p>A pipeline exported in JSON format from CDF/CDAP defines a <a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">Directed Acyclic Graph</a> (DAG) containing the sequence of operations that were designed visually, the plugins that are used by that DAG, and some configuration information for other components of the ecosystem where the pipeline will be executed.</p><p>So, if you already have the exported pipeline JSON, what else might you need?</p><p>The exported pipeline JSON itself is enough to recreate the visual representation of the pipeline on another instance of CDF/CDAP, but you will also need all the configuration information from the source system which is not contained in the pipeline itself. Therefore, it’s prudent to create a checklist of all the information you will need when promoting a pipeline from one environment to another.</p><p>Here’s all the information you need to take into consideration:</p><ul><li><strong>Pipeline</strong> — The exported JSON of the pipeline DAG.</li><li><strong>Plugins</strong> — These are the actual JAR files containing the code of the plugins themselves. Your pipeline might have different versions of the plugins or it may have custom plugins and UDDs that may not exist on the target system. If you used custom plugins and UDDs then you will need to transfer those artifacts as well.</li><li><strong>Datasets</strong> — There is a good chance that the dataset you used for testing locally on your CDAP Sandbox instance might not be the same as the one on the target system.</li><li><strong>Preferences</strong> — You can set preferences globally at the system level or at the namespace level, and if your pipelines use any of these preferences then you will want to ensure that the target environment is configured the same.</li><li><strong>Macros</strong> — In order to make your pipelines portable across environments it makes sense to set any field that will change dynamically, based on the environment that it will be executed on, as macros.</li></ul><p>I’ll discuss how to invoke a pipeline test on a target environment with the requisite system preferences and macro setting in the last article in the series. For now, make sure you stash your pipelines, plugins, and datasets into the git repository. Preferences and macros are key-value pairs and can be represented nicely in JSON format. This is the format I will use in later articles for storing and porting that information.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Oh1Npvp0BSJMlP6m" /></figure><h3><strong>GitHub Repo</strong></h3><p>If you are new to GitHub and unfamiliar with <strong><em>git</em></strong> commands I highly recommend you read up on the topic from the multitude of sources available on the internet. This will not be a tutorial on Git, but you should be able to follow along with the git workflow. The scenario I will use in this project is a two person team that works in tandem to both develop and review the work of the other party.</p><p>The github repo for these examples can be found here: <a href="https://github.com/vhajdari/cdap-pipelines">https://github.com/vhajdari/cdap-pipelines</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*SwAJmAYDOQTkoRM5" /></figure><p>You can start off by creating a new fork of the GitHub repo I set up for this project. Once you have forked the project you can clone it to your local machine.</p><blockquote>git clone <a href="https://github.com/vhajdari/cdap-pipelines.git">https://github.com/vhajdari/cdap-pipelines.git</a></blockquote><p>This will create a local folder named <strong>cdap-pipelines</strong> containing the contents of the git repo.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Yjq4I6-PtMvc9OBr" /></figure><p>I created another user in github and forked the <a href="https://github.com/vhajdari/cdap-pipelines">https://github.com/vhajdari/cdap-pipelines</a> repo into the new account. Keep in mind that you will need to create your own repo in order to check in new files.</p><h3><strong>Export Pipelines</strong></h3><p>I developed a rudimentary pipeline with three stages that looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KwwnzaPyWeIMMr_0c_-tUw.png" /></figure><p>To export this pipeline from the UI click on the <strong>Actions</strong> icon on the top right of the screen and select <strong>Export</strong>. This will bring up a new window that will let you inspect the pipeline JSON. Click <strong>Export</strong> to save the file. The pipeline JSON can now be added to your GitHub repo.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tfrlQnttI2Yh5ctaE28rog.png" /></figure><p><strong>Bulk Export Pipelines</strong></p><p>Exporting a one pipeline at a time can become very cumbersome if you have a large number of pipelines that you need to export. Unfortunately there is no way to bulk export all your pipelines from the UI. So, how can we get around this problem? This is where the ReST API comes to the rescue.</p><p>If you are familiar with <strong>curl</strong> or use tools like Postman or Insomnia, you can quickly get a handle on how the ReST API works. Go get a listing of all the pipelines in CDF/CDAP you invoke the following HTTP request using the GET option:</p><blockquote><a href="http://localhost:11015/v3/namespaces/default/apps">http://localhost:11015/v3/namespaces/default/apps</a></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*TZS1-qtyZo6fi5Lx" /></figure><p>This gives you a listing of all the deployed pipelines in the default namespace. In this example I used Insomnia. The curl version of this request is:</p><blockquote>curl — request GET \</blockquote><blockquote>— url <a href="http://localhost:11015/v3/namespaces/default/apps">http://localhost:11015/v3/namespaces/default/apps</a> \</blockquote><blockquote>— header ‘content-type: application/x-www-form-urlencoded’</blockquote><p>You will notice that this API request simply returns a listing of all the pipelines, but our goal was to export all the pipelines all at once! So, how can we accomplish that?</p><p>Well, once we have the list of the pipelines we can iterate over that list and extract the JSON of each pipeline. The API request to get the pipeline JSON for Titanic_01 is as follows:</p><blockquote>curl — request GET \</blockquote><blockquote>— url &#39;http://localhost:11015/v3/namespaces/<strong>default</strong>/apps/<strong>Titanic_01</strong>&#39;</blockquote><p>To iterate over a list of pipelines and extract the one we want we would simply replace the last portion of the URL that has the pipeline name with the desired pipeline name.</p><p>This API returns the same JSON that we saw in the UI, and you can use the output option in <strong>curl</strong> to write out the content to a file. As you can ascertain from these API calls, we could easily script these HTTP requests to extract all the contents to the file system — and this is exactly what I have done with a simple Python script that extracts all pipelines from all namespaces and writes them out to disk. You can find the <a href="https://github.com/vhajdari/cdap-pipelines/blob/master/util/export_pipelines.py">export_pipelines.py</a> file in the <strong>util</strong> folder of the repo you cloned earlier.</p><p>Here’s the export script in action:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*x07M8AyeaZ5udGoU" /></figure><p>Once all your pipelines are exported you can copy the desired files to the pipelines folder in the git project. The python script I mentioned here is by no means comprehensive, and you may find that there are lots of other things that you may want to extract as well, but if you are so inclined it’s a good way to get started for learning how to script with the ReST API.</p><p>By the way, this is also how you can extract all the system preferences and some additional settings from the source environment so that you can add those settings to a file to support CI/CD efforts down the road. For example, we’ll want to extract all the system preferences — this is how to get them:</p><blockquote>curl — request GET \</blockquote><blockquote>— url <a href="http://localhost:11015/v3/namespaces/default/preferences">http://localhost:11015/v3/namespaces/default/preferences</a></blockquote><h3><strong>Push to Git Repo</strong></h3><p>OK, now that you know how to extract pipelines both individually and in bulk, the next step is to check these pipelines into your git repository. Assuming you forked my GitHub repo to your own repo, you would clone your forked repo and work off of that. For this example I created a new branch for each pipeline I want to push to my repo.</p><p>Start off by configuring some global settings for your repo. This will help you avoid any pesky error messages when you attempt to push your code.</p><blockquote>git config — global user.email “you@example.com”</blockquote><blockquote>git config — global user.name “Your Name”</blockquote><p>Create a branch for your work. In this example I used <strong>titanic-01</strong> as the development branch for the first pipeline I checked in to git.</p><blockquote>git checkout titanic-01</blockquote><p>Once you’ve copied your pipeline JSON to the <strong><em>pipelines</em></strong> directory add all the files in the project folder to source control.</p><blockquote>git add .</blockquote><p>You can check the state of the git repo at any time by running:</p><blockquote>git status</blockquote><p>Now you are ready to commit your changes. Make sure to add a comment so that the commit is documented.</p><blockquote>git commit -m “This is a message that will explain what this commit contains”</blockquote><p>Almost done. All that is left to be done is to push all the changes to the GitHub repository.</p><blockquote>git push origin titanic-01</blockquote><p>Granted, all of the steps of exporting the pipelines and checking them into git can be scripted as well, but that would be left as an exercise for the reader since no two teams operate the same way, let alone different enterprises. You can get as elaborate as you want for this process, but to minimize bugs it’s always best to keep things simple.</p><h3><strong>Pull Requests and Merges</strong></h3><p>When working in your own feature branch, you have the freedom to make all the changes you want locally and check in whatever you would like to be merged with the upstream project. In order for the upstream project to reflect the changes we pushed to our git repository we need to create a pull request. You do this in the GitHub page.</p><p>Make sure to create the pull request against a branch that the maintainer is expecting to merge PRs in. In this case I’m creating a PR against the upstream development branch, and will leave it up to the maintainer to merge the development branch into testing, QA, or master (production) branch. These will come into play later when we configure CI/CD.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6Iiav0aOtNqcc1IK" /></figure><p>The maintainer will review, and can comment, accept, or reject the PR.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MkwCvLtiD2ynkJ9b" /></figure><h3>Deploy a Pipeline From Git</h3><p>Deploying a pipeline from a cloned GitHub repo is a simple as performing an import in CDF/CDAP. Once again, the UI lets you do imports one pipeline at a time, but it also takes care a lot of the validation in the UI.</p><p>Pipeline validation includes things like:</p><ul><li>Checking if the pipeline already exists</li><li>Checking plugin versions and upgrading to the latest versions as necessary</li><li>Upgrading to the latest version of plugins</li><li>Identifying missing plugins, and the ability to download from the hub.</li></ul><p>To import from the UI locate the big green plus button. When you click it you will be presented with the following window:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*umBZAuDqYZoeBPWl6Pk3wA.png" /></figure><p>Click the <strong>Import</strong> button on the Pipeline card and select the pipeline JSON from your file system. The pipeline with then load into edit mode in the studio where you can continue updating it or deploy it.</p><p>Alternatively, here’s an example of how to deploy a pipeline using the API with curl:</p><blockquote><em>curl -X PUT “</em><a href="http://localhost:11015/v3/namespaces/default/preferences">http://localhost:11015</a><em>/v3/namespaces/</em><strong><em>namespace-id</em></strong><em>/apps/</em><strong><em>pipeline-name</em></strong><em>” -d “@/path/to/the/pipeline/JSON”</em></blockquote><p>Make sure to substitute <strong>namespace-id</strong> and <strong>pipeline-name</strong> with your own values. In my case values are <strong>default</strong>, because I’m deploying it to the default namespace, and <strong>Titanic-01, </strong>which is the name I’ve given to this pipeline. The final component is the path to your pipeline JSON file. Don’t forget the <strong>@</strong> symbol before the path.</p><p><strong><em>A word of caution.</em></strong> Deploying a pipeline via API does not provide any validation like you have in the UI. Therefore, any validation that needs to take place needs to be encapsulated in your deployment code. Similarly to what I did with the pipeline export script in Python, you would have to perform each validation step if you want to ensure that the pipeline will have all the requisite artifacts on the target system.</p><h3>Conclusion</h3><p>There you have it. Your pipelines are now in source control on GitHub. Of course, you don’t have to use GitHub for your VCS, but it is one of the most popular options in the open source community and has broad adoption among a majority of open source projects, including CDAP.</p><p>In this article we learned how to extract pipelines via the UI and ReST, and how the <strong>export_pipelines.py</strong> script helps us export pipelines in bulk. I showed you how to check your pipelines into your repository with git, and how to create a PR to push your pipelines to the upstream repo for testing and promotion.</p><p>In the next article we’ll discuss the process for migrating artifacts from GitHub into a TEST, QA, or PROD environments. We’ll dig a little deeper into automation options and discover how we can leverage the API more broadly.</p><p>Until next time, stay safe and healthy, and wash your hands!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a286e806c2f2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-2-a286e806c2f2">CI/CD and Change Management for Pipelines — Part 2</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CI/CD and Change Management for Pipelines — Part 1]]></title>
            <link>https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-1-1b4100aef66a?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/1b4100aef66a</guid>
            <category><![CDATA[ci-cd-pipeline]]></category>
            <category><![CDATA[cdap]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <dc:creator><![CDATA[Tony Hajdari]]></dc:creator>
            <pubDate>Mon, 09 Mar 2020 13:01:00 GMT</pubDate>
            <atom:updated>2020-03-09T14:35:44.323Z</atom:updated>
            <content:encoded><![CDATA[<h3>CI/CD and Change Management for Pipelines — Part 1</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*UD048Upd9cfHmkOE" /><figcaption>Typical CI/CD process for data pipelines</figcaption></figure><p>Welcome to my latest series on continuous integration of data pipelines with Cloud Data Fusion (CDF) and/or CDAP. This will be a <strong>4 part series</strong> of articles where I’ll discuss the promotion process of data pipelines across multiple environments and all the tools and techniques that we’ll use along the way.</p><h4><strong>Change Control and CI/CD</strong></h4><p>Whenever we consider a development lifecycle in an enterprise setting there are a number of gates that a product has to go through before being released to production. Typically we do development in a segregated development environment, most often this is our very own laptop. Artifacts that have completed the development phase would be transferred to a test environment to undergo both unit and integration testing. Some organizations have a more rigorous Quality Assurance (QA) process that requires that all artifacts that pass initial tests undergo further testing in order to be certified for release to production. Finally, all artifacts that have been tagged for promotion will be published to a production environment and put to use with production data and secured accordingly.</p><p>There are two parts to this equation; governance and automation. Some or all of these activities can be automated with platforms like <a href="https://github.com/">GitHub</a> and similar systems, others require implicit human intervention. The governance process for developing and promoting code to production is now commonplace and very well understood by enterprises, but how does this apply to data integration pipelines that are not necessarily code?</p><p>If we come from the programming world we most likely have been exposed to <a href="https://en.wikipedia.org/wiki/Version_control">Version Control Systems</a> like <a href="https://git-scm.com/">Git</a>. To automate the process of migrating artifacts from Git most enterprises leverage CI/CD tools like <a href="https://jenkins.io/">Jenkins</a> or <a href="https://travis-ci.com/">Travis CI</a>. The human element in the governance process for change control becomes a simple workflow in GitHub, as in reviewing and merging a PR, but scheduling and prioritizing promotion of artifacts may still require approval and scheduling by committee.</p><p>In this article I’m not going to focus so much on the change control process and all the bureaucracy that may go with it, but I’ll primarily discuss the mechanics of how a CI/CD workflow can be implemented for developing and promoting CDF/CDAP artifacts from development through production.</p><p>CI/CD has many moving parts so I’ll break this topic down into multiple articles to focus on each area in greater detail as we go through this journey. The series of articles will be broken down as follow:</p><ul><li>In this first article we’ll cover the overall process and define the concepts.</li><li>In the second article we’ll focus on how we can extract artifacts from a CDF/CDAP environment and store them on GitHub.</li><li>In the third article we’ll discuss the process for migrating artifacts from GitHub into a TEST, QA, or PROD environment.</li><li>And finally, in the fourth article I’ll discuss how we can automate the whole process so that we can invoke the whole CI/CD process with Jenkins.</li></ul><h4><strong>What is CI/CD?</strong></h4><p>Often you will see the acronym CI/CD in reference to software development workflows. This stands for Continuous Integration and Continuous Development or Deployment. In simple terms this means we write some code and some tests for that code, and some automation system takes over once we’ve checked in our code to build it, run the unit and integration tests that were written, and deploy the resulting artifacts somewhere.</p><p>So why is CI/CD important? As you can imagine, most enterprises will have distributed teams working on different portions of a system that supports production goals, therefore automation for continuously building and delivering new features and functionality, as well as bug fixes, is highly valued. This also allows teams to be more agile and work on multiple tasks simultaneously.</p><p>Most important of all is that only artifacts that have been fully tested and validated make their way into production. CI/CD processes, and systems that enable those processes, are widely used to achieve this goal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9jtGp46qQnsRO1Y8" /></figure><p>In the context of developing pipelines we don’t really have a build process as in the traditional sense of compiling code. The development process itself is defining the logical flow and processing steps for the data pipeline using a visual development environment, and testing will typically happen in preview mode while in the pipeline development studio of CDF/CDAP. Further testing is then conducted by publishing the pipeline in a development environment that most likely has only a subset of data. Asserting the validity of the test requires that sources and sinks are checked for the expected number of records, and this can change based on which environment the tests are run on and the volume of data.</p><p>From an automation perspective we would look at leveraging the REST API of CDF/CDAP to deploy repeatable sets of tests along with preparing the execution environment.</p><h4><strong>Organizing Artifacts Into Namespaces</strong></h4><p>One powerful feature of CDF/CDAP is that you can organize your artifacts into separate namespaces. Each namespace can have its own artifacts; including pipelines, plugins and applications, as well as its own preferences, a set of key value pairs that can store information for things like database connections or folder paths.</p><p>As an example you can have a DEV namespace that may be configured to point to a development database and a TEST namespace that would point to an entirely different database. Using this technique it makes it easy to test your promotion process without having to maintain multiple CDF/CDAP instances.</p><h4><strong>Segregated CDF/CDAP Instances</strong></h4><p>At times it is absolutely essential that a production environment is segregated into its own distinct set of instances with security and networking configured specifically for that purpose.</p><p>Oftentimes this means having a dedicated VPC and strict network ingress and egress rules, as well as very restrictive service accounts and user account controls.</p><p>In this context, CDF/CDAP may be just one component of a greater ecosystem of data storage and processing systems, and thus would need to be highly secured and would be required to work in a very restrictive environment. To effectively test such environments it would be necessary to have a similarly configured QA environment that mimics the same properties of the production environment such that the integrated tests will be representative of the workloads that would be experienced in production.</p><h4><strong>Testing a Pipeline</strong></h4><p>When developing a pipeline locally in CDAP Sandbox you can test the pipeline in preview mode, and when you are ready to test with real datasets you can publish the pipeline and run it locally. This is an effective way to test that the pipeline logic is working well, but this only tests the pipeline with a limited scope of data, and without true parallelism. To test the pipeline at scale you really need to deploy and test it at scale using an Apache Hadoop cluster like Dataproc and a sufficiently large input.</p><p>For quick tests across environments you will need to make sure that you have the following elements replicated to the target environment where you intend to do the testing:</p><ul><li><strong>Source Data</strong> — This data should be in a storage system reasonably equivalent to production, and be large enough to simulate your desired levels of scalability. We will use datasets from HDFS or GCS in CSV or Avro format for the purpose of this blog series.</li><li><strong>Pipeline</strong> — The JSON export of the pipeline DAG.</li><li><strong>Plugins</strong> — If you have any custom developed plugins or plugins you’ve downloaded from the hub.</li><li><strong>Preferences</strong> — Key value pairs used in that namespace for macro substitution.</li></ul><p>CDAP provides three ways to export a pipeline. You can export a pipeline directly from within the UI, via the CLI, or by using the API. I’ll show you two ways to export a pipeline that include the UI and the API. But, keep in mind that environment specific configurations like preferences and deployed applications are not part of the pipeline export — more on this in the next article.</p><h4><strong>Part 1 — Wrap Up</strong></h4><p>In the next article I will cover the following:</p><ul><li>Creating a checklist of all the artifacts you will need to test in a target environment</li><li>How to set up a GitHub project to house our pipeline artifacts</li><li>How to export pipelines from CDF/CDAP using the export tools in the UI and how to do it via the ReST API</li><li>How to check in our development pipelines to GitHub</li><li>How to deploy a pipeline to an alternate environment using both the UI and the ReST API</li></ul><p>See you next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1b4100aef66a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/ci-cd-and-change-management-for-pipelines-part-1-1b4100aef66a">CI/CD and Change Management for Pipelines — Part 1</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wrangler Functions Cheat Sheet]]></title>
            <link>https://medium.com/cdapio/wrangler-functions-cheat-sheet-cda98370ea89?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/cda98370ea89</guid>
            <category><![CDATA[data-analytics]]></category>
            <category><![CDATA[etl]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[java-expressions]]></category>
            <category><![CDATA[cdap]]></category>
            <dc:creator><![CDATA[Tony Hajdari]]></dc:creator>
            <pubDate>Tue, 11 Feb 2020 14:01:01 GMT</pubDate>
            <atom:updated>2020-02-11T14:01:01.730Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*yySFdonI8vl72TJK" /><figcaption>Photo by <a href="https://unsplash.com/@davidoclubb?utm_source=medium&amp;utm_medium=referral">Dave Clubb</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>To really become a Ninja with Wrangler Directives you have to get to know all the functions that Wrangler supports. In this article I’m going to list out all the Wrangler functions with a short description of what each one does.</p><p>At the time of this writing the Wrangler code branch on GitHub is at version 4.1 for the latest release. The link to the Wrangler functions documentation can be found <a href="https://docs.cdap.io/cdap/current/en/user-guide/data-preparation/functions/index.html">here</a>.</p><p>In a <a href="https://medium.com/cdapio/advanced-cdap-directives-c10569724da0">previous article</a> I discussed how you can use JEXL expressions in your directive, and these functions are no different. This article will primarily focus on the functions themselves, so that you have a good understanding what is available.</p><p>If you want to know what JEXL function types are available to Wrangler you can take a look at the code <a href="https://github.com/data-integrations/wrangler/blob/release/4.1/wrangler-core/src/main/java/io/cdap/wrangler/expression/EL.java">here</a>, but, I’ll save you the trouble and list them here along with their associated Classes.</p><p><strong>Function Prefix → Class Name</strong><br><em>null</em> → Global.class<br><em>date</em> → Dates.class<br><em>json</em> → JSON.class<br><em>dq</em> → DataQuality.class<br><em>ddl</em> → DDL.class<br><em>geo</em> → GeoFences.class</p><p>… and a few more that are not covered in this blog! The rest of the functions allow you to call methods from the corresponding Java utility classes like Math, Sting, etc — more on this in future articles, so keep an eye out! But, for the impatient here’s now such a function would be used:</p><blockquote>set-column :rounded_price math:ceil(price)</blockquote><p><em>math</em> → Math.class<br><em>string</em> → StringUtils.class<br><em>strings</em> → Strings.class<br><em>escape</em> → StringEscapeUtils.class<br><em>bytes</em> → Bytes.class<br><em>arrays</em> → Arrays.class</p><h3>Using Functions</h3><p>Most commonly you will use these functions with a directive that can parse an expression, such as <strong>send-to-error</strong>.</p><blockquote><strong>send-to-error <em>exp:{&lt;condition&gt;}</em></strong></blockquote><p>So, in order to use an expression you put the prefix in the <strong>exp</strong> part of the expression, unless it’s the Global variety, in which case you leave it empty. This would take the form of: FUNCTION(), date:FUNCTION(), dq:FUNCTION(), json:FUNCTION(), and so on.</p><p>For example, a directive that uses the Data Quality function would look like this:</p><blockquote><em>send-to-error</em><strong> <em>dq:isCreditCard(Credit_Card_Number)</em></strong></blockquote><p>As you can see in this example, <strong>dq</strong> is the function prefix and <strong>isCreditCard()</strong> is the function that is part of the Data Quality class, using the “<strong>:”</strong> to concatenate the expression.</p><p>This function checks to see that the value in the column “<strong><em>Credit_Card_Number</em></strong>” is in fact a valid credit card number.</p><p>Conversely, you can check for the inverse condition by prefixing the function name with a “!” — this is the result of the function evaluating to false.</p><blockquote><em>send-to-error </em><strong><em>!dq:isCreditCard(Credit_Card_Number)</em></strong></blockquote><p>The following screenshots illustrate what happens to the record that are filtered out that have invalid credit card numbers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*B7YExBcXV2LQRhZj7ZYfSQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DWGW_E2giI_ZQmlYlTPNIg.png" /></figure><p>Additionally, you can also use these functions with the <strong>set-column</strong> directive, to create a new column based on an expression applied on one or more other columns.</p><blockquote><strong>set-column</strong><em> </em><strong><em>:column-name exp:{&lt;condition&gt;}</em></strong></blockquote><p>For example,</p><blockquote><em>set-column :Full_Name</em><strong><em> </em>string:concat(first, ‘ ‘, last)</strong></blockquote><p>The following cheatsheet lists the functions along with the data type it accepts as input. The source code for all these functions can be found <a href="https://github.com/data-integrations/wrangler/tree/release/4.1/wrangler-core/src/main/java/io/cdap/functions">here</a>.</p><h3>Global Functions</h3><p>In this section the functions do not have a prefix (<em>null</em> → Global.class), therefore you do not put anything before the function.</p><h4>Conversion Functions</h4><p><em>Example</em>: <strong>set-column :Phone1 toDouble(Phone)</strong></p><ul><li><strong>toDouble</strong>(String value) → Converts String value to double.</li><li><strong>toFloat</strong>(String value) → Coverts a String value to float.</li><li><strong>toLong</strong>(String value) → Converts a String value to Long.</li><li><strong>toInteger</strong>(String value) → Converts a String value to integer.</li><li><strong>toBytes</strong>(String value) → Converts a String value to byte array.</li></ul><h4>String Utility Functions</h4><p><em>Example</em>: <strong>set-column Salutation concat(“Mr. ”, Name)</strong></p><ul><li><strong>concat</strong>(String a, String b) → Concatenates two string without any separator in between.</li><li><strong>concat</strong>(String a, String delim, String b) → Concatenates two string with a delimiter.</li><li><strong>coalesce</strong>(Object … objects) → Finds the first non-null object.</li><li><strong>rcoalesce</strong>(Object … objects) → Finds the last non-null object.</li><li><strong>format</strong>(String str, Object… args) → Formats the string in way similar to JAVA’s String format.</li><li><strong>padAtStart</strong>(String string, int minLength, char padChar) → Returns a string, of length at least minLength, consisting of string prepended with as many copies of padChar as are necessary to reach that length.</li><li><strong>padAtEnd</strong>(String string, int minLength, char padChar) → Returns a string, of length at least minLength, consisting of string appended with as many copies of padChar as are necessary to reach that length.</li><li><strong>repeat</strong>(String string, int count) → Returns a string consisting of a specific number of concatenated copies of an input string.</li><li><strong>unquote</strong>(String string) → Removes single or double quotes from a string if its quoted.</li></ul><h3>Data Quality Functions</h3><p>Data quality functions validate the data being passed in, so <strong>send-to-error </strong>or <strong>filter-*</strong> type directives are good options to use in combination with these functions.</p><p><em>Example</em>: <strong>send-to-error !dq:isEmail(Email)</strong></p><ul><li><strong>columns</strong>(Row row) → Given a row, finds the length of the row</li><li><strong>hascolumn</strong>(Row row, String column) → Finds if the row has a column.</li><li><strong>inrange</strong>(double value, double lower, double upper) → Checks if the value is within the range.</li><li><strong>strlen</strong>(String str → Returns the length of the string.</li><li><strong>isnull</strong>(Object object) → Checks if the object is null.</li><li><strong>isempty</strong>(String str) → Checks if the string is empty.</li><li><strong>isDate</strong>(String date) → Validate using the default Locale.</li><li><strong>isDate</strong>(String date, String pattern) → Validate using the specified pattern.</li><li><strong>isNumber</strong>(String str) → Checks if a value is a number.</li><li><strong>isBoolean</strong>(String str) → Checks if a value is a boolean.</li><li><strong>isEmpty</strong>(String str) → Checks if a value is a empty</li><li><strong>isDouble</strong>(String str) → Checks if a value is a double.</li><li><strong>isInteger</strong>(String str) → Checks if a value is an integer.</li><li><strong>isIP</strong>(String ip) → Checks if string is a valid IP address. Could be IPv4 or IPv6.</li><li><strong>isIPv4</strong>(String ip) → Checks if string is a valid IPv4 address.</li><li><strong>isIPv6</strong>(String ip) → Checks if string is a valid IPv6 address.</li><li><strong>isEmail</strong>(String email) → Checks if string is a valid email address.</li><li><strong>isUrl</strong>(String url) → Checks if string is a valid url address.</li><li><strong>isDomainName</strong>(String domain) → Checks if string is a valid url domain.</li><li><strong>isDomainTld</strong>(String domain) → Checks if string is a valid top-level domain.</li><li><strong>isGenericTld</strong>(String domain) → Checks if string is a valid generic top-level domain.</li><li><strong>isCountryTld</strong>(String domain) → Checks if string is a valid country top-level domain.</li><li><strong>isISBN10</strong>(String isbn) → Checks if string is a valid ISBN-10.</li><li><strong>isISBN13</strong>(String isbn) → Checks if string is a valid ISBN-13.</li><li><strong>isCreditCard</strong>(String cc) → Checks if string is a valid credit card number.</li><li><strong>isAmex</strong>(String cc) → Checks if string is a valid AMEX credit card number.</li><li><strong>isVisa</strong>(String cc) → Checks if string is a valid VISA credit card number.</li><li><strong>isMaster</strong>(String cc) → Checks if string is a valid Master Card credit card number.</li><li><strong>isDiner</strong>(String cc) → Checks if string is a valid Diner’s Card card number.</li><li><strong>isDiscover</strong>(String cc) → Checks if string is a valid Discover Card number.</li><li><strong>isVPay</strong>(String cc) → Checks if string is a valid VPay card number.</li></ul><h3>Date Functions</h3><p>The majority of the functions here expect <strong>ZonedDateTime</strong> as input, so make sure to parse the date filed before using the functions.</p><p><em>Example</em>:</p><blockquote><strong>send-to-error !date:isDate(Birthday)</strong></blockquote><blockquote><strong>parse-as-simple-date :Birthday MM/dd/yyyy</strong></blockquote><blockquote><strong>set-column month_born date:MONTH_LONG(Birthday)</strong></blockquote><ul><li><strong>UNIXTIMESTAMP_MILLIS</strong>(ZonedDateTime date) → Converts a date to long unix timestamp in milli-seconds.</li><li><strong>UNIXTIMESTAMP_SECONDS</strong>(ZonedDateTime date) → Converts a date to long unix timestamp in seconds.</li><li><strong>MONTH</strong>(ZonedDateTime date) → Converts a ZonedDateTime to Month in year.</li><li><strong>MONTH_SHORT</strong>(ZonedDateTime date) → Extracts a short month description from Date.</li><li><strong>MONTH_LONG</strong>(ZonedDateTime date) → Extracts a long month description from Date.</li><li><strong>YEAR</strong>(ZonedDateTime date) Extracts only year from Date.</li><li><strong>DAY_OF_WEEK</strong>(ZonedDateTime date) → Extracts day of the week from the date.</li><li><strong>DAY_OF_WEEK_SHORT</strong>(ZonedDateTime date) → Extracts day of the week from the date.</li><li><strong>DAY_OF_WEEK_LONG</strong>(ZonedDateTime date) → Extracts day of the week from the date.</li><li><strong>DAY_OF_YEAR</strong>(ZonedDateTime date) → Extracts Day of the year from the date.</li><li><strong>ERA</strong>(ZonedDateTime date) → Extracts Era from the date.</li><li><strong>ERA_SHORT</strong>(ZonedDateTime date) → Extracts Era from the date as short text.</li><li><strong>ERA_LONG</strong>(ZonedDateTime date) → Extracts Era from the date as long text.</li><li><strong>DAYS_BETWEEN</strong>(ZonedDateTime date1, ZonedDateTime date2) → Return number of days between two dates.</li><li><strong>DAYS_BETWEEN_NOW</strong>(ZonedDateTime date) → Return number of dates between now and date days.</li><li><strong>SECONDS_TO_DAYS</strong>(int seconds) → Converts seconds to days.</li><li><strong>SECONDS_TO_HOURS</strong>(int seconds) → Converts seconds to hours.</li><li><strong>SECONDS_TO_MINUTES</strong>(int seconds) → Converts seconds to mins.</li><li><strong>SECONDS_TO_WEEKS</strong>(int seconds) → Converts seconds to weeks.</li><li><strong>isDate</strong>(String value) → Checks if a column is a date column.</li><li><strong>isTime</strong>(String value) → Checks if the value passed is a date time.</li></ul><h3>Geo Fencing Functions</h3><p>Some Geo functions for good measure.</p><p><em>Example</em>: <strong>send-to-error !geo:inFence(latitude,longitude,body)</strong></p><ul><li><strong>inFence</strong>(double latitude, double longitude, String geofences) → Checks if Point is inside any of the given polygonal geofences based on the winding number algorithm.</li><li><strong>isPointInside</strong>(Feature feature, Coordinates location) → Checks if geometry is inside the given location</li><li><strong>isLeft</strong>(Coordinates vertex0, Coordinates vertex1, Coordinates gpC) → You’ll have to look at the source code to figure this one out!!!</li></ul><h3>JSON Functions</h3><p>For those times when you need to work JSON data in a column. Make sure to parse the JSON string first with the <strong>parse-as-json</strong> directive for the functions that expect a <strong>JsonElement</strong> as input.</p><p><em>Example</em>: <strong>set-column character_data json:parse(JSON)</strong></p><ul><li><strong>select</strong>(String json, String path, String …paths) → Selects elements from a JSON string.</li><li><strong>select</strong>(String json, boolean toLower, String path, String …paths) → Selects elements from a JSON string.</li><li><strong>select</strong>(JsonElement element, String path, String …paths) → Selects elements from a JSON string.</li><li><strong>select</strong>(JsonElement element, boolean toLower, String path, String …paths) → Selects elements from a JSON string.</li><li><strong>drop</strong>(String json, String field, String … fields) → Removes fields from a JSON, inline, by recursively iterating through the JSON to delete one or more fields specified.</li><li><strong>drop</strong>(JsonElement element, String field, String … fields) → Removes fields from a JSON, inline, by recursively iterating through the JSON to delete one or more fields specified.</li><li><strong>keysToLower</strong>(JsonElement element) → Lowers the keys of the json. it applies this transformation recurively.</li><li><strong>join</strong>(JsonElement element, String separator) → Join two JSON elements together</li><li><strong>stringify</strong>(JsonElement element) → Converts an JavaScript Object to a JSON string.</li><li><strong>parse</strong>(String json) → Parses a column or string to JSON. This is equivalent to JSON.parse()</li><li><strong>parse</strong>(String json, boolean toLower) → Parses a column or string to JSON. This is equivalent to JSON.parse()</li></ul><h3><strong>Conclusion</strong></h3><p>This article listed all the functions you have available in Wrangler which can be used in directives that support expressions. You may have noticed that the <strong>isDate</strong> function is available in multiple classes. The one you use will depend on the type of input you are validating and the logic you are implementing.</p><p>In future articles we’ll dive a little deeper to see how we can chain such functions together and use them in combination to really spruce up our Wrangler recipes.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cda98370ea89" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/wrangler-functions-cheat-sheet-cda98370ea89">Wrangler Functions Cheat Sheet</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Announcing CDAP 6.1.1 Release]]></title>
            <link>https://medium.com/cdapio/announcing-cdap-6-1-1-release-ef35efd6fd96?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/ef35efd6fd96</guid>
            <category><![CDATA[cdap]]></category>
            <category><![CDATA[os]]></category>
            <category><![CDATA[lineage]]></category>
            <category><![CDATA[data-analytics]]></category>
            <category><![CDATA[validation]]></category>
            <dc:creator><![CDATA[Mo Eseifan]]></dc:creator>
            <pubDate>Mon, 03 Feb 2020 14:01:01 GMT</pubDate>
            <atom:updated>2020-02-03T14:01:01.408Z</atom:updated>
            <content:encoded><![CDATA[<p>On behalf of the Cask Data Application Platform (CDAP) community, it is my pleasure to announce the release of CDAP version 6.1.1. This release includes a new, intuitive UI for Field Level Lineage (FLL), and extends FLL support to more plugins. It also introduces a new failure validation framework for plugins for early detection of configuration errors in pipelines. Additionally, you can now connect to your data in Azure Data Lake Storage (ADLS) and transform it visually in Wrangler.</p><h3>Field Level Lineage</h3><p>The new field level lineage feature allows users to better visualize the journey of individual fields in datasets. The updated design also makes relationships between fields easier to see by connecting fields in the UI, and users can analyze relationships and transformations by selecting specific fields. For more information on Field Level Lineage, see this blog post series: <a href="https://medium.com/cdapio/field-level-lineage-part-1-3cc5c9e1d8c6">part 1</a>, <a href="https://medium.com/cdapio/designing-field-level-lineage-part-2-b6c7e6af5bf4">part 2</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*y7gMnNOBTQO7vDp6" /><figcaption>Example of Field Level Lineage visualization</figcaption></figure><h3>Pipeline Improvements</h3><p>This release also introduces a new way for validating plugin configurations that allows us to detect and highlight errors within a pipeline, so that you can detect configuration errors sooner and build pipelines more efficiently. All the configuration fields with errors are highlighted once the user opens the plugin properties and a helpful error message is also displayed below the field . This greatly reduces the amount of time spent configuring and validating pipelines. For more information on the validation framework, please see this <a href="https://medium.com/cdapio/validation-framework-for-cdap-plugins-fe646757cabc">blog post</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/973/0*zV_sgtr58KbRStZV" /><figcaption>Example of validation in the Joiner plugin</figcaption></figure><p>This release also adds new widgets so users get better controls to configure their pipelines more intuitively. These include ConfigurationGroup — a wrapper component that can hold other widgets. It allows for easier visual grouping of related fields. Additionally, developers can now use dynamic filters to define custom conditions for when certain fields should be displayed/hidden depending on the contents of other fields within the ConfigurationGroup. For example, show the ZipCode field if the value of the Country field equals “USA”.</p><h3>Platform Enhancements</h3><p>This release contains performance improvements to several areas across the platform. The most notable improvements can be seen on the pipeline list page and from within the Studio. These improvements have significantly improved the performance of the UI under heavy load.</p><p><a href="https://cdap.io/get-started/">Download CDAP 6.1.1 today</a> and take it for a spin! Also consider helping us develop the platform by <a href="mailto:%20cdap-user@googlegroups.com">reaching out to the community</a> with any comments, feedback, suggestions, or improvements or by creating and following <a href="https://issues.cask.co/browse/CDAP">JIRA issues</a> and submitting <a href="https://github.com/cdapio/cdap">pull requests</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ef35efd6fd96" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/announcing-cdap-6-1-1-release-ef35efd6fd96">Announcing CDAP 6.1.1 Release</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to debug your plugin]]></title>
            <link>https://medium.com/cdapio/how-to-debug-your-plugin-f5d41dba6805?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/f5d41dba6805</guid>
            <category><![CDATA[cdap]]></category>
            <category><![CDATA[intellij]]></category>
            <category><![CDATA[plugins]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[big-data]]></category>
            <dc:creator><![CDATA[Tony Hajdari]]></dc:creator>
            <pubDate>Mon, 27 Jan 2020 14:01:01 GMT</pubDate>
            <atom:updated>2020-01-27T14:01:01.313Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tlFVc7Jkfn3ThePhFPv9rQ.jpeg" /></figure><p>In a previous <a href="https://medium.com/cdapio/getting-started-with-cdap-plugin-development-bcd21cc7ae66">article</a> I showed you how to get started with plugin development in CDAP. But, wouldn’t it be nice if we can attach a debugger to a deployed plugin and see how it performs inside of a pipeline with actual data?</p><p>I’m going to show you how to attach a debugger to a CDAP sandbox instance so that we can inspect the data that is being processed by the plugin. This will help us get a better sense of what the plugin is doing, and all the other interesting bits that we can observe from the objects we’ll have access to.</p><p>It’s always handy to be able to see what data is being passed along from a previous stage in a pipeline and how that data is being passed to the next stage in the pipeline. When you build a plugin it’s always best to start simple and layer on additional functionality as you go. This article builds on the knowledge from the previous plugin development article and once again I’ll use the <a href="https://github.com/data-integrations/example-transform.git"><strong>example-transform</strong></a> plugin for illustration.</p><h4><strong>Clone and Build the Plugin</strong></h4><p>First, clone the <a href="https://github.com/data-integrations/example-transform.git"><strong>example-transform</strong></a> plugin from the Git repo.</p><blockquote>git clone <a href="https://github.com/data-integrations/example-transform.git">https://github.com/data-integrations/example-transform.git</a></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tE2-0vwCR7ZEp2EW" /></figure><p>Open up the <strong>pom.xml</strong> in IntelliJ as a project — we’ll come back to it later.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*V5nvyNjVutVcgC7i" /></figure><p>At this point we won’t be making any changes to the source code. We just want to build the project as is and deploy the resulting plugin to CDAP. You can build it manually in the terminal or use Maven inside of IntelliJ to build the project. The command to use is:</p><blockquote>mvn clean package</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EU5LIVdY7Fn3GisT" /></figure><p>Once the build finishes you will find the plugin artifacts in the resulting target directory as illustrated.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mYgg3JK_hdJUNt48" /></figure><h4><strong>Start CDAP in Debug Mode</strong></h4><p>In order for us to debug the plugin we must first start CDAP in debug mode so out debugger has something to attach to. To enable the debugging agent simply add the following command argument to your normal cdap start command:</p><blockquote>— enable-debug</blockquote><p>The full comand would thus be:</p><blockquote>cdap sandbox start — enable-debug</blockquote><p>Optionally, if you want to use a port other than the default port 5050, simply add the port number after the enable debug command.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Gt-lupIII4cTJ8Ak" /></figure><p>Take note of the following line in the output.</p><blockquote><strong><em>Remote debugger agent started on port 5005</em></strong></blockquote><p>This indicates that the debugger agent service has been started on port 5005. We will see this port referenced again later when we attach to the debbuger to that port. Now that CDAP is running in debug mode deploy the plugin to CDAP and build a quick pipeline that will pass some data on to this example-transform. Keep in mind that this is a transform plugin, thus you will find it within the Transform bin in the pipeline Studio. Bellow is a a screencap that illustrates this process.</p><p>At this point we won’t be making any changes to the source code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*phCYIRJPOHofkrZRHlBEXA.gif" /></figure><h3><strong>Start Debugger in IntelliJ</strong></h3><p>With the plugin now added to our pipeline we are ready to start debugging the plugin code with IntelliJ while the pipeline is running. We will run the pipeline in preview mode so that we can iterate over deployment of the plugin and observe the effects of any changes we make.</p><p>Open up the source file for the ExampleTransformPlugin class, navigate to the transform method and put a breakpoint somewhere in that method. From the IntelliJ menu select <strong>Run → Attach to Process</strong>. This will open up a dialog that will show you the process listening on 5005. Select the Java process and connect the debugger.</p><p>Switch over to the pipeline studio, put in preview mode and run it. Switch back to the IntelliJ screen and whooila, your breakpoint has been reached and you can now inspect the data that is coming from the Wrangler stage. Clicking on the <strong>fields</strong> variable will show you that you have an object with 12 elements. You can now inspect each of the elements within this object and see what results it produces. As you step over each iteration of the loop you will see the records being submitter in sequence through the pipeline.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ztnn3tQhlm_vA6sd_96c7w.gif" /></figure><p>While you’re in debug mode the preview for the pipeline will continue to run until you stop the debugger or the pipeline preview.</p><h4>Conclusion</h4><p>When working with plugin development debugging is an essential part of this process. Being able to pause the pipeline in particular blocks of code help us better understand what is happening within the the code and how the plugins accesses all the other information that is passed along in the DAG, and the object we have access to. In particular you can see the StructuredRecord object with the input schema and the filed it contains so that you can determine how to deal with these records in your code. Hopefully you now have a good handle on how to proceed with debuging your plugin and can use the debugger to create plugins with less effort.</p><p>Happy coding!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ADr6GhxcIpm44G4RZCcJ4w.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f5d41dba6805" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/how-to-debug-your-plugin-f5d41dba6805">How to debug your plugin</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploying and Running CDF pipelines with REST]]></title>
            <link>https://medium.com/cdapio/deploying-and-running-cdf-pipelines-with-rest-1d469a243bd0?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/1d469a243bd0</guid>
            <category><![CDATA[cdap]]></category>
            <category><![CDATA[rest-api]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[data-pipeline]]></category>
            <dc:creator><![CDATA[Tony Hajdari]]></dc:creator>
            <pubDate>Mon, 13 Jan 2020 16:00:41 GMT</pubDate>
            <atom:updated>2020-01-13T16:00:41.492Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YpTGz-zvD88w8EZZ" /><figcaption>Photo by <a href="https://unsplash.com/@matthew_t_rader?utm_source=medium&amp;utm_medium=referral">Matthew T Rader</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>By now you probably know that you run CDAP pipelines on Cloud Data Fusion (CDF), but did you know that you can also control your CDF instance remotely using the rest API. In this blog I will walk you through the process of deploying and starting a CDAP pipeline on CDF using only a REST client. We’ll use the handy <strong>curl</strong> utility since it is easy to work with and is available on most platforms.</p><p>Using the REST API you can do quite a lot with CDF. You can create an instance from scratch, retrieve information about running instances, deploy pipelines and run them, and all the sorts of things that DevOps folks get the warm and fuzzies over. The underlying CDAP API is well documented on the <a href="https://docs.cdap.io/cdap/current/en/reference-manual/http-restful-api/index.html">CDAP site,</a> so it’s probably a good idea to head over there for a quick glance at the types of things you can use the API to automate your CDF and CDAP workflows. What you won’t find on the CDAP site is the documentation around all the Googley things that you need to set up for GCP. You can find the reference documentation for the CDF REST API on the Google site <a href="https://cloud.google.com/data-fusion/docs/reference/cdap-reference">here</a>.</p><h3>Set up GCP</h3><p>The first thing you will need is the Google Cloud SDK, so go on and download it and install it by following the instructions <a href="https://cloud.google.com/sdk/docs/">here</a>. If you have not set up a CDF instance yet go ahead and do that as well. Instructions for setting up CDF are located <a href="https://cloud.google.com/data-fusion/docs/how-to/create-instance">here</a>. You can select either Basic or Enterprise edition. For this blog I’ll be using the Basic edition.</p><p>After you’ve downloaded and installed the <strong>gcloud</strong> SDK you will need to authenticate and generate a Google OAuth 2.0 token. On your terminal type the following command:</p><blockquote>$ gcloud auth login &lt;YOUR_ROJECT_ID&gt;</blockquote><p>Make sure to substitute &lt;YOUR_ROJECT_ID&gt; with the value from the ID column in the project ID list.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tjdXLlNcJ2vWobfm" /></figure><p>Entering this command will also launch your browser and take you through a typical authentication process for Google where you will be asked to provide a Google account (Gmail account) and grant access to the Google SDK. Once complete the terminal window with acquire the authentication token and tell you that you are now logged in with the user account you specified.</p><p>With authentication out of the way we can now set the environment variables that will contain the authentication token and the link to the CDF instance. Set the two environment variables to make the subsequent <strong><em>curl</em></strong> commands easier to work with.</p><blockquote>export AUTH_TOKEN=$(gcloud auth print-access-token)</blockquote><blockquote>export CDF_API=https://<strong>id</strong>-dot-<strong>region</strong>.datafusion.googleusercontent.com</blockquote><p>Take note that the <strong>CDF_API</strong> variable contains information from your CDF instance, project ID and region that CDF is deployed in. This URI may not be the most intuitive to construct, so you can go to the following <a href="https://cloud.google.com/data-fusion/docs/reference/rest/v1beta1/projects.locations.instances/list">page</a> to retrieve your link from the CDF API for <strong>projects.locations.instances.list</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*WtzI8kz1jhZBm-h3" /></figure><p>Make sure to put in<strong> projects/PROJECT_ID/locations/-</strong> in the <strong>parent</strong> field, and substiture PROJECT_ID with your own and click on the blue <strong>EXECUTE</strong> button. Your JSON response in the output should look as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kUcmstCSSmfT4LEIPzAT9A.png" /></figure><p>The part that interests us is the value associated with the <strong>apiEndpoint</strong> key. Copy this string and add it to the environment variable as illustrated below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LFooce55Ijfsfkim" /></figure><p>In my case the <strong>id</strong> is <strong><em>rest-demo-blog4rest-212908</em></strong> and the <strong>region</strong> is <strong><em>usw1</em></strong>.</p><h3>Create, Deploy and Run the Pipeline</h3><p>Let’s build a pipeline using CDAP sandbox and see how we can upload this to the CDF instance via REST. I created a simple pipeline and exported the JSON file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*D5ZQre0kw3K1Gsvd" /></figure><p>The pipeline itself is rudimentary and its purpose is simply to demonstrate that I can deploy the pipeline to CDF and run it. As a preparatory step I uploaded a CSV file the the GCP bucket for the sink. Validate and test the pipeline, and if all is good you can export it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5oNddCk2bgkd0gIT" /></figure><p>We now have a pipeline that we can deploy to CDF. The command to deploy the pipeline JSON with curl is as follows:</p><blockquote>curl -X PUT -H “Authorization: Bearer ${AUTH_TOKEN}” “${CDF_API}/v3/namespaces/<strong>namespace-id</strong>/apps/<strong>pipeline-name</strong>” -d “@/path/to/the/pipeline/JSON”</blockquote><p>Make sure to substitute <strong>namespace-id</strong> and <strong>pipeline-name</strong> with your own values. In my case values are <strong>default</strong>, because I’m deploying it to the default namespace, and <strong>my-rest-pipeline, </strong>which is the name I’ve given to this pipeline. The final component is the JSON file comprising your pipeline that was exported earlier. Don’t forget the <strong>@</strong> symbol before the path.</p><p>The command entered into the console should look like the illustration below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*im8Rkt9IlyjzY5Mr" /></figure><p>It may take a few moments for the command to complete, but once it does you will see a <strong>Deploy Complete</strong> message on the console. You can now check your CDF instance and see that the pipeline does indeed show up in the list of pipelines.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*L25uo1nUGoyhHt_-" /></figure><p>Open the pipeline and inspect it to make sure that everything looks in order, but don’t run it manually just yet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*WHSJh2eyZTugQ7Lg" /></figure><p>OK, so now we have a pipeline deployed via the REST API. Now let’s issue the API command to run it.</p><p>The command to run the pipeline via the REST is:</p><blockquote>curl -X POST -H “Authorization: Bearer ${AUTH_TOKEN}” “${CDF_API}/v3/namespaces/<strong>namespace-id</strong>/apps/<strong>pipeline-name</strong>/workflows/DataPipelineWorkflow/start”</blockquote><p>Once again make sure to make the necessary substitutions in the command. The command entered into the console should look like the illustration below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*PQXL43FBujOlwbge" /></figure><p>I added a timestamp for purposes of comparison, and as we can observe the timestamp that shows up in the CLI is identical to the one displayed in the pipeline list view. The pipeline will follow the typical lifecycle of <strong>Provisioning → Starting → Running → Success</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*KUnlgAWR321NXlyg" /></figure><p>In the next illustration we can see the pipeline has entered the starting phase.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ayG-i-RbRbD9X_5F" /></figure><h3>Conclusion</h3><p>As you saw in this article, we can now leverage the CDAP API with Cloud Data Fusion to manage a CDF instance, and we used some of the most rudimentary aspects of the API to deploy and run a pipeline remotely with <strong><em>curl</em></strong>. This of course is only the tip of the iceberg and you can do significantly more with the API.</p><p>Future uses to consider is infrastructure as code where you can provision whole environments of CDF and deploy pipelies to the newly created instances. Beyond this you can monitor and manage your CDF instances in an automated fashion which will surely put a smile on the faces of all those DevOps folks that had been waiting for this feature in GCP.</p><p>What use cases do you envision the the REST API?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*NwvV2ZWJgfD6T_eg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1d469a243bd0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/deploying-and-running-cdf-pipelines-with-rest-1d469a243bd0">Deploying and Running CDF pipelines with REST</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Validation Framework for CDAP Plugins]]></title>
            <link>https://medium.com/cdapio/validation-framework-for-cdap-plugins-fe646757cabc?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/fe646757cabc</guid>
            <category><![CDATA[data-pipeline]]></category>
            <category><![CDATA[validation]]></category>
            <category><![CDATA[cloud-data-fusion]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[cdap]]></category>
            <dc:creator><![CDATA[Vinisha Shah]]></dc:creator>
            <pubDate>Mon, 06 Jan 2020 14:01:03 GMT</pubDate>
            <atom:updated>2020-01-06T14:01:03.014Z</atom:updated>
            <content:encoded><![CDATA[<p>CDAP provides an interactive UI to build data pipelines to apply code-free transformations on data. CDAP data pipeline is an acyclic graph composed of multiple plugins as its nodes and connections between them representing data flow. Each plugin in the pipeline can be configured by providing configuration properties, input and output schema for the plugin.</p><p>A CDAP data pipeline solving a real world use case can contain ~10 or more nodes in the graph. While building such CDAP pipelines, pipeline developers can provide invalid plugin configurations or schema. For example, the BigQuery sink plugin can have output schema which does not match with underlying BigQuery table or GCS source has invalid bucket name.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/761/0*fF6nl1Ld1GcY6Am-" /></figure><p>When a deployed pipeline fails due to invalid configurations, a common pipeline development flow will be to check the logs to identify the invalid configuration, clone the data pipeline and run it. This iterative process increases the data pipeline development time. Imagine how much time it will take to build a data pipeline with ~10–20 nodes. Also, during this process, Pipeline developers have to go through technical logs, stacktraces or even code base in order to identify problems such as NullPointerExceptions.</p><p>In the upcoming CDAP release 6.1.1, a new validation framework has been introduced to fail fast and collect all the validation failures. This framework also exposes a Failure Collector API to surface contextual error messages on CDAP UI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*g9lQ4cAMpOM1WwQJ" /></figure><p>Lets see what this framework provides and how it can be used in plugins to surface contextual error messages:</p><p>The validation framework should be able to collect multiple error messages in order to provide better user experience while building the pipeline. To do that, below FailureCollector APIs are exposed from the framework:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bdcc5014b4f1532869fbba4522038d8b/href">https://medium.com/media/bdcc5014b4f1532869fbba4522038d8b/href</a></iframe><h3>Error collection using FailureCollector Api</h3><p>CDAP plugins override method configurePipeline() which is used to configure the stage at deploy time. The same method is called through validation endpoint as well. In order to collect multiple validation failures, FailureCollector API is exposed through stage configurer to validate the configurations in this method. The sample usage of FailureCollector API looks as below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9b34d65f96541b077cc7b7d18cd1744c/href">https://medium.com/media/9b34d65f96541b077cc7b7d18cd1744c/href</a></iframe><h3>Adding ValidationFailures to FailureCollector</h3><p>A validation failure is made up of 3 components:</p><ul><li>Message — Represents a validation error message</li><li>Corrective action — An optional corrective action that represents an action to be taken by the user to correct the error situation</li><li>Causes — Represents one or more causes for the validation failure. Each cause can have more than one attribute. These attributes are used to highlight different sections of the plugins on UI.</li></ul><p>Example:</p><p>In bigquery source if the bucket config contains invalid characters, a new validation failure will be added to the collector with a `stageConfig` cause attribute as below:</p><pre>Pattern p = Pattern.compile(&quot;[a-z0–9._-]+&quot;);<br>if (!p.matcher(bucket).matches()) { <br>collector.addFailure(<br>&quot;Allowed characters are lowercase characters, numbers,&#39;.&#39;, &#39;_&#39;, and &#39;-&#39;&quot;, <br>&quot;Bucket name should only contain allowed characters.&#39;&quot;)<br>.withConfigProperty(&quot;bucket&quot;);<br>}</pre><p>While a ValidationFailure allows plugins to add a cause with any arbitrary attributes, ValidationFailure API provides various util methods to create validation failures with <a href="https://github.com/cdapio/cdap/blob/develop/cdap-app-templates/cdap-etl/cdap-etl-api/src/main/java/io/cdap/cdap/etl/api/validation/CauseAttributes.java">common causes</a> that can be used to highlight appropriate UI sections. Below is the list of common causes and associated plugin usage:</p><h4>1. Stage config cause</h4><p>Purpose: Indicates an error in the stage property</p><p>Scenario: User has provided invalid bucket name for BigQuery source plugin</p><p>Example:</p><pre>collector.addFailure(“Allowed characters are lowercase characters, numbers,’.’, ‘_’, and ‘-’”, “Bucket name should only contain allowed characters.’”).withConfigProperty(“bucket”);</pre><h4>2. Plugin not found cause</h4><p>Purpose: Indicates a plugin not found error</p><p>Scenario: User is trying to use a plugin/JDBC driver that has not been deployed</p><p>Example:</p><pre>collector.addFailure(“Unable to load JDBC driver class ‘com.mysql.jdbc.Driver’.”, “Jar with JDBC driver class ‘com.mysql.jdbc.Driver’ must be deployed”).withPluginNotFound(“driver”, “mysql”, “jdbc”);</pre><h4>3. Config element cause</h4><p>Purpose: Indicates a single element in the list of values for a given config property</p><p>Scenario: User has provided a field to keep in the project transform that does not exist in input schema</p><p>Example:</p><pre>collector.addFailure(“Field to keep ‘non_existing_field’ does not exist in the input schema”, “Field to keep must be present in the input schema”)<br>.withConfigElement(“keep”, “non_existing_field”);</pre><h4>4. Input schema field cause</h4><p>Purpose: Indicates an error in input schema field</p><p>Scenario: User is using BigQuerysink plugin that does not record fields</p><p>Example:</p><pre>collector.addFailure(“Input field ‘record_field’ is of unsupported type.”,<br> “Field ‘record_field’ must be of primitive type.”)<br> .withInputSchemaField(“record_field”, null);</pre><h4>5. Output schema field cause</h4><p>Purpose: Indicates an error in output schema field</p><p>Scenario: User has provided output schema field that does not exist in BigQuerysource table</p><p>Example:</p><pre>collector.addFailure(<br>“Output field ‘non_existing’ does not exist in table ‘xyz’.”,<br>”Field ‘non_existing’ must be present in table ‘xyz’.”)<br>.withOutputSchemaField(“non_existing”, null);</pre><h4>Cause Associations</h4><p>While validating the plugin configurations, the validation failure can be caused by multiple causes. Below are a few examples of associated causes:</p><p>Example 1:</p><p>Database source has username and password as co-dependent properties. If username is not provided but password is provided, the plugin can just add a new validation failure with 2 causes as below:</p><pre>collector.addFailure(“Missing username”,<br> “Username and password must be provided’”)<br> .withConfigProperty(“username”).withConfigProperty(“password”);</pre><p>Example 2:</p><p>Projection Transform received incompatible input schema and output schema for a field such that input field can not be converted to output field. In that case a new validation failure can be created with 2 different causes as below:</p><pre>collector.addFailure(“Input field ‘record_type’ can not be converted to string”,”Field ‘record_type’ must be of primitive type’”) .withConfigProperty(“convert”).withInputSchemaField(“record_type”);</pre><h3>Summary</h3><p>Validation framework reduces pipeline development time by failing fast and providing contextual error messages. This framework is available in upcoming CDAP release 6.1.1 to provide better user experience. Try out CDAP today and if you would like to explore such exciting challenges, consider contributing to CDAP.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fe646757cabc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/validation-framework-for-cdap-plugins-fe646757cabc">Validation Framework for CDAP Plugins</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting started with CDAP plugin development]]></title>
            <link>https://medium.com/cdapio/getting-started-with-cdap-plugin-development-bcd21cc7ae66?source=rss----52cb94a9300a---4</link>
            <guid isPermaLink="false">https://medium.com/p/bcd21cc7ae66</guid>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[cdap]]></category>
            <category><![CDATA[open-source]]></category>
            <dc:creator><![CDATA[Tony Hajdari]]></dc:creator>
            <pubDate>Mon, 02 Dec 2019 14:01:01 GMT</pubDate>
            <atom:updated>2019-12-02T14:01:01.661Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LD9FLKRky_X8xVyt" /><figcaption>Photo by <a href="https://unsplash.com/@steve_j?utm_source=medium&amp;utm_medium=referral">Steve Johnson</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>One of my favorite features of CDAP is that the extensibility of the platform allows you to add new functionality yourself. If you need to perform a transformation, or need to source or sink data to or from a system that is not currently available in the plugin ecosystem, you can easily add your own plugin to provide that capability.</p><p>Getting started with plugin development is as simple as cloning one of the example plugins and modifying it to add your own implementation logic. In this article I will cover what you’ll need to know to get started with plugin development. I’ll discuss the key configuration elements that you need to be familiar with so you can iterate over the development of the plugin.</p><h3>Prerequisites</h3><p>When writing plugins for CDAP you will need a collection of tools to help you develop, organize, build, and run your code. For this we’ll be relying on a number of tools: <strong>Git, Java 8, IntelliJ IDE, </strong>and<strong> Maven</strong>.</p><p>Fist step is to make sure you have Java 8 SDK installed on your computer. To check which version of Java you have run the following command in a terminal:</p><blockquote><strong>java -version</strong></blockquote><p>If your computer has a newer version of Java you will need to install a version of Java 8 SDK. You can download this from the Java <a href="https://jdk.java.net/java-se-ri/8">web site</a><a href="https://jdk.java.net/java-se-ri/8,">,</a> and we’ll use it later in the Java IDE for you project.</p><p>If you are on Linux you can install it with your package manager, Yum or APT, for Centos/RedHat or Debian/Ubuntu, respectively. For installing Java on a Mac with Homebrew you can use the following commands:</p><blockquote>brew tap AdoptOpenJDK/openjdk</blockquote><blockquote>brew cask install adoptopenjdk8</blockquote><p>If you have the latest version of Java on your Mac and have issues changing the version of Java to 1.8 then refer to this<a href="https://stackoverflow.com/questions/21964709/how-to-set-or-change-the-default-java-jdk-version-on-os-x/24657630#24657630"> Stack Overflow article</a>.</p><p>For writing the actual plugin code we’ll use the IntelliJ Java IDE which you can download from <a href="https://www.jetbrains.com/idea/">here</a>.</p><p>Once our code is written, we’ll use Maven to build the plugin so that we can test it on CDAP. You can download Apache Maven from <a href="https://maven.apache.org/download.cgi">here</a>. After you’ve downloaded Maven extract it to a directory of your choice and set your PATH environment variable so that it is available globally in your terminal. In my case my <strong><em>.bash_profile</em></strong> file has the <strong><em>PATH</em></strong> variable set as follows:</p><blockquote>export PATH=$PATH:/Users/vetoni/apps/tools/apache-maven-3.6.2/bin</blockquote><p>With the environment setup we can now use Git to clone the sample-transform plugin form the repo with the following command:</p><blockquote>git clone <a href="https://github.com/data-integrations/example-transform.git">https://github.com/data-integrations/example-transform.git</a></blockquote><p>Git will create a folder named <strong><em>example-transform</em></strong>, and in that folder you will find a <strong>pom.xml</strong> file. We will use this file to load up the project in IntelliJ. Launch IntelliJ and select <strong><em>open</em></strong>, and make sure to choose <strong><em>Open as Project</em></strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/906/0*JQriPagggNs-sUsN" /></figure><p>After IntelliJ finishes loading all the dependencies you will see a list of directories created with the components that we’ll be using in this tutorial.</p><p>The screen will look something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5JAYCcdyfXVfoEXM" /></figure><p>The plugin in its current state is ready to be built, so you can try building it to make sure that Java 8 and Maven are working happily together. First check to make sure that you have set your Java version to 1.8. From <strong><em>file</em></strong>, select <strong><em>project structure</em></strong> and take note of the Java version listed for the <strong><em>Project SDK</em></strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/792/0*YHFmaw08L9adaPgZ" /></figure><p>If you don’t see Java 1.8 listed in the dropdown you can add it to the project by clicking the <strong><em>New…</em></strong> button and selecting JDK.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Xx-pQoL9Za-dkQ7t" /></figure><p>Pick the location where you downloaded your Java 8 SDK earlier. On my machine Java 8 is located at the following location :</p><blockquote>/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/</blockquote><p>You are now ready to test building the plugin. On the right hand side of the screen you can select the <strong><em>Maven Projects</em></strong> tab and select <strong><em>package</em></strong> from the list of Lifecycle commands.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mjkBc4q6DDpu02HI" /></figure><p>This will invoke the Maven build, test and package targets and create a new <strong><em>target</em></strong> directory with the JAR file containing the plugin — <strong>example-transform-1.1.0-SNAPSHOT.jar</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*HHH0jsNCQzvotGAb" /></figure><h3>Project Structure</h3><p>Before getting started with the actual coding, let’s take a look at the project structure for the plugin. The plugin has a number of files that need to be updated according to your specific needs. Looking at the folder structure you can see the four directories that contain the files we need to modify.</p><ul><li><strong>docs</strong> → ExampleTransform-transform.md</li><li><strong>icons</strong> → ExampleTransform-transform.png</li><li><strong>src</strong> → ExampleTransformPlugin.java</li><li><strong>widgets</strong> → ExampleTransform-transform.json</li></ul><p>In each of these directories, with the exception of the src folder, you will see a file named with the following pattern:</p><blockquote>&lt;PluginName&gt;-&lt;pluginType&gt;.&lt;fileType&gt;</blockquote><p>Take note that the icons folder is optional, so when you first clone the repo you will not see the folder in your project structure. If you don’t create the folder and provide your own 64x64 pixel icon, CDAP will provide a generic plugin icon for you. For this tutorial I’m going to supply my own plugin icon.</p><p>In the Java class you will notice there is a <strong><em>@Name</em></strong> annotation that matches the prefix for all the files used in the directories mentioned above. Pay close attention to the name of the plugin as this is where you may run into issues later on as you refactor the code to change the plugin name.</p><blockquote><strong>NOTE:</strong> The plugin name, as specified via the @Name annotation, must match the file prefixes in docs, icons, and widgets directory.</blockquote><p>At the root directory we also have the <strong>README.md</strong> file that provides information and any documentation you may want to provide to users, as well as the <strong>pom.xml</strong> file that is used to build the plugin artifacts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JjdUb5uRYTR6c-oI" /></figure><p>We are now ready to start customizing the <strong>ExampleTransform</strong> plugin and modify it to suit your needs.</p><p>I’ll start will some refactoring so that the plugin will reflect the intent of the implementation. First I’m going to modify the<strong> pom.xml</strong> and update the following properties with my own values.</p><blockquote>groupId → io.cdap.hydrator.plugins</blockquote><blockquote>artifactId → simple-mask</blockquote><blockquote>version → 1.0.0-SNAPSHOT</blockquote><blockquote>name → Simple Mask</blockquote><blockquote>exported-packages → io.cdap.hydrator.plugin.*</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JF7m1J34ZmBIlAHs" /></figure><p>For this plugin we are going to implement a super simple Rot13 Caesar cipher to obfuscate text data. This cipher simply takes some input text and jumbles the text using the Rot13 algorithm. To help us with the actual implementation we’re going to leverage a pre-existing library that provides us a Rot13 implementation, and were simply going to add it to our pom.xml file.</p><p>Add the following XML snippet to your dependencies section.</p><blockquote>&lt;! — <a href="https://mvnrepository.com/artifact/org.soulwing/rot13">https://mvnrepository.com/artifact/org.soulwing/rot13</a> →</blockquote><blockquote>&lt;dependency&gt;</blockquote><blockquote>&lt;groupId&gt;org.soulwing&lt;/groupId&gt;</blockquote><blockquote>&lt;artifactId&gt;rot13&lt;/artifactId&gt;</blockquote><blockquote>&lt;version&gt;1.0.0&lt;/version&gt;</blockquote><blockquote>&lt;/dependency&gt;</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mlXi4Jz3QmvpL5dv" /></figure><p>IntelliJ will download the dependencies and you will be able to use the cipher classes in your code.</p><h3>Refactor</h3><p>Now we can start refactoring the files I discussed earlier. In IntelliJ you can use Shift+F6 to rename the files, package (folder), and class to <strong>SimpleTransform</strong>. I chose to name it something more generic so I can come back to it in the future and add more Caesar ciphers. Later I will refactor this plugin once more to change its name.</p><p>After you refactor the project files will look as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*L0uG2ExHLqgHDcuY" /></figure><p>Now’s a good time to build the project once again to make sure that nothing is broken. From the terminal you can run the following command to build the project:</p><blockquote><strong>mvn clean package</strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rQPUF1H5RsU_a3GQ" /></figure><p>When the build runs, it will scan the <strong>widgets</strong> and <strong>docs</strong> directories in order to build an appropriately formatted <strong>.json</strong> file under the `<strong>target`</strong> directory. This file is deployed along with your <strong>.jar</strong> file to add your plugin to CDAP.</p><p>We can see Maven creates a target directory where we can find the plugin artifacts:</p><p><strong>-&gt; simple-mask-1.0.0-SNAPSHOT.jar</strong></p><p><strong>-&gt; simple-mask-1.0.0-SNAPSHOT.json</strong></p><p>We can now deploy the plugin to see what it looks like in CDAP Studio. Launch CDAP studio and click on the green plus icon and upload the plugin as illustrated.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*oblihjxEpME1UVy_" /></figure><h3>Time to Code</h3><p>We now have all the prep and setup work out of the way, and we have validated that the plugin compiles and loads into CDAP correctly. Now we can turn our attention to coding. In the next step we’ll write the logic for the transform plugin that takes text as input and generates scrambled text as output. Along the way I will refactor the code as I go so that it matches my requirements and will rename the plugin to <strong>ScrambleText</strong> so that its intent and purpose is absolutely clear.</p><p>All plugins have an API that includes a number of annotations as well as some configuration, initialization, validation, and depending on the plugin type, a method that implements the plugin’s core functionality. In this case, the majority of the implementation for a transform plugin in done in the <strong>transform()</strong> method. For further details on the various plugin types that you can build and how the API works please refer to the <a href="https://docs.cdap.io/cdap/6.0.0/en/developer-manual/pipelines/developing-plugins/creating-a-plugin.html#H2614">CDAP documentation page</a> for reference.</p><p>Replace this section of code…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*PwjMYb63K4OTZliv" /></figure><p>So that it looks like the following snippet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-WncN3dv5VQfFZUM" /></figure><p>As you can see we make use of the Rot13 class to convert the input string and set it to the output value for that record. IntelliJ should auto import the class for you when you use this class.</p><p>Next, we need to handle user input from the plugin configuration in CDAP studio. Input that is collected from the user will dictate how the plugin needs to behave and what output it should generate. Input validation is another important function that needs to be performed as you can never be sure that you will be getting valid data from a user. The degree as to how you configure your widgets and how you handle use input is totally up to you and what features you want to include in your plugin. For this example we’re going to keep it as simple as possible just to illustrate the concepts. For now make sure your config class looks like the following illustration:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*_2d7-omzILe6GlGg" /></figure><p>Now we can turn out attention to customizing the plugin configuration form so that it looks like the following illustration. Plugin presentation is configured in the widget JSON file. Refer to the CDAP documentation for all the <a href="https://docs.cdap.io/cdap/6.0.0/en/developer-manual/pipelines/developing-plugins/presentation-plugins.html">presentation widgets</a> that you can use in your plugins.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*B9V0xxdbytfPgzkT" /></figure><p>Replace the contents of the widgets JSON file with the following, and take note of the <strong>name</strong> used for the KeyValue widget as this is the variable that is used in the <strong>Config</strong> section of the code to work with form inputs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wuEGZFVpseWBm5k5Jsme0w.png" /></figure><p>Right now the plugin doesn’t do much validation, and it only looks for the existence of an input field that will contain text to be scrambled. It will take all the fields from the previous stage and apply the Rot13 algorithm to generate scrambled output tex. At this point you’ll need to either comment out the code in the test class, as it will fail to build, or skip the tests for now when running the Maven build.</p><p>Finally, time to test the plugin in CDAP studio. At this stage of the plugin’s evolution it should be able to take input from a node in a pipeline, scramble the text, and output the scrambled text to the next node in the pipeline. There is only one validation that the plugin performs — it checks for the existence of a <strong>scrambleText</strong> input from the plugin configuration. Even though the plugin does not actually do anything with this data it needs to be provided in the plugin configuration in order for the plugin validation to succeed.</p><h3>Looking Ahead</h3><p>There is still more to be done! As an exercise, I leave up to you to expand upon the code to add the form validation as well as select the input fields the plugin will operate upon.</p><p>Further enhancements you can consider would be to include additional ciphers that the user can select from the drop-down. You will also need to update the test class to match the inputs, transformations, and outputs you want this plugin to test.</p><h3>Summary</h3><p>In this article we reviewed the steps required to get started with plugin development. There are a number of example plugins that can be cloned from the <a href="https://github.com/data-integrations">data-integrations</a> GitHub repo that provide a quick start to plugin development. Using the sample plugin as a starting point for your custom plugin saves you from having to code all the boilerplate, and you’ll have all the requisite classes and methods in place to give you a springboard into development.</p><p>There are over a dozen plugin types available in CDAP. Although we did not go into any details of the Plugin API itself, the plugin API is fairly well documented on CDAP’s <a href="https://docs.cdap.io/cdap/6.0.0/en/developer-manual/pipelines/developing-plugins/index.html">documentation</a> page, so that you can dive deeper into the API on your own and learn about all the different types of plugins you can build.</p><p>Testing you plugin is also an important part of the development process, but that’s a topic for another blog article. In future articles we’ll look at plugin validation and testing in grater detail.</p><p>Hopefully this has given you sense of how to get started with developing plugins for CDAP. So, jump right in and create a few cool plugins of your own. Happy coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bcd21cc7ae66" width="1" height="1" alt=""><hr><p><a href="https://medium.com/cdapio/getting-started-with-cdap-plugin-development-bcd21cc7ae66">Getting started with CDAP plugin development</a> was originally published in <a href="https://medium.com/cdapio">cdapio</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>