# Creating Plugins

Use this guide to author your own Omnata plugin.

In the previous sections, we walked through some example plugins to demonstrate how they work.

Here, we'll cover the development tools that Omnata provides to help simplify the task and make you productive.

In this example, we'll build a plugin which supports outbound syncing. Once you're familiar with this, the inbound functionality works similarly.

### Summary

Ultimately, creating a plugin is an simple as uploading a python class with a product logo, and optionally some other python files or libraries. These are packaged and distributed as Snowflake Native Applications.

Omnata provides a development kit which helps by providing:

* a plugin template as a starting point
* a convenient command-line interface (CLI) which takes care of uploading and packaging your plugin code, and registering it with the Omnata Application
* a "development session" concept, so that you can use an interactive session (e.g. a Jupyter notebook) to build the plugin functionality incrementally with a fast feedback loop, and verify that it's behaviour is correct
* automated test case generation, including HTTP recording with secrets redacted

Once it's working correctly, we can migrate it all into the plugin class and tidy it up.

### Step 1 - App Familiarisation

Read over your application's API documentation to gain familiarisation with it.

Questions to answer:

* What use cases does the application have for receiving data from Snowflake? What are some examples?
* What style of sync will it be? Event-style, where Snowflake rows are published once? Or record-style, where ongoing changes are replicated?
* How do people typically call the API? Is there a python package we can leverage, or will we make http requests?
* What information do we need to connect to the app? (e.g. API Key, OAuth Client Credentials)
* What information do we need to collect to configure the sync behaviour? (e.g. names of objects/fields in the app, any other options exposed by the app's API)
* What rate limits apply to the API?

### Step 2 - Environment setup

To enable iterative prototyping of a plugin, we recommend using [Visual Studio Code](https://code.visualstudio.com/) with the [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python). This will help you manage your Python environment, and also includes Jupyter support which works nicely with our SDK.

Your Python plugin is able to use any of the Python packages available in the [Snowflake Anaconda channel](https://repo.anaconda.com/pkgs/snowflake/) or on PyPi. By adding the package to the requirements.txt file, the Plugin uploader will either reference it on Anaconda or download it from PyPi.

We recommend using a Python environment manager like `conda` to keep your plugin's environment controlled and minimal.

In order to develop plugins, you'll need to install the Omnata Plugin Devkit:

```
pip install omnata-plugin-devkit
```

After this, all plugin development commands can be see by running:

```
omnata plugin_dev -h
```

Finally, the Omnata plugin devkit uses [snowcli](https://github.com/Snowflake-Labs/snowcli) to authenticate to your Snowflake environment, which means you must follow its onboarding instructions first.

### Step 3 - Create Plugin from template

We recommend you create a git repository and clone it first, so that you can properly manage changes to your code.

Run the following command to generate a plugin project into the current directory:

```
omnata plugin_dev init
```

Start by editing the plugin's `get_manifest` method, so that it returns a unique name and the real label for your app, as well as the sync directions and strategies you intend to support.

Then you can run the following command to upload your plugin to your Snowflake account:

```
omnata plugin_dev upload
```

After this command completes, unless you overrode the defaults:

* Your code will have been uploaded to a stage named `OMNATA_PLUGIN_DEVELOPMENT.<Plugin ID>.PLUGIN_CODE`
* An [application package](https://docs.snowflake.com/en/developer-guide/native-apps/creating-app-package) named `<Plugin ID>_PLUGIN` will have been created, with a version named `DEVELOPMENT`

Then you can create/update an instance of your plugin by running:

```
omnata plugin_dev deploy <package name from above> <application name>
```

This will create an [application](https://docs.snowflake.com/en/sql-reference/sql/create-application) from the above application package.

There's one final step which only needs to be done once, initially. Because applications have no visiblity of each other by default, we have to register the plugin application with the Omnata Sync Engine:

```
omnata plugin_dev register <application name>
```

Now in the Omnata UI, you should see your plugin in the list.

### Step 4 - Connect to your App

Edit the plugin's `connection_form` method so that it returns the fields you want to present to the user. You can see the form whenever you create a new connection in the UI.

Also edit the plugin's `connect` method to have it do a direct connection test, or return OAuth parameters to commence an OAuth flow.

Upload the plugin using sample code shown in the `plugin_upload.ipynb` notebook. There will be warnings to say that the plugin is incomplete, that's ok.

Once it's uploaded, you can go into the Omnata UI, create a new Connection, then select your new app from the list. Ensure that the connection is created without errors.

### Step 5 - Define Sync Parameters

Now modify the `outbound_configuration_form` method to return the fields required for sync configuration. This usually involves at least one fixed value (like the Salesforce object name or the Slack channel name).

To map the Snowflake table data to something inside the app, you choose an appropriate type of `Mapper` to return:

* `FormFieldMappingSelector` provides a visual column -> field mapper. Your Plugin provides a list of possible target fields in the app, and the user selects which Snowflake columns to map to them. This mapper is most common in record-based systems like Salesforce CRM.
* `FormJinjaTemplate` provides a text-based template, which can have Snowflake column values interpolated. This mapper is most common in event-based systems like Slack.

Once you've defined the form, upload the plugin again to apply the changes. Then, create a Sync in Omnata. Just choose a manual schedule so that it never runs automatically.

### Step 6 - Sample data

Now, create a table with sample data. This will be used to test the plugin.

### Step 7 - Build via a development session

Use the example code provided in the `plugin_development.ipynb` notebook to create a development session.

A development session simplifies the hardest part of plugin development; writing the code that talks to the App. It does this by connecting to your Snowflake account and retrieving what's normally provided to the `sync_outbound` method during a scheduled sync.

Instead of having to continually upload new versions of your plugin and re-run syncs until the `sync_outbound` method works perfectly start to finish, you can simply use the `parameters` and `sync_request` objects in your Jupyter notebook to gradually construct the code.

As part of this, you'll capture the outcome of the API operation back into a DataFrame to return to Omnata. This DataFrame contains the columns:

* IDENTIFIER - Provided in the original apply request
* SUCCESS - A boolean flag to indicate true/false
* APP\_IDENTIFIER - (Optional) an identifier for this record on the app side
* RESULT - A JSON object containing any useful/relevant information about the record or specific errors encountered. Error messages returned under the "error" property will be displayed in the UI for troubleshooting.

The response DataFrame can be validated with:

```python
dev_sess.validate_response(sync_request)
```

This will return a Success or Failure message back to the terminal

Once you get a success message, you can complete the scenario:

```python
dev_session.complete_scenario()
```

### Step 8 - Generate test cases automatically

Once your Jupyter notebook code is working, and you can see data appearing in your app correctly, you should move the code from your Jupyter notebook into the `apply_records` method of your plugin.

Once this is done you can re-run your development session start to finish going directly against your completed plugin class, as shown in `plugin_test.ipynb`.

If it all works as expected, you can generate a [behave](https://behave.readthedocs.io/en/stable/) test:

```python
dev_sess.generate_behave_test()
```

This will output a feature file, as well as a [VCR.py](https://vcrpy.readthedocs.io/en/latest/index.html) cassette containing your HTTP requests and responses, which is referenced in the behave test.

{% hint style="warning" %}
The test case generator assumes that everything worked correctly, but you need to confirm this yourself!

In other words, the test case is useful for ensuring you don't inadvertently break this scenario in future, but it gives no guarantees of original correctness.

It also relies on recordings of the app responses, which assumes the app's behaviour will remain consistent.
{% endhint %}

This behave test can be ran at any time, without needing to connect to Snowflake or your App.

You can continue to record and generate new test cases to cover other sync scenarios and account for different behaviours in the app.

### Step 9 - Implement inbound syncing

Similar to outbound, implement the `inbound_configuration_form` and `sync_inbound` functions to enable inbound syncing.

### Step 10 - Complete your plugin

With the plugin fully working locally, you can upload it again.

Now Omnata can start syncing data for you by scheduling the sync you created.

###


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.omnata.com/omnata-product-documentation/omnata-sync-for-snowflake/plugins/creating-plugins.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
