Skip to content

Building CI Responses

dotasek edited this page Apr 13, 2018 · 4 revisions

This page describes how developers creating Cytoscape Automation Functions can generate valid CI Response data. The CI Response model is a data standard shared by multiple Cytoscape Cyber-infrastructure services, such as Diffusion and CyNDEx, and is described in greater detail in the Cytoscape Function Best Practices wiki page for App Developers.

There are a number of ways to build a CI Response from within an App. If you are adding Automation using Functions, you will have a great deal of flexibility in how to produce your data, from streaming, to automatic mapping via JAX-RS, to manual construction using strings. Familiarizing yourself with all these options will allow you to choose a method that requires less coding and is more appropriate to the automation function you are building.

Note: App Maven Dependencies

The ci-api Cytoscape models and utilities are very useful in creating CI Responses and errors, and are used throughout the following code. They remove boilerplate code and are reduce errors. They are provided in the Cytoscape API. To use these models, you can use the following code snippet in the dependencies section of your pom.xml file:

<dependency>
  <groupId>org.cytoscape</groupId>
  <artifactId>ci-api</artifactId>
  <version>3.6.0</version>
  <scope>provided</scope>
</dependency>

The Diffusion App makes extensive use of these models and is a good example of their intended use.

Returning a CIResponse by extending the CIResponse class

Create a CIResponse instance whose type variable T is a class containing the operation's return value. For example, to return a class called DiffusionResultColumns containing the operation result, you should define the DiffusionResultColumns class and return it:

public static class DiffusionAppResponse extends CIResponse<DiffusionResultColumns>{
}
...
DiffusionAppResponse myAppFunction(...) {
    ...
    DiffusionAppResponse ci = new DiffusionAppResponse();
    ci.data = new DiffusionResultColumns(...);
    ...
    return ci;
}

The errors field should remain unset.

If an operation returns a CIResponse<T> class, CyREST will convert the class to JSON before returning the result to the caller.

Returning a CIResponse by manually building a response String

While we recommend that an operation return a result as a CIResponse class, it is equally acceptable to return it as a JSON string. This can be used in instances where a the app developer wishes to manually create JSON, through a library such as GSON, or Jackson, or by other means. An example of this process is shown in the code snippet below:

public static class MyAppResponse extends CIResponse<MyResponseModel>{
}

...

@ApiOperation(value = "My custom JSON Operation",
    notes = "A prose description of what my JSON Operation Does",
    response = MyAppResponse.class)
@Produces(MediaType.APPLICATION_JSON)
public String myAppFunction(...) {
    ...
    String myJSONData;
    ...
    // Build your JSON.
    ...
    return "{ \"data\":{" + myJSONData + "}, \"errors\":[]}";
}

Note that we included an @APIOperation annotation for myAppFunction. This follows Swagger Best Practices by providing our Swagger documentation with MyAppResponse.class, a class that provides the data model for myJSONData. Without this annotation, there is no way for Swagger or any other documentation system to convey what myJSONData could contain.

Returning CIResponse in by writing to a Stream

When an operation return result can grow large, we recommend that the operation return a streamed CIResponse instead of a CIResponse class or String. In such an instance, we can use a combination of JAX-RS's StreamingOutput and Response classes to produce a valid CIResponse. An example of this process is shown in the code snippet below:

public static class MyAppResponse extends CIResponse<MyResponseModel>{
}

...

@ApiOperation(value = "My custom JSON Operation",
    notes = "A prose description of what my JSON Operation Does",
    response = MyAppResponse.class)
@Produces(MediaType.APPLICATION_JSON)
public Response myAppFunction() {
  StreamingOutput stream = new StreamingOutput() {
    @Override
    public void write(OutputStream os) throws IOException,
    WebApplicationException {
      Writer writer = new BufferedWriter(new OutputStreamWriter(os));
      writer.write("{ \"data\":{");
      ...
      // Add your JSON
      ...
      writer.write("}, \"errors\":[]}");
      writer.flush();  
    }
  };
  return Response.ok(stream).build();
}

Note that we included an @APIOperation annotation for myAppFunction. See the previous example, Returning a CIResponse by manually building a response String for a detailed explanation of why this is done.