Waiting for engine...
Skip to main content

Adding Browse functionality

Using a custom connector for a specific REST-based web service brings about a few notable benefits over using a technology connector like the REST Client Connector. One of them is providing request and response profiles so that the user can know how to structure their integrations to handle the inputs and outputs of your connector. You can achieve this by providing the ability to browse with your connector.

Let’s assume you have a list of objects that you want to perform some kind of action on. An action can be performing an HTTP request on it like GET, POST, PUT, or DELETE. Integrators know the allowed actions based on the user interface that is generated from your descriptor file. They also need to know the objects on which they can perform these actions and the input and output profiles of the operation. Here is where implementing a Browser would be beneficial.

Sample code
public class SampleRestBrowser extends BaseBrowser implements ConnectionTester {

protected SampleRestBrowser(BrowseContext context) {
super(context);
}

@Override
public ObjectTypes getObjectTypes() {
// Here is the list of objects that your operation can be performed on
return new ObjectTypes().withTypes(
new ObjectType().withId("object1").withLabel("Object 1"),
new ObjectType().withId("object2").withLabel("Object 2"),
new ObjectType().withId("object3").withLabel("Object 3"));
}

@Override
public ObjectDefinitions getObjectDefinitions(String objectTypeId, Collection<ObjectDefinitionRole> roles) {
// Load schema as a string
String schema = getSchema();
ObjectDefinitions definitions = new ObjectDefinitions();
for (ObjectDefinitionRole role : roles) {
definitions.getDefinitions()
.add(new ObjectDefinition().withElementName("/definitions/" + objectTypeId)
.withInputType((role == ObjectDefinitionRole.INPUT) ? ContentType.JSON
: ContentType.NONE)
.withOutputType((role == ObjectDefinitionRole.OUTPUT) ? ContentType.JSON : ContentType.NONE)
.withJsonSchema(schema));
}
return definitions;
}

/**
* Loads the packaged schema resource.
*
* @return string representation of the JSON schema
*/
private static String getSchema() {
// assuming we have a resource json file named "schema.json"
try (InputStream input = ClassUtil.getResourceAsStream("schema.json")) {
return StreamUtil.toString(input, "utf-8");
} catch (IOException e) {
throw new ConnectorException(e);
}
}

@Override
public void testConnection() {
// replace when finished
throw new UnsupportedOperationException("Not yet implemented");
}

}

A schema.json file that works for the above code can be:
{
"definitions": {
"object1": {
"properties": {
"code": {
"format": "int32",
"type": "integer"
},
"message": {
"type": "string"
},
"type": {
"type": "string"
}
},
"type": "object"
},
"object2": {
"properties": {
"date": {
"format": "date-time",
"type": "string"
},
"message": {
"type": "string"
}
},
"type": "object"
},
"object3": {
"properties": {
"complete": {
"type": "boolean"
}
},
"type": "object"
}
}
}

Browsing is a 2 step process where you are connecting to a runtime twice

  • once to get a list of objects
  • then again to get the profiles of the selected object

When the object types are known at development time (perhaps they are a set list), it can make for a better user experience to skip the first round trip to the runtime and provide the static list of objects to the Platform UI. This makes configuring an operation faster for the user since a browse function takes some time to complete. You can achieve this by introducing provided objects.

Sample implementation of this in your connector descriptor file:
<?xml version="1.0" encoding="UTF-8"?>
<GenericConnectorDescriptor requireConnectionForBrowse="true">

<testConnection method="CUSTOM" />

<operation types="EXECUTE" supportsBrowse="true" customTypeId="POST" customTypeLabel="Create">
<browseConfiguration>
<objectTypes>
<object id="object1" label="Object 1"/>
<object id="object2" label="Object 2"/>
<object id="object3" label="Object 3"/>
</objectTypes>
</browseConfiguration>
</operation>

</GenericConnectorDescriptor>

With the above descriptor, you have defined a Create operation. The Browser code then looks like this:

Sample Browser code
public class SampleRestBrowser extends BaseBrowser implements ConnectionTester {

protected SampleRestBrowser(BrowseContext context) {
super(context);
}

/**
* Throws an UnsupportedOperationException to prevent the usage of this method.
* The connector developer should provide object types through the connector-descriptor instead.
*/
@Override
public ObjectTypes getObjectTypes() {
throw new UnsupportedOperationException();
}


@Override
public ObjectDefinitions getObjectDefinitions(String objectTypeId, Collection<ObjectDefinitionRole> roles) {
// Load schema as a string
String schema = getSchema();
ObjectDefinitions definitions = new ObjectDefinitions();
for (ObjectDefinitionRole role : roles) {
definitions.getDefinitions()
.add(new ObjectDefinition().withElementName("/definitions/" + objectTypeId)
.withInputType((role == ObjectDefinitionRole.INPUT) ? ContentType.JSON
: ContentType.NONE)
.withOutputType((role == ObjectDefinitionRole.OUTPUT) ? ContentType.JSON : ContentType.NONE)
.withJsonSchema(schema));
}
return definitions;
}

/**
* Loads the packaged schema resource.
*
* @return string representation of the JSON schema
*/
private static String getSchema() {
// assuming we have a resource json file named "schema.json"
try (InputStream input = ClassUtil.getResourceAsStream("schema.json")) {
return StreamUtil.toString(input, "utf-8");
} catch (IOException e) {
throw new ConnectorException(e);
}
}

@Override
public void testConnection() {
// replace when finished
throw new UnsupportedOperationException("Not yet implemented");
}

}
info

For your REST connector to use your custom Browser implementation, you still need to extend the RestConnector class and override the createBrowser method.

SampleRestConnector code
public class SampleRestConnector extends RestConnector {

@Override
public Browser createBrowser(BrowseContext context) {
return new SampleRestBrowser(context);
}

}