ABCDEFGHIJ
1
Lens APIs Response Contract
2
3
Guidelines
4
Lens REST API Response format is heavily inspired from Google’s Json Style Guide [1] to build RPC based and REST based APIs.

Some highlights from guidelines:
Reserved JavaScript Keywords shall be avoided in request/response json.
If a property is optional or has an empty or null value, consider dropping the property from the JSON, unless there's a strong semantic reason for its existence.
5
[1] Google Json Style Guide
6
[2] Reserved JavaScript Keywords
7
8
Response Format of Lens APIs
9
{
"apiVersion": "",
"id": "",
"data": {},
"error": {
"code": <number>,
"message": "",
"stackTrace": "",
"payLoad": {},
"childErrors": []
}
}
10
11
FieldDescriptionRequired/Optional
12
apiVersionRepresents the API version used in responding to the requestRequired
13
idA server supplied identifier for the response (regardless of whether the response is a success or an error). This is useful for correlating server logs with individual responses received at a client.Required
14
dataContainer for all the data from a response. A JSON response should contain either a data object or an error object, but not both. If both data and error are present, the error object takes precedence.Optional
15
errorIndicates that an error has occurred, with details about the error. The error format supports either one or more errors returned from the service. A JSON response should contain either a data object or an error object, but not both.Optional
16
error.codeRepresents the code for this error.Required
17
error.messageA human readable message providing more details about the error.Required
18
error.stackTraceException stack trace.Optional
19
error.payLoadContainer for all the data which can be used to elaborate error response.Optional
20
error.childErrorsA collection of errors which have lead to the parent error. In a multithreaded application, main thread might collect errors from all background threads and return all of them as child errors of parent error. Every childError instance shall itself have the same structure as the parent error.Optional
21
22
How to identify whether a request has succeeded or has encountered an error ?
23
There are two ways to identify whether a request has succeeded or has encountered an error :
(1) All 2XX class of Http Status Codes indicates success and remaining all codes indicates that request has encountered an error.
(2) Absence of error property at the top level in response body indicates success and presence of the same property indicates that request has encountered an error.

(1) and (2) will always be in sync. E.g.: It would never happen that Http Status code is 2XX and response body has error property in it.
24
25
API Internal NameAPI URI
HTTP Method
ResultCurrent HTTP Status CodeNew HTTP Status CodeCurrent ResponseNew ResponseNew Response (XML)
26
openSession/session
POST
parameters passed: username, passwd,
sessionconf, database
Success: Session Created200201{
"secretId": "...",
"publicId": "..."
}
{
"apiVersion": "...",
"id": "...",
"data": {
"secretId": "...",
"publicId": "..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<data>
<publicId>...</publicId>
<secretId>...</secretId>
</data>
<id>...</id>
</root>
27
Error: Null, empty or blank Username400400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "No username provided"
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>No username provided</message>
</error>
<id>...</id>
</root>
28
Error: Authentication Failed401401Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Invalid Username or password."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Invalid Username or password.</message>
</error>
<id>...</id>
</root>
29
Error: HiveException/LensException500500Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": 1001,
"message": "Internal Server Error."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>1001</code>
<message>Internal Server Error.</message>
</error>
<id>...</id>
</root>
30
Error: Provided database name does not exist.404400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Could not set current database for the session. Provided database name does not exist."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Could not set current database for the session. Provided database name does not exist.</message>
</error>
<id>...</id>
</root>
31
getLatestDate/metastore/cubes/{cubeName}/latestdate
GET
Success: Returns LatestDate200200{
"date": "..."
}
{
"apiVersion": "...",
"id": "...",
"data": {
"date": "..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<data>
<date>...</date>
</data>
<id>...</id>
</root>
32
Error: Null Session id400400Empty String{
"type": "errorResponse",
"apiVersion": "...",
"id": "...",
"error": {
"code": 2001,
"message": "Session id not provided. Please provide a session id."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2001</code>
<message>Session id not provided. Please provide a session id.</message>
</error>
<id>...</id>
</root>
33
Error: Invalid Cube400400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Provided cube name is not valid."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Provided cube name is not valid.</message>
</error>
<id>...</id>
</root>
34
Error: Invalid Fact in Cube Definition404500Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": 1001,
"message": "Internal Server Error."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>1001</code>
<message>Internal Server Error.</message>
</error>
<id>...</id>
</root>
35
Error: HiveException/LensException500500Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": 1001,
"message": "Internal Server Error."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>1001</code>
<message>Internal Server Error.</message>
</error>
<id>...</id>
</root>
36
cancelQuery/queryapi/queries/{queryHandle}
DELETE

Remarks:
Current response can be improved for this API.
In current state, there are two ways to identify failure. A return false and LensException both represents failure in cancellation in internal code and REST layer responds differently for both of them to client. We can have only one uniform way to represent failure.

Underlying LensDriver interface can also be improved with respect to cancel query. It also has two ways to represent cancellation failure. We can have only one way for representing failure i.e. Exception throughout the code.

public interface LensDriver {
boolean cancelQuery(QueryHandle handle) throws LensException;
}
Success200200{
"message" : "...",
"status" : "SUCCEEDED"
}
{
"apiVersion": "...",
"id": "...",
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<id>...</id>
</root>
37
Error: Null Session id400400Empty String{
"type": "errorResponse",
"apiVersion": "...",
"id": "...",
"error": {
"code": 2001,
"message": "Session id not provided. Please provide a session id."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2001</code>
<message>Session id not provided. Please provide a session id.</message>
</error>
<id>...</id>
</root>
38
Error: Invalid query handle400400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Query does not exist."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Query does not exist.</message>
</error>
<id>...</id>
</root>
39
Error: Driver or internal lens server code returned false200500{
"message" : "...",
"status" : "FAILED"
}
{
"apiVersion": "...",
"id": "...",
"error": {
"code": 1001,
"message": "Internal Server Error."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>1001</code>
<message>Internal Server Error.</message>
</error>
<id>...</id>
</root>
40
Error: LensException500500Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": 1001,
"message": "Internal Server Error."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>1001</code>
<message>Internal Server Error.</message>
</error>
<id>...</id>
</root>
41
Error: Query Execution has already finished200500{
"message" : "...",
"status" : "FAILED"
}
{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Query execution has already finished. It cannot be cancelled."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Query execution has already finished. It cannot be cancelled.</message>
</error>
<id>...</id>
</root>
42
Error: Query does not exist.404404Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Query does not exists."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Query does not exists.</message>
</error>
<id>...</id>
</root>
43
query
ESTIMATE
/queries
POST
parameters passed: sessionid, query, operation#ESTIMATE, conf, timeoutmillis, queryname
Success: Query Estimation Successful200200{
"error" : false,
"cost" : {
"estimatedExecTimeMillis" : ...,
"estimatedResourceUsage" : ...
},
"errorMsg" : "..."
}
{
"apiVersion": "...",
"id": "...",
"data": {
"estimatedExecTimeMillis": "...",
"estimatedResourceUsage": "..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<data>
<estimatedExecTimeMillis>...</estimatedExecTimeMillis>
<estimatedResourceUsage>...</estimatedResourceUsage>
</data>
<id>...</id>
</root>
44
Error: Null Session id400400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": 2001,
"message": "Session id not provided. Please provide a session id.",
"stackTrace": "..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2001</code>
<message>Session id not provided. Please provide a session id.</message>
<stackTrace>...<stackTrace>
</error>
<id>...</id>
</root>
45
Error: Submitted query is null or empty or blank400400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": 2002,
"message": "Query is not provided, or it is empty or blank. Please provide a valid query.",
"stackTrace": "..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2002</code>
<message>Query is not provided, or it is empty or blank. Please provide a valid query.</message>
<stackTrace>...<stackTrace>
</error>
<id>...</id>
</root>
46
Error: Invalid Operation Submitted400400Empty String{
  "apiVersion": "...",
  "id": "...",
  "error": {
    "code": "2003",
    "message": "Provided Operation is not supported. Supported Operations are: [estimate, execute, explain, execute_with_timeout]",
    "stackTrace": "...",
    "payload": {
      "type": "supportedQuerySubmitOperations",
      "supportedOperations": [
        "estimate",
        "execute",
        "explain",
        "execute_with_timeout"
      ]
    }
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<lensResponse>
<apiVersion>...</apiVersion>
<id>...</id>
<error>
<code>2003</code>
<message>Provided Operation is not supported. Supported Operations are: [estimate, execute, explain, execute_with_timeout]</message>
<stackTrace>...</stackTrace>
<payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="supportedQuerySubmitOperations">
<supportedOperations>
<operation>estimate</operation>
<operation>execute</operation>
<operation>explain</operation>
<operation>execute_with_timeout</operation>
</supportedOperations>
</payload>
</error>
</lensResponse>
47
Error: LensException200500{
"error" : true,
"cost" : null,
"errorMsg" : "..."
}
{
"apiVersion": "...",
"id": "...",
"error": {
"code": 1001,
"message": "Internal Server Error.",
"stackTrace":"..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>1001</code>
<message>Internal Server Error.</message>
<stackTrace>...<stackTrace>
</error>
<id>...</id>
</root>
48
Error: Exception in Parsing Query200400{
"error" : true,
"cost" : null,
"errorMsg" : "..."
}
{
"apiVersion": "...",
"id": "...",
"error": {
"code": 3001,
"message": "Syntax error: [syntax error msg]",
"stackTrace": "..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>3001</code>
<message>Syntax error: [syntax error msg]</message>
<stackTrace>...<stackTrace>
</error>
<id>...</id>
</root>
49
Error: Fields cannot be queried together.200400{
"error" : true,
"cost" : null,
"errorMsg" : "..."
}
{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Selected fields cannot be queried together. Please remove some fields and try again."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Selected fields cannot be queried together. Please remove some fields and try again.</message>
</error>
<id>...</id>
</root>
50
Error: Some columns cannot be queried in selected time range.200400{
"error" : true,
"cost" : null,
"errorMsg" : "..."
}
{
  "apiVersion": "...",
  "id": "...",
  "error": {
    "code": "3002",
    "message": "{ColumnName} can only be queried after {StartTime} [and before {EndTime}]. Please adjust the selected time range accordingly.",
    "stackTrace": "...",
    "payload": {
      "type": "colUnAvailableInTimeRange",
      "columnName": "...",
      "availableFromInMillisSinceEpoch": "...",
      "availableTillInMillisSinceEpoch": "..."
    }
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<lensResponse>
<apiVersion>...</apiVersion>
<id>...</id>
<error>
<code>3002</code>
<message>{ColumnName} can only be queried after {StartTime} [and before {EndTime}]. Please adjust the selected time range accordingly.</message>
<stackTrace>...</stackTrace>
<payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="colUnAvailableInTimeRange">
<columnName>...</columnName>
<availableFromInMillisSinceEpoch>...</availableFromInMillisSinceEpoch>
<availableTillInMillisSinceEpoch>...</availableTillInMillisSinceEpoch>
</payload>
</error>
</lensResponse>
51
query
EXECUTE
/queries
POST
parameters passed: sessionid, query, operation#EXECUTE, conf, timeoutmillis, queryname
Success: Query accepted for asynchronous execution.200202{
"handleId": "..."
}
{
"apiVersion": "...",
"id": "...",
"data": {
"handleId": "..."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<data>
<queryHandle>
<handleId>...</handleId>
</queryHandle>
</data>
<id>...</id>
</root>
52
Error: Null Session id400401Empty String{
"type": "errorResponse",
"apiVersion": "...",
"id": "...",
"error": {
"code": 2001,
"message": "Session id not provided. Please provide a session id."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2001</code>
<message>Session id not provided. Please provide a session id.</message>
</error>
<id>...</id>
</root>
53
Error: Submitted query is null or empty or blank400400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": 2002,
"message": "Query is not provided, or it is empty or blank. Please provide a valid query."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2002</code>
<message>Query is not provided, or it is empty or blank. Please provide a valid query.</message>
</error>
<id>...</id>
</root>
54
Error: Invalid Operation Submitted400400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": "2003",
"message": "Provided Operation is not supported. Supported Operations are: [estimate, execute, explain, execute_with_timeout]",
"stackTrace": "...",
"payload": {
"type": "supportedQuerySubmitOperations",
"supportedOperations": [
"estimate",
"execute",
"explain",
"execute_with_timeout"
]
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<lensResponse>
<error>
<code>2003</code>
<message>Provided Operation is not supported. Supported Operations are: [estimate, execute, explain, execute_with_timeout]</message>
<stackTrace />
<payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="supportedQuerySubmitOperations">
<supportedOperations>
<operation>estimate</operation>
<operation>execute</operation>
<operation>explain</operation>
<operation>execute_with_timeout</operation>
</supportedOperations>
</payload>
</error>
</lensResponse>
55
getStatus (name shall be refactored to getQuery as the API returns the entire lensQueryResource instead of just returning status)/queries/{queryHandle}
GET
parameters passed: sessionId
Success: Query retrieved successfully200200{
"driverOpHandle": "...",
"queryHandle": {
"handleId": "..."
},
"closedTime": 999,
"driverQuery": "...",
"selectedDriverClassName": "...",
"status": {
"progressMessage": "...",
"progress": 999,
"isResultSetAvailable": false,
"statusMessage": "...",
"errorMessage": "...",
"status": "NEW"
},
"launchTime": 999,
"resultSetPath": "...",
"queryName": "...",
"driverFinishTime": 999,
"driverStartTime": 999,
"isPersistent": false,
"submissionTime": 999,
"queryConf": {
"properties": {
"key": "value"
}
},
"finishTime": 999,
"submittedUser": "...",
"priority": "VERY_HIGH",
"userQuery": "..."
}
{
"apiVersion": "...",
"id": "...",
"data": {
"query": {
"driverOpHandle": "...",
"queryHandle": {
"handleId": "..."
},
"closedTime": 999,
"driverQuery": "...",
"selectedDriverClassName": "...",
"status": {
"progressMessage": "...",
"progress": 999,
"isResultSetAvailable": false,
"statusMessage": "...",
"errorMessage": "...",
"status": "NEW"
},
"launchTime": 999,
"resultSetPath": "...",
"queryName": "...",
"driverFinishTime": 999,
"driverStartTime": 999,
"isPersistent": false,
"submissionTime": 999,
"queryConf": {
"properties": {
"key": "value"
}
},
"finishTime": 999,
"submittedUser": "...",
"priority": "VERY_HIGH",
"userQuery": "..."
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<data>
<query>
<closedTime>999</closedTime>
<driverFinishTime>999</driverFinishTime>
<driverOpHandle>...</driverOpHandle>
<driverQuery>...</driverQuery>
<driverStartTime>999</driverStartTime>
<finishTime>999</finishTime>
<isPersistent>false</isPersistent>
<launchTime>999</launchTime>
<priority>VERY_HIGH</priority>
<queryConf>
<properties>
<key>value</key>
</properties>
</queryConf>
<queryHandle>
<handleId>...</handleId>
</queryHandle>
<queryName>...</queryName>
<resultSetPath>...</resultSetPath>
<selectedDriverClassName>...</selectedDriverClassName>
<status>
<errorMessage>...</errorMessage>
<isResultSetAvailable>false</isResultSetAvailable>
<progress>999</progress>
<progressMessage>...</progressMessage>
<status>NEW</status>
<statusMessage>...</statusMessage>
</status>
<submissionTime>999</submissionTime>
<submittedUser>...</submittedUser>
<userQuery>...</userQuery>
</query>
</data>
<id>...</id>
</root>
56
Error: Null Session id400400Empty String{
"type": "errorResponse",
"apiVersion": "...",
"id": "...",
"error": {
"code": 2001,
"message": "Session id not provided. Please provide a session id."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2001</code>
<message>Session id not provided. Please provide a session id.</message>
</error>
<id>...</id>
</root>
57
Error: Query does not exist.404404Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Query does not exists."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Query does not exists.</message>
</error>
<id>...</id>
</root>
58
setDatabase/metastore/databases/current
PUT
parameter passed: session id, dbName

Remarks: I might be wrong but in my opinion this API does not belong to /metastore and it shall not use PUT HTTP method. This API is updating current db of a session resource referenced by session id, so it shall be a part of /session. Also it is doing a partial update of a resource. For partial update we shall use POST or PATCH. In PUT a client shall pass a complete resource entity to be replaced by server. We could have had an API as follows:

/session/{sessionId}
POST | PATCH
Request Body:
{
"data": {
"operations": [
{
"op": "replace",
"path": "/currentDatabase",
"value": "<dbName>"
}
]
}
}
Success: current database set for the session200200{
"message": "...",
"status": "SUCCEEDED"
}
{
"apiVersion": "...",
"id": "...",
}

<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<id>...</id>
</root>
59
Error: Null Session id400400Empty String{
"type": "errorResponse",
"apiVersion": "...",
"id": "...",
"error": {
"code": 2001,
"message": "Session id not provided. Please provide a session id."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>2001</code>
<message>Session id not provided. Please provide a session id.</message>
</error>
<id>...</id>
</root>
60
Error: Provided database name does not exist.404400Empty String{
"apiVersion": "...",
"id": "...",
"error": {
"code": TBD,
"message": "Could not set current database for the session. Provided database name does not exist."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>TBD</code>
<message>Could not set current database for the session. Provided database name does not exist.</message>
</error>
<id>...</id>
</root>
61
Error: HiveException/LensException200500{
"message": "...",
"status": "FAILED"
}
{
"apiVersion": "...",
"id": "...",
"error": {
"code": 1001,
"message": "Internal Server Error."
}
}
<?xml version="1.0" encoding="UTF-8"?>
<root>
<apiVersion>...</apiVersion>
<error>
<code>1001</code>
<message>Internal Server Error.</message>
</error>
<id>...</id>
</root>
62