Skip to main content

Data Filters Compilation API

Creating data filters from Rego policies in Enterprise OPA uses an expanded version of the Open Policy Agent /v1/compile API1.

Generate Filters

POST /v1/compile
Content-Type: application/json
Accept: ...

Request Headers

NameRequiredAccepted ValuesDescription
Content-TypeYesapplication/json

application/x-yaml
Indicates the request body is either a JSON or YAML encoded document.
Content-EncodingNogzipIndicates the request body is a compressed gzip object.
AcceptYesSee belowSee below

Accept Header – Controlling the Target Response Format

The same request can generate filters that are representable in many different ways, such as raw SQL WHERE clauses or Universal Conditions AST.

Enterprise OPA uses the Accept header to denote the target response format.

ValueResponse SchemaDescription
Rego AST: application/jsonresult.queries[]The default behavior of Open Policy Agent. Response format is JSON in Rego AST
Multitarget: application/vnd.styra.multitarget+jsonresult.{ucast,sqlserver,mysql,postgresql}The partially evaluated result of the query in each target dialect. Use the options.targetDialects field in the request body to control targets.
UCAST: application/vnd.styra.ucast.all+json, application/vnd.styra.ucast.minimal+json, application/vnd.styra.ucast.linq+json, application/vnd.styra.ucast.prisma+jsonresult.queryUCAST JSON object describing the conditions under which the query is true.
SQL: application/vnd.styra.sql.sqlserver+json, application/vnd.styra.sql.mysql+json, application/vnd.styra.sql.postgresql+jsonresult.queryString representing the SQL equivalent of the conditions under which the query is true.

Request Body

FieldTypeRequiredDescription
querystringYesThe query to partially evaluate and compile. e.g., data.filters.include
inputanyNoThe input document to use during partial evaluation (default: undefined).
optionsobject[string, any]NoAdditional options to use during partial evaluation
options.disableInliningarray[string]No. Default: undefinedA list of rule references
options.nondeterminsticBuiltinsboolNo. Default: false. If using any target other than Rego AST, it is always trueWhen false, non-deterministic built-in functions are kept in the PE results, without being partially evaluated.
options.targetDialectsarray[string], one of ucast+all, ucast+minimal, ucast+prisma, ucast+linq, sql+sqlserver, sql+mysql, sql+postgresqlYes, if using multitarget. Ignored for all other targetsThe output targets for partial evaluation. Different targets will have different constraints. Use Accept header to request a single compilation target.
options.targetSQLTableMappingsobject[string, object[string, string]]NoA mapping between tables and columns. See the example for the schema.
unknownsarray[string]NoThe terms to treat as unknown during partial evaluation (default: ["input"]]).

Query Parameters

See the Open Policy Agent /v1/compile API1

SQL Examples

In this support ticket application example, the list of tickets a user can see is determined by their role.

Roles are provided to Enterprise OPA via roles/data.json, and the SQL tables contain tickets and assignees.

package filters

import rego.v1

# Include in results if
# User has the "admin" role.
include if {
"admin" in data.roles[input.user]
}

# Include in results if
# User has the "reader" role, and the ticket is assigned to them.
include if {
"reader" in data.roles[input.user]

# Name field on Users table == the current user.
# input.<table>.<column>
input.users.name == input.user
}

# Include in results if
# User has the "resolver" role, and the ticket is assigned to them.
include if {
"resolver" in data.roles[input.user]

# ticket is assigned to user
input.users.name == input.user
}

# Include in results if
# User has the "resolver" role, and the ticket is unassigned and unresolved.
include if {
"resolver" in data.roles[input.user]

# ticket is unassigned and unresolved
input.tickets.assignee == null
input.tickets.resolved == false
}

Admin

As an Admin, I can view every ticket.

No filter means show all

Note that the filter returned is empty, which implies "show all". In UCAST this is represented with a {}, and with SQL strings, this is "".

Request
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.ucast.all+json

{
"query": "data.filters.include",
"input": {
"user": "alice",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
]
}
Response
HTTP/1.1 200 OK
Content-Type: application/json

{
"result": {
"query": {}
}
}

Reader

As a Reader, I can only see tickets that I am assigned to.

Request
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.ucast.prisma+json

{
"query": "data.filters.include",
"input": {
"user": "bob",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
]
}
Response
HTTP/1.1 200 OK
Content-Type: application/json

{
"result": {
"query": {
"type": "field",
"operator": "eq",
"field": "users.name",
"value": "bob"
}
}
}

Resolver (using Multitarget)

As a Resolver, I can see either tickets I'm assigned to, or tickets that are unassigned and unresolved

Request
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.multitarget+json

{
"query": "data.filters.include",
"input": {
"user": "ceasar",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
],
"options": {
"targetDialects": [
"ucast+prisma",
"sql+postgres"
]
}
}
Response
HTTP/1.1 200 OK
Content-Type: application/json

{
"result": {
"ucast": {
"query": {
"type": "compound",
"operator": "or",
"value": [
{
"type": "field",
"operator": "eq",
"field": "users.name",
"value": "ceasar"
},
{
"type": "compound",
"operator": "and",
"value": [
{
"type": "field",
"operator": "eq",
"field": "tickets.assignee",
"value": null
},
{
"type": "field",
"operator": "eq",
"field": "tickets.resolved",
"value": false
}
]
}
]
}
},
"postgres": {
"query": "WHERE (users.name = E'ceasar' OR (tickets.assignee IS NULL AND tickets.resolved = FALSE))"
}
}
}

Using Table and Column Remappings

In this example, we'll imagine the same scenario as above but the SQL schema is as follows:

The tickets table is now tbl_t and the users table is now tbl_u, and the assignee joined field is now named assignedto.

Request
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.multitarget+json

{
"query": "data.filters.include",
"input": {
"user": "ceasar",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
],
"options": {
"targetDialects": [
"ucast+prisma",
"sql+postgres"
],
"targetSQLTableMappings": {
"postgres": {
"users": {
"$self": "tbl_u"
},
"tickets": {
"$self": "tbl_t",
"assignee": "assignedto"
},
"ucast": {
"users": {
"$self": "tbl_u"
},
"tickets": {
"$self": "tbl_t",
"assignee": "assignedto"
}
}
}
}
}
Response
HTTP/1.1 200 OK
Content-Type: application/json

{
"result": {
"ucast": {
"query": {
"type": "compound",
"operator": "or",
"value": [
{
"type": "field",
"operator": "eq",
"field": "tbl_u.name",
"value": "ceasar"
},
{
"type": "compound",
"operator": "and",
"value": [
{
"type": "field",
"operator": "eq",
"field": "tbl_t.assignedto",
"value": null
},
{
"type": "field",
"operator": "eq",
"field": "tbl_t.resolved",
"value": false
}
]
}
]
}
},
"postgres": {
"query": "WHERE (tbl_u.name = E'ceasar' OR (tbl_t.assignedto IS NULL AND tbl_t.resolved = FALSE))"
}
}
}

External Reference

  1. Open Policy Agent Compile API
  2. Universal Conditions AST Syntax