API Files

Requiring External Requests

Some plugins are required to make external API calls in order to function. To assist in this matter, creating an api.py file can be used to make calls to an external API that are utilised by the plugin and also be used in parsing the responses from the API.

Typically, it is best to organize utility files such that the api.py file is inside a new directory called utils within the plugin structure to maintain a standardized and organized framework. This helps the code within this file to be easily accessible by any action, trigger, or connection.

example-plugin/example-plugin/utils/api.py

python
1
import requests
2
from insightconnect_plugin_runtime.exceptions import PluginException
3
import json
4
5
class ExamplePluginAPI:
6
def __init__(self, logger):
7
self.logger = logger
8
self.url = "https://www.example-url.com/api/v1"
9
10
def _call_api(self, method, url, params=None, json_data=None):
11
try:
12
# Make the API call
13
response = requests.request(method, url, json=json_data, params=params)
14
15
# Usually, 204 returns None if successful
16
if response.status_code == 204:
17
return None
18
# All other 200 codes, return response.json()
19
if response.status_code in range(200, 300):
20
return response.json()
21
# Handle errors and raise PluginException
22
if response.status_code == 404:
23
raise PluginException(preset=PluginException.Preset.NOT_FOUND)
24
if response.status_code == 500:
25
raise PluginException(preset=PluginException.Preset.SERVER_ERROR)
26
if response.status_code == 503:
27
raise PluginException(preset=PluginException.Preset.SERVICE_UNAVAILABLE)
28
if response.status_code >= 400:
29
response_data = response.json()
30
raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response_data.message)
31
# Handle JSON errors
32
except json.decoder.JSONDecodeError as exception:
33
self.logger.info(f"Invalid JSON: {exception}")
34
raise PluginException(preset=PluginException.Preset.INVALID_JSON, data=response.text)
35
# Handle HTTP errors
36
except requests.exceptions.HTTPError as exception:
37
self.logger.info(f"Call to Example API failed: {exception}")
38
raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text)
39
40
41
def list_users(self) -> str:
42
return self._call_api("GET", "users/list")
43
44
def add_user(self, params):
45
return self._call_api("POST", "users/add", params=params)
46
47
# etc

This example uses the Requests module to make an external API call, attempts to handle any errors arising from unexpected responses, and returns the result of the HTTP request.

In this file we define a class to contain the functions and logic necessary to interact with our chosen API.

By creating individual functions for each request such as the list_users method, which calls upon the _call_api function, we can simplify our API access logic.

Responses are also handled using PluginException, InsightConnects custom exception type which allows for a standardized response for plugin users and debugging.

Using the API File to Send Requests

To utilize these requests we can use the created API class as a property of the Connection class within the connection.py file created when the insight-plugin create command was first run.

python
1
import insightconnect_plugin_runtime
2
from .schema import ConnectionSchema, Input
3
4
# Custom imports below
5
from example_plugin.util.api import ExampleAPI
6
7
8
class Connection(insightconnect_plugin_runtime.Connection):
9
def __init__(self):
10
super(self.__class__, self).__init__(input=ConnectionSchema())
11
self.client = None
12
13
def connect(self, params):
14
self.logger.info("Connect: Connecting...")
15
16
# Retrieve all inputs
17
account_id = params.get(Input.ACCOUNT_ID)
18
client_id = params.get(Input.CLIENT_ID)
19
client_secret = params.get(Input.CLIENT_SECRET, {}).get("secretKey")
20
21
# Connect to API
22
self.example_api = ExampleAPI(
23
logger=self.logger,
24
account_id=account_id,
25
client_id=client_id,
26
client_secret=client_secret,
27
)
28
29
def test(self):
30
return {"success": True}

In this example, we’ve assigned our ExampleAPI class to the client property of our connection.

We’re now able to access these API contact methods from our action files run method and return the response to complete the plugin action.

python
1
import insightconnect_plugin_runtime
2
from .schema import GetUsersInput, GetUsersOutput, Input, Output, Component
3
4
5
class GetUsers(insightconnect_plugin_runtime.Action):
6
7
def __init__(self):
8
super(self.__class__, self).__init__(
9
name="get_user",
10
description=Component.DESCRIPTION,
11
input=GetUserInput(),
12
output=GetUserOutput())
13
14
def run(self, params={}):
15
# Get input from schema
16
user_id = params.get(Input.USER_ID)
17
# Call function from api.py file
18
users = self.connection.client.list_user_by_id(user_id)
19
# Map schema to output
20
return {Output.USERS: users}

Individual plugin authors are responsible for writing the api.py code.