Integration View filters

Overview

It is often the case that two different external systems need substantially similar views of the same data but not exactly the same view. This case could arise when two different downstream systems both need policy information but might also need different levels of detail about coverage options or transaction data. The case could also arise when two different client systems want to display a different subset of the data to their end users. Developers putting together an integration message or building a REST API have to decide if they want to create a single schema that represents the superset of data that all such systems might want. In the alternative, developers have to decide if they must create separate, more targeted schemas that only contain exactly what a particular system or use case requires. Having a single, shared schema reduces implementation and maintenance costs but comes at the cost of fetching, processing, and serializing out data that a given client will simply ignore.

The Integration Views feature attempts to meet these competing needs by allowing you to apply a filter to a schema and mapping file. The filter serves as a whitelist of the properties that must be included for a given invocation of a mapping file. Properties that are not included as part of a given filter never have their associated path or predicate expressions executed. These properties will not end up in the resulting TransformResult object at all. Not including unnecessary properties saves the cost of fetching, processing, and serializing the associated data.

Filters can also be used to create a stable view on top of a given schema. By whitelisting the properties desired, newly added properties will never show up unless the filter is explicitly changed.

Syntax

Filters make use of a simplified version of GraphQL syntax to describe the data to be fetched. See https://graphql.org and http://facebook.github.io/graphql/October2016 for more details about the specification. The queries all have an implicit entry point based on their invocation and do not support arguments or mutations. They simply look like a JSON object with keys but no values.

For example, suppose we have a JSON schema that minimally defines activities and notes:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "definitions" : {
    "Activity" : {
      "type" : "object",
      "properties" : {
        "assignedUser" : {
          "type" : "string"
        },
        "assignmentDate" : {
          "type" : "string",
          "format" : "date-time"
        },
        "assignmentStatus" : {
          "type" : "string",
          "x-gw-type" : "typekey.AssignmentStatus"
        },
        "createUser" : {
          "type" : "string"
        },
        "notes": {
          "type": "array",
          "items" : {
            "$ref" : "note#/definitions/Note"
          }
        },
        "subject" : {
          "type" : "string"
        }
      }
    },
    "Note" : {
      "type" : "object",
      "properties" : {
        "created": {
          "type": "string",
          "format" : "date"
        },
        "subject": {
          "type": "string"
        },
        "body": {
          "type": "string"
        },
        "topic": {
          "type": "string",
          "x-gw-type" : "typekey.NoteTopicType"
        }
      }
    }
  }
}

The following GraphQL query will filter the resulting data, retrieving just the subject and notes for the Activity but ignoring the other properties. The query retrieves just the subject, body, and topic from the Note.

{
  subject
  notes {
    subject
    body
    topic
  }
}
Objects and object array references

As illustrated in the previous example, referencing an object or object array in a GraphQL query requires that you specify the fields on the subobject you would like to retrieve.

Fragments
Fragments are a GraphQL feature that allows you to query fragments that can be applied to a given object. Fragments are useful in cases where the same subobject type appears within multiple places in the data you want to fetch. For example, a Contact may have both primaryAddress and allAddresses properties that map to the same Address type. Contacts may also appear in multiple places within a given schema graph. Fragments give you a way to define the set of properties you want from a Contact or Address once, as a fragment. You can then reference that fragment in each context that you want the same set of properties. The previous example with activities and notes can be rewritten such that the note fields are defined as a fragment:
{  
  subject
  notes {
    ...noteParts
  }
}

fragment noteParts on Note {
  subject
  body
  topic
}
Differences from GraphQL proper
Our filter syntax leverages the basics of GraphQL for the syntax of basic field selection, nested objects, arrays, and fragments. However, the syntax differs from GraphQL proper in some important ways:
  • No explicit query operation exists. The query operation is always implied.
  • No mutations are supported.
  • None of our fields accept arguments.
  • Fields cannot be aliased to change the name of the result property.
  • Our GraphQL syntax allows for using the original unicode name of a schema property rather than restricting everything to a limited ASCII subset.

Storage

Filter text is stored in files under config/integration/filters with names of the form <name>-<version>.gql. As with JSON schema files, integration mapping files, and Swagger schemas; the fully-qualified name of the filter is formed first based on the package of the file indicated by the file subfolder. Then, the fully-qualified filter name relies upon the name and version of the file. For example, if the above query was in the file config/integration/filters/gw/pl/activities/activity_basics-1.0.gql, the fully-qualified name of the filter would be gw.pl.activities.activity_basics-1.0.

Usage

In order to use a given filter, you specify it on the JsonMappingOptions that you can optionally pass to a JsonMapper during invocation:
var activity : Activity
var mapper = JsonConfigAccess.getMapper("gw.pl.activities.activity-1.0", "Activity")
var transformResult = mapper.transformObject(activity, new JsonMappingOptions().withFilter("gw.pl.activities.activity_basics-1.0"))