Robotic Process Automation (RPA)

 View Only

How to create an IBM RPA connector for any API

By Joba Diniz posted Fri April 08, 2022 03:21 PM

  
Following the previous article on how to better design libraries in IBM RPA, let's build an IBM RPA connector to AccuWeather API, specifically the Forecast API and Locations API.
To use the APIs, we need an API Key. So, first, you need to register. Then you need to create an app within AccuWeather selecting the "Limited Trial" option. Save your API Key.

We won't build an entire connector for every API available. For demonstration purposes, we will build the connector for 2 APIs:
  1. City Search using the Locations API.
  2. 5 Days of Daily Forecasts using the Forecast API.
Here is the implemented AccuWeather.wal connector. Further down we explain it.
defVar --name apiKey --type String --parameter
defVar --name locationKey --type String --parameter
defVar --name isDebug --type Boolean
defVar --name methodNames --type List --innertype String --parameter
defVar --name methodName --type String --parameter
defVar --name success --type Boolean
defVar --name httpResponse --type String
defVar --name httpStatusCode --type Numeric
defVar --name httpReasonPhrase --type String
defVar --name iconMapping --type StringDictionary --innertype String
defVar --name forecasts --type DataTable --output
defVar --name forecastHeadline --type String --output
defVar --name i --type Numeric
defVar --name mapJsonKey --type String
defVar --name forecastDay --type String
defVar --name dateText --type String
defVar --name forecastDate --type DateTime
defVar --name forecastUnit --type String
defVar --name forecastMinTemperature --type Numeric
defVar --name forecastMaxTemperature --type Numeric
defVar --name forecastIconId --type String
defVar --name forecastIconPhrase --type String
defVar --name forecastIcon --type String
defVar --name query --type String --parameter
defVar --name locations --type DataTable --output
defVar --name jsonTable --type DataTable
defVar --name jsonRegion --type String
defVar --name jsonCountry --type String
defVar --name locationCountry --type String
defVar --name locationRegion --type String
defVar --name locationCity --type String
#region debug data
executeScript --name Utils --parameters "methodName=IsDebug" --output "isDebug=${isDebug}" --comment "methods: IsDebug"
setVarIf --variablename "${apiKey}" --value "<MY API KEY>" --left "${isDebug}" --operator "Is_True"
//setVarIf --variablename "${methodName}" --value SearchCities --left "${isDebug}" --operator "Is_True"
//setVarIf --variablename "${locationKey}" --value 858642 --left "${isDebug}" --operator "Is_True"
//setVarIf --variablename "${query}" --value "sao paulo" --left "${isDebug}" --operator "Is_True"
#endregion

#region validating
case --switches "CaseSwitchesAll"
	when --left "${methodName}" --operator "Is_Null_Or_Empty"
	when --left "${methodNames}" --operator "Is_Empty"
then
	failTest --message "The parameter \'methodName\' or \'methodNames\' is required and was not specified"
endCase
#endregion

if --left "${methodName}" --operator "Is_Null_Or_Empty" --negate
	add --collection "${methodNames}" --value "${methodName}"
endIf
foreach --collection "${methodNames}" --variable "${methodName}" --distinct
	goSub --label "${methodName}"
endFor
beginSub --name SearchCities
	httpRequest --verb "Get" --url "http://dataservice.accuweather.com/locations/v1/cities/search?apikey=${apiKey}&q=${query}" --cookiecontainer  --noproxy  success=success httpResponse=value httpStatusCode=statusCode httpReasonPhrase=reasonPhrase
	assert --message "Request failed with \"${httpStatusCode}\": ${httpReasonPhrase}" --left "${success}" --operator "Is_True"
	gosubIf --label __LogHttpResponse --left "${isDebug}" --operator "Is_True"
	goSub --label __BuildLocations
endSub
beginSub --name DailyForecasts
	httpRequest --verb "Get" --url "http://dataservice.accuweather.com/forecasts/v1/daily/5day/${locationKey}?apikey=${apiKey}&metric=true" --cookiecontainer  --noproxy  success=success httpResponse=value httpStatusCode=statusCode httpReasonPhrase=reasonPhrase
	assert --message "Request failed with \"${httpStatusCode}\": ${httpReasonPhrase}" --left "${success}" --operator "Is_True"
	gosubIf --label __LogHttpResponse --left "${isDebug}" --operator "Is_True"
	goSub --label __IconMapping
	goSub --label __BuildForecasts
endSub
beginSub --name __LogHttpResponse
	logMessage --message "\r\n${httpResponse}" --type "Info"
endSub
beginSub --name __IconMapping
	strDictAdd --key 1 --value "https://developer.accuweather.com/sites/default/files/01-s.png" --dictionary ${iconMapping}
	strDictAdd --key 2 --value "https://developer.accuweather.com/sites/default/files/02-s.png" --dictionary ${iconMapping}
	strDictAdd --key 3 --value "https://developer.accuweather.com/sites/default/files/03-s.png" --dictionary ${iconMapping}
	strDictAdd --key 4 --value "https://developer.accuweather.com/sites/default/files/04-s.png" --dictionary ${iconMapping}
	strDictAdd --key 5 --value "https://developer.accuweather.com/sites/default/files/05-s.png" --dictionary ${iconMapping}
	strDictAdd --key 6 --value "https://developer.accuweather.com/sites/default/files/06-s.png" --dictionary ${iconMapping}
	strDictAdd --key 7 --value "https://developer.accuweather.com/sites/default/files/07-s.png" --dictionary ${iconMapping}
	strDictAdd --key 8 --value "https://developer.accuweather.com/sites/default/files/08-s.png" --dictionary ${iconMapping}
	strDictAdd --key 11 --value "https://developer.accuweather.com/sites/default/files/11-s.png" --dictionary ${iconMapping}
	strDictAdd --key 12 --value "https://developer.accuweather.com/sites/default/files/12-s.png" --dictionary ${iconMapping}
	strDictAdd --key 13 --value "https://developer.accuweather.com/sites/default/files/13-s.png" --dictionary ${iconMapping}
	strDictAdd --key 14 --value "https://developer.accuweather.com/sites/default/files/14-s.png" --dictionary ${iconMapping}
	strDictAdd --key 15 --value "https://developer.accuweather.com/sites/default/files/15-s.png" --dictionary ${iconMapping}
	strDictAdd --key 16 --value "https://developer.accuweather.com/sites/default/files/16-s.png" --dictionary ${iconMapping}
	strDictAdd --key 17 --value "https://developer.accuweather.com/sites/default/files/17-s.png" --dictionary ${iconMapping}
	strDictAdd --key 18 --value "https://developer.accuweather.com/sites/default/files/18-s.png" --dictionary ${iconMapping}
	strDictAdd --key 20 --value "https://developer.accuweather.com/sites/default/files/20-s.png" --dictionary ${iconMapping}
	strDictAdd --key 21 --value "https://developer.accuweather.com/sites/default/files/21-s.png" --dictionary ${iconMapping}
	strDictAdd --key 22 --value "https://developer.accuweather.com/sites/default/files/22-s.png" --dictionary ${iconMapping}
	strDictAdd --key 23 --value "https://developer.accuweather.com/sites/default/files/23-s.png" --dictionary ${iconMapping}
	strDictAdd --key 24 --value "https://developer.accuweather.com/sites/default/files/24-s.png" --dictionary ${iconMapping}
	strDictAdd --key 25 --value "https://developer.accuweather.com/sites/default/files/25-s.png" --dictionary ${iconMapping}
	strDictAdd --key 26 --value "https://developer.accuweather.com/sites/default/files/26-s.png" --dictionary ${iconMapping}
	strDictAdd --key 29 --value "https://developer.accuweather.com/sites/default/files/29-s.png" --dictionary ${iconMapping}
	strDictAdd --key 30 --value "https://developer.accuweather.com/sites/default/files/30-s.png" --dictionary ${iconMapping}
	strDictAdd --key 31 --value "https://developer.accuweather.com/sites/default/files/31-s.png" --dictionary ${iconMapping}
	strDictAdd --key 32 --value "https://developer.accuweather.com/sites/default/files/32-s.png" --dictionary ${iconMapping}
	strDictAdd --key 33 --value "https://developer.accuweather.com/sites/default/files/33-s.png" --dictionary ${iconMapping}
	strDictAdd --key 34 --value "https://developer.accuweather.com/sites/default/files/34-s.png" --dictionary ${iconMapping}
	strDictAdd --key 35 --value "https://developer.accuweather.com/sites/default/files/35-s.png" --dictionary ${iconMapping}
	strDictAdd --key 36 --value "https://developer.accuweather.com/sites/default/files/36-s.png" --dictionary ${iconMapping}
	strDictAdd --key 37 --value "https://developer.accuweather.com/sites/default/files/37-s.png" --dictionary ${iconMapping}
	strDictAdd --key 38 --value "https://developer.accuweather.com/sites/default/files/38-s.png" --dictionary ${iconMapping}
	strDictAdd --key 39 --value "https://developer.accuweather.com/sites/default/files/39-s.png" --dictionary ${iconMapping}
	strDictAdd --key 40 --value "https://developer.accuweather.com/sites/default/files/40-s.png" --dictionary ${iconMapping}
	strDictAdd --key 41 --value "https://developer.accuweather.com/sites/default/files/41-s.png" --dictionary ${iconMapping}
	strDictAdd --key 42 --value "https://developer.accuweather.com/sites/default/files/42-s.png" --dictionary ${iconMapping}
	strDictAdd --key 43 --value "https://developer.accuweather.com/sites/default/files/43-s.png" --dictionary ${iconMapping}
	strDictAdd --key 44 --value "https://developer.accuweather.com/sites/default/files/44-s.png" --dictionary ${iconMapping}
endSub
beginSub --name __BuildForecasts
	addColumn --dataTable ${forecasts} --columnname Date --type DateTime
	addColumn --dataTable ${forecasts} --columnname Minimum --type Numeric
	addColumn --dataTable ${forecasts} --columnname Maximum --type Numeric
	addColumn --dataTable ${forecasts} --columnname Unit --type String
	addColumn --dataTable ${forecasts} --columnname Icon --type String
	addColumn --dataTable ${forecasts} --columnname Text --type String
	mapJson --json "${httpResponse}" --mappings "$.Headline.Text=${forecastHeadline}"
	for --variable ${i} --from 0 --to 5 --step 1
		setVar --name "${mapJsonKey}" --value "$.DailyForecasts[${i}]" --comment "this is required because of this bug: https://jsw.ibm.com/browse/DBACLD-43741"
		mapJson --handleError  --json "${httpResponse}" --mappings "${mapJsonKey}=${forecastDay}" success=value
		if --left "${success}" --operator "Is_True" --negate
			break
		endIf
		mapJson --json "${forecastDay}" --mappings "$.Date=${dateText}"
		textToDateTime --culture "en-US" --text "${dateText}" --usecustomformat  --customformat "M/d/yyyy h:mm:ss tt" forecastDate=value
		mapJson --json "${forecastDay}" --mappings "$.Temperature.Minimum.Unit=${forecastUnit}"
		mapJson --json "${forecastDay}" --mappings "$.Temperature.Minimum.Value=${forecastMinTemperature}"
		mapJson --json "${forecastDay}" --mappings "$.Temperature.Maximum.Value=${forecastMaxTemperature}"
		mapJson --json "${forecastDay}" --mappings "$.Day.Icon=${forecastIconId}"
		strDictGet --key "${forecastIconId}" --dictionary ${iconMapping} forecastIcon=value
		mapJson --json "${forecastDay}" --mappings "$.Day.IconPhrase=${forecastIconPhrase}"
		addRow --valuesmapping "Date=${forecastDate},Minimum=${forecastMinTemperature},Maximum=${forecastMaxTemperature},Icon=${forecastIcon},Text=${forecastIconPhrase},Unit=${forecastUnit}" --dataTable ${forecasts}
	next
endSub
beginSub --name __BuildLocations
	addColumn --dataTable ${locations} --columnname Key --type String
	addColumn --dataTable ${locations} --columnname Region --type String
	addColumn --dataTable ${locations} --columnname Country --type String
	addColumn --dataTable ${locations} --columnname City --type String
	jsonToTable --json "${httpResponse}" --jsonPath "$" jsonTable=value
	for --variable ${i} --from 1 --to ${jsonTable.Rows} --step 1
		mapTableRow --dataTable ${jsonTable} --row ${i} --mappings "name=Key=${locationKey},name=EnglishName=${locationCity},name=Region=${jsonRegion},name=Country=${jsonCountry}"
		mapJson --json "${jsonCountry}" --mappings "$.EnglishName=${locationCountry}"
		mapJson --json "${jsonRegion}" --mappings "$.EnglishName=${locationRegion}"
		addRow --valuesmapping "Key=${locationKey},Region=${locationRegion},Country=${locationCountry},City=${locationCity}" --dataTable ${locations}
	next
endSub​

This exposes 2 methods:
SearchCities implementing City Search.
Parameters
  • String apiKey
  • String query
Outputs
  • DataTable locations with the following columns: String Key, String Region, String Country and String City
DailyForecasts implementing 5 Days of Daily Forecasts.
Parameters
  • String apiKey
  • String locationKey retrieved from Key column of SearchCities locations output.
Outputs
  • String forecastHeadline
  • DataTable forecasts with the following columns: DateTime Date, Numeric Minimum, Numeric Maximum, String Unit, String Icon, String Text.
Notice that we have private methods in AccuWeather script. We name those methods with a double underscore prefix, like __BuildLocations. That was a good practice to build libraries, so we explicitly say which methods are public and which are private.
We also used the Utils library created in the last article, to query whether we are running in debug mode. This allows us to test the script with hardcoded data using the setVarIf command. Using such command with isDebug condition won't require us to change the library before publishing it to production.

Okay, how can I use it?
SearchCities:
executeScript --name AccuWeather --parameters "methodName=SearchCities,query=london" --output "locations=${locations}"​

//loop through the data table
for --variable ${i} --from 1 --to ${locations.Rows} --step 1
	if --left "${i}" --operator "Equal_To" --right 5 --comment "get only 5 cities"
		break
	endIf
	mapTableRow --dataTable ${locations} --row ${i} --mappings "name=Key=${locationKey},name=Region=${locationRegion},name=Country=${locationCountry},name=City=${locationCity}"
next
DailyForecasts:
executeScript --name AccuWeather --parameters "methodName=DailyForecasts,locationKey=${userAnswer}" --output "forecasts=${forecasts},forecastHeadline=${forecastHeadline}"
mapTableRow --dataTable ${forecasts} --row 1 --mappings "name=Minimum=${forecastMinimum},name=Maximum=${forecastMaximum},name=Text=${forecastText},name=Unit=${forecastUnit}"​

Shouldn't we fetch the Api Key from the Control Center parameters?
We could, but that is not the responsibility of the library. If we did that, we would require everyone to create such parameter before using the library. Instead, we provided the flexibility to consumers to pass the parameter in any form they wish to.

We demonstrated how we can create connectors in IBM RPA using good development practices. Stay tuned to find out how to use such libraries within a chatbot.
0 comments
142 views

Permalink