Internal platform changes that are not visible to capsule developers will be communicated via internal status updates.
You must now explicitly declare and access actions as endpoints. You can make your actions accessible as local, remote, and client endpoints.
You create action endpoints through an endpoints.bxb
file that defines the action, including the inputs the action will accept and the endpoints that the action will call.
Here is an example endpoints.bxb
for a test capsule that allows users to search for shoes:
endpoints {
authorization {
none
}
action-endpoints {
action-endpoint (FindShoe) {
accepted-inputs (name, type, minPrice, maxPrice)
local-endpoint (FindShoe.js)
}
}
}
The corresponding FindShoe.js
then includes the external action implementation that returns values back to the capsule:
exports.tests = []
var SHOES = require('./lib/shoes')
exports.function = function (name, type, minPrice, maxPrice) {
var result = SHOES.filter(function (shoe) {
return (
(!name || shoe.name.toUpperCase() == name.toUpperCase()) &&
(!type || shoe.type.toUpperCase() == type.toUpperCase()) &&
(!maxPrice || shoe.price.value <= maxPrice.value) &&
(!minPrice || shoe.price.value >= minPrice.value)
)
})
return result
}
To learn more about endpoints, read Configuring Endpoints.
In addition to natural-order
sorting, you can now sort action results using the sort-orderings
, which gives you more control over results that are presented. You can define sort ordering within structures and then use them in actions, or your actions can define specific sort ordering.
Here are examples of how you can define sorting within a structure and an action, respectively:
structure {
...
sort-orderings {
default-ordering (EarliestArrival) {
sorting (somePropertyExprOrBindingPath) {
by (OtherOrderingOfSomePropertyExprStaticType)
}
sorting (somePropertyExprOrBindingPath) {
by (AnotherOrderingOfSomePropertyExprStaticType)
inverted
}
// default order used
sorting (somePropertyExprOrBindingPath)
// inverted default order used
sorting (somePropertyExprOrBindingPath) {
inverted
}
}
ordering (LatestArrival) {
...
}
...
}
}
action (MyAction) {
type (Search)
collect {
input (mySort) {
type (MySortEnum)
}
}
...
output (MyOutputType) {
...
sort {
switch (mySort) {
case (PriceAsc) {
each (r) {
sorting (r.price) {
by (Desc)
inverted
}
// compound in case of a tie
ranking (-1 * r.price)
}
}
case (FooBarBaz) {
each (r) {
sorting (r) {
by (SomeOtherCriteriaDefinedInFlightStatus)
}
}
}
default {
each (r) {
sorting (r) // use natural order of r
}
}
}
}
}
}
To learn more, refer to sort-orderings (structure) and sort (action) within the Reference Guide.
You now have a limit of 2,000 training examples for your capsule. You also are limited to 50,000 vocabulary entries per type.
You are now limited to 512 symbols for each enum. You also cannot extend an enum or add a symbol
when you add a concept that is a role-of
an enum.
You can now ensure that two primitive types never merge into a single value using the new equivalence
child key, never-equivalent
.
integer (calculator.Number) {
equivalence {
never-equivalent
}
We have standardized using intent
within the default-init
key (previously the default-value
key). As a result, the value
key has been deprecated within default-init
.
Old:
input (test) {
...
default-init {
value {MyType(...)}
}
}
New:
input (test) {
...
default-init {
intent {goal {MyType} value {MyType(...)}
}
}
To better reflect how the key works, we've renamed the value-template
key within actions to evaluate
.
Old:
output (test) {
value-template {
...
}
}
New:
output (test) {
evaluate {
...
}
}
To simplify creating layouts, we've removed the <header>
element for all layout types and have now consolidated all layout content into the <content>
element, which is now interchangeable with <header>
.
Old:
<layout type="details">
<match>
Recipe (this)
</match>
<header>
<div class="photo" style="background-image: url({{#try}}{{thumbnail.url}}{{/try}})"></div>
<layout-macro id="title-area">
<title>{{name}}</title>
</layout-macro>
</header>
<content>
<div class="details">
<div class="info-row">
<div>
<h5>Serves</h5>
<span>{{#if exists(servings)}}{{ servings }}{{else}}—{{/if}}</span>
</div>
...
New:
<layout type="details">
<match>
Recipe (this)
</match>
<content>
<div class="photo" style="background-image: url({{#try}}{{thumbnail.url}}{{/try}})"></div>
<layout-macro id="title-area">
<title>{{name}}</title>
</layout-macro>
<div class="details">
<div class="info-row">
<div>
<h5>Serves</h5>
<span>{{#if exists(servings)}}{{ servings }}{{else}}—{{/if}}</span>
</div>
<div>
...
To enforce cardinality constraints on input groups, the input-group
key must now include a requires
child key.
Example:
collect {
input (shape) {
type (ShapeType)
min(Required) max (One)
}
input-group(atLeastOne) {
requires (OneOrMoreOf)
collect {
input (line1) {
type (Line1)
min(Optional) max (One)
validate {
if (exists(line1) && line1 <= 0) {
halt {
dialog {
template("Need a positive length.")
}
}
}
}
}
...
We've deprecated support for having an open-match
declaration within name
primitives. Previously, open-match
was true
by default and was used to restrict the matching of words not present in a vocabulary file. We instead recommend better training and vocabulary data.
Old:
name (SubLocalityOneName) {
extends (AdministrativeDivisionName)
open-match (false)
}