-
Notifications
You must be signed in to change notification settings - Fork 112
[Proposal] Optional Inputs and Errors
It is important to formally describe the current behavior before defining enhancements.
- When input to a table's URI are missing (such as required parameters), the script fails and none of the subsequnt statements are executed. The client receives an error with a
400
response code. - When a variable has no value, variable substitution fills an empty value. Script execution continues and dependent statements fail or succeed based on how the resource is implemented.
- Route matching is absolute - i.e., all path/query parameters in the URI path must match the route's path. If not, the client would get a
404
. This forces apps to create duplicate routes.
These are the classes of errors we need to deal with.
- Network errors
- HTTP error codes
- Errors in the body with a success HTTP response code.
- Both errors and success in the same response with an HTTP response code.
Right now, the engine can deal with the first two conditions. In these two cases, the script execution is aborted.
- Be able to declare required and optional inputs on routes.
- Be able to declare that a given statement must not execute when an parameter is missing.
- Be able to declare that a given statement must exectute only certain variables are defined.
- Be able to declare that a given statement must execute only when a given boolean condition is true.
- Be able to extract errors from the body.
Apps can extract extract from the body mapping the result set to the root, and then running multiple selects to get errors and success separately. However, this does not work when the table is generic.
- Be able to execute an alternative path in case of error (or more generally, when a given condition is true)
The approach consists of several backwards compatible changes to the language.
The first step is to borrow the syntax that we currently use for URI templates of tables. These templates have the notion of required tokens which are preceded with ^
s. When applied to routes, an example might look like the following:
return ... via route '/p/a/t/h?p1={^v1}&p2={^v2}&p3={v3}
with optional params
using defaults 'p2' = 'some default'
In this example, parameters p1
and p2
are required while p3
is optional. This examples use two new clauses:
- The clause
with optional params
triggers optional parameter matching. This is done for backwards compat sake. When this clause is present, route matching will be limited to required tokens only. - Parameters in the route path can be adorned with
^
to denote that the parameter is required. - Any unlisted params in the route will continue to be accessible to the script.
The second step is to state variables as required. When statement has one or more variables as required, but if they are undefined or null, then the statement will not be executed. Here is an example.
var1 = select id from ids;
var2 = select * from details where id in ("{^var1}");
var3 = select * from someother where id in ("{var1}");
In case of a failure with the first statement, the second statement will not be executed, while the third will be. The same flow works for variables extracted from requests (URI parameters, headers, or body).
TODO - need to experiment whether route params can trigger different styles of extraction.
The logical or assignment allows conditional execution of a statement. Here are some examples.
-- Execute a fallback in case of failure. Here when the first select fails, the second
-- select takes over.
var1 = select * from table
var2 = var1 || select * from cached.table
The !
operator lets you specify preconditions on statemetns.
-- Execute a statement only when certain inputs meet a certain condition.
-- The UDF func returns true when inputs match some conditions
f = require('./myudfs.js');
var3 = !f.func("{param1}", "{param2}") || select * from some conditioally.exec.this.table
-- This statement runs only if `paramA` is defined
!"{paramA}" || select * from foo ...