API Server
Classes
- EventController
This class acts a controller for all things related to events
- HistoryController
This class acts a controller for all things related to history
- PendingUserController
Table projectId | Issuer | emailOrName | PermissionId | Context
- BigQueryQueryer
This is the connection/query engine owner for a big query json layout
- JsonQueryer
This is the connection/query engine owner for a big query json layout
- Models
This is a subtle wrapper around sequelize to create and sync the database.
Members
- systemId
Returns the project id if the data is loaded
- projectId
Returns the project id if the data is loaded
Constants
- columnTest
This is a regex test that detects if the input string matches the format to reference a column in an aggregate function
- DEFAULT_RAW_OPERATORS :
Object
the default allowed raw operators and how to translate them in sequelize
- DEFAULT_DEFAULT_TYPE :
String
the default type to cast to when all else fails
Functions
- create(options)
Returns an object containing named controllers. This controllers will be installed into the context and will be available for use to access/change data. To use these, there are two steps. The first it to invoke this method. The results should be merged into the context object. The second step it to call init() with the result and the context to 'start' the controllers.
- init(cxt, controllers)
This will call init(cxt) on all the controllers in the controllers object
- resolveTemplate()
Returns a Template object. idOrTemp can be either an id, a Template object, or a { id }
- buildStruct(attrTree) ⇒
This function builds a tree out of a list of requested attributes
- buildStruct(attrTree) ⇒
This function builds a tree out of a list of requested attributes
- getServiceConfig()
returns the firebase definition to be sent to a service
- simpleLiteralFunctionBuilder(literal) ⇒
This function is a builder for an arithmetic operation handler designed to be plugged back through the json query util
- bucketTimeArgs(args) ⇒
- BUCKET_TIME(args) ⇒
This is an alias that will look like a single sql function, but that actually consists of a set of operations that transform time fields into discrete buckets that can then be grouped/aggregated by bucket/window
- getSqlFromFindAll(the, the) ⇒
Promise.<string>
a function to get a find all query from a model and some options https://github.com/sequelize/sequelize/issues/2325 <- screw this guy
- prepareOptions()
taken from sequelize
- buildStruct(attrTree) ⇒
This function builds a tree out of a list of requested attributes
- getChildrenData()
Returns the child objects in the table. Currently, this will return all the objects in the list in a object that looks like: { startIndex : 0, count :
, items : [ obj1...objN ] } - removeAll()
This will call delete on all children in the table. If the accumBuffer is not null, the update to the db is not performed here, but from the invoker.
- getData()
Retrieves the data object form this DataItem, sets it as the current data, and returns it as the result in the callback
- delete()
Deletes the data item and all its children. If the accumBuffer is not defined, than this is the root of the delete. All children should be removed, as well as all references. If accumBuffer is present, it's keys should be updated with the new firebase value (normally null). All changes will be pushed at once.
If this class implements a deleteResources( accumBuffer, cb ) method, it will be invoked to allow for the cleanup of any external resources.
- changeNetwork()
Changes the networkId of the device. This will remove aliases from the current network and call join/leaveNetwork as needed
- changeSimNetwork()
Changes the simNetworkId of the device. This will remove aliases from the current network and call join/leaveNetwork as needed
- getMessageRouteForDevice()
This will return a Promise with the 'routes' part of a MessageRoute.
- getId()
Returns the id of device with the given network, aliasKey and alias
- getNetworkAliases()
Looks up the networkAliases on the object with the given id
- getAlias()
Looks up the specific networkAlias on the object with the given id
- addAccount()
Accounts are Users that were made by the project. They are deleted when the project is deleted, and the user's username is unique in the project.
- acquireProjectAlias()
Attempts to acquire the aliasName for the project. The aliasType determines the kind of alias (like a 'blueprint' alias), the aliasName is the desired human readable name, and the id is the actual if of the referenced object.
The lock will be saved like this: -projectAlias -projectId -aliasType -aliasName : id
- removeRef()
Removes the ref between the owner and the reference object.
- getRef()
Returns the value stored at the ownerId's refId. This is the last value pasted into addRef for the given ownerId/refId
- getRefSubValue()
Returns the value stored at the ownerId's refId[subpath]. This is the last value passed into addRef for the given ownerId/refId
- getRefsData()
Returns the ref objects in the table. Currently, this will return all the objects in the list in a object that looks like: { startIndex : 0, count :
, items : [ obj1...objN ] }. Each objN will contain: { id : , type : , value : } - getOwnerIds()
Returns the owner ids of the owners that include the ref. Currently, this will return all the objects in the list in a object that looks like: { startIndex : 0, count :
, items : [ ownerId1...ownerIdN ] }. - saveWithManualId(rootRef, idFunc(, data, cb)
This will attempt to create a rootRef/
entry where the id is created by idFunc. This allows human friendly ideas to be created. - deleteResources()
Remove the project alias if it exists. Note, the check of the validity of the alias is performed in the controller
- delete()
This assumes the user has been deleted
- encodeOpenIdUser()
Encodes the iss and sub into a string useable as a firebase key
- keyForOpenIdUser(iss, sub)
Returns the firebase key for the openid
- openIdUserExists(iss, sub) ⇒
boolean
|string
Returns the user if for the iss/sub combination, or false if it doesn't exist
- getOpenIdUserForJwt(jwt)
returns the User data or null if they don't exist
- createOpenIdUser(iss, sub, userInfo, returnExistingUser)
This will create and return the user for the
- deleteOpenIdUser()
This assumes the user has been deleted
- create()
This will create a Models object and starts it. If options.createModels is null, this will use the Definitions.createModels by default.
- convertToData()
Converts the object into the form: [ { path :
, value : }, ...] The value forms of obj are: 1) { values : [ { path : , value : }, ...] } 2) [ { path : , value : }, ...] 3) { path : , value: } 4) { keyValue : { : , : }} - setDeviceData()
Sets the realtime data for the device. The data should be one of these formats: 1) { values : [ { path :
, value : }, ...] } 2) [ { path : , value : }, ...] 3) { path : , value: } 4) { keyValue : { : , : }} - addDeviceUser(device, userData)
Addes a user to the system.
- createCheckNearestPermission(deviceSource, check)
This will return a function(req, res, next) that will check for read permissions on the devicec and walk the device.parentDeviceId field to check to see if the user has permission to read the device from any of those devices
- sendMessage(sendMessage, device, message)
This function will send an outbound message to a device on its network
- flatten()
This will flatten either an patch ops array or an object into { 'a/b/c' : 1 }
- projectTransfer()
Determines if a project transfer is occuring anf performs it. the next function will be invoked with next( err, [ ..ops..] ), where the 'projectId' element in the ops array will be removed.
- appendLookupToResultList(options)
Will look for items to expand and include in the result list. The result object should look like { startIndex, count, total, items, search }. This will append to result, a refs : { XXX : { id : {...} } } object, where XXX is blueprint, network, etc
- createGetAccessToken()
Returns a function that will request an access token from the database in dbInfo. dbInfo should contain { db : the database, service: the service, key : the location to place the accessToken in applyTo }
- allowUpdate()
Returns false if the patch ops array contains a path that is not in the given valid array
- createResult(user, options, authJwt) ⇒
Promise.<Object>
Creates response object for a verification of the previous jwt token
- sendDeviceMessage()
Sends the message using the device's routes model or the defaultTopic
- joinNetwork(cb)
- leaveNetwork(cb)
- aliasSet(cb)
- sendMessage(network, device, msg, cb)
Attempts to send a message to the given device on the given network.
- cleanse() ⇒
Removes any project sensitive data from the network.
- send()
Uses Request to send a message. Unlike request, this will convert any statusCode < 200 or >= 300 to an error
- getTemplateContext()
Used to constuct a template context for use in building the db root. The extra should contain at least systemId and deviceId.
- v4()
v4
- v1()
v1
- encode()
encode
- decode()
decode
- ensureLength()
ensureLength
- padLeft()
padLeft
- trimLeft()
trimLeft
EventController
This class acts a controller for all things related to events
Kind: global class
- EventController
- instance
- .ackEvent(projectId, ack, device) ⇒
Promise
- .initProject(projectId) ⇒
Promise.<array>
- .query(q, context) ⇒
Promise.<object>
- .initQueryUtil(util, models)
- .ackEvent(projectId, ack, device) ⇒
- static
- .queryTransform(model, obj, q) ⇒
string
- .queryTransform(model, obj, q) ⇒
- instance
Promise
eventController.ackEvent(projectId, ack, device) ⇒ This functions attempts to ack an event
Kind: instance method of EventController
Returns: Promise
- resolves after sending the event on the device route
Param | Type | Description |
---|---|---|
projectId | string | the project we are acking an event in |
ack | object | the ack object to send back to the writer |
device | Device | the device object needed to send back to writer |
Promise.<array>
eventController.initProject(projectId) ⇒ this attempts to create the pea table in all of the dbs (sql and bigquery)
Kind: instance method of EventController
Returns: Promise.<array>
- resolves once all of the tables exists or fails if creation fails
Param | Type | Description |
---|---|---|
projectId | string | the id of the project we need to make tables for |
Promise.<object>
eventController.query(q, context) ⇒ Kind: instance method of EventController
Returns: Promise.<object>
- the results of the query
Param | Type | Description | ||
---|---|---|---|---|
q | object | the query object | ||
context | object | the context of the query process | ||
context.deviceId | object | the (potential) device to query | ||
context.systemId | object | the (potential) system to query | ||
context.projectId | object | the project to query | ||
context.mode | object | device | system | project dictates the table to hit |
eventController.initQueryUtil(util, models)
This function inits a query util for either bigquery or cloudsql
Kind: instance method of EventController
Param | Type | Description |
---|---|---|
util | object | the query util we need to define the models in |
models | object | the models object to add the resulting models to |
string
EventController.queryTransform(model, obj, q) ⇒ Kind: static method of EventController
Returns: string
- the edited query string
Param | Type | Description |
---|---|---|
model | Sequelize.Model | A sequelize model that we are querying (some kind of event always) |
model.imaginePrefix | Sequelize.Model | the table prefix |
obj | object | a context object containing the table name |
obj.table | string | the table that we are replacing in |
obj.database | string | the database that we are replacing in |
q | string | the query string that we are transforming |
HistoryController
This class acts a controller for all things related to history
Kind: global class
- HistoryController
- instance
- .query(q, context) ⇒
Promise.<object>
- .initQueryUtil(util, models)
- .query(q, context) ⇒
- static
- .queryTransform(model, obj, q) ⇒
string
- .queryTransform(model, obj, q) ⇒
- instance
Promise.<object>
historyController.query(q, context) ⇒ Kind: instance method of HistoryController
Returns: Promise.<object>
- the results of the query
Param | Type | Description | ||
---|---|---|---|---|
q | object | the query object | ||
context | object | the context of the query process | ||
context.deviceId | object | the (potential) device to query | ||
context.systemId | object | the (potential) system to query | ||
context.projectId | object | the project to query | ||
context.mode | object | device | system | project dictates the table to hit |
historyController.initQueryUtil(util, models)
This function inits a query util for either bigquery or cloudsql
Kind: instance method of HistoryController
Param | Type | Description |
---|---|---|
util | object | the query util we need to define the models in |
models | object | the models object to add the resulting models to |
string
HistoryController.queryTransform(model, obj, q) ⇒ Kind: static method of HistoryController
Returns: string
- the edited query string
Param | Type | Description |
---|---|---|
model | Sequelize.Model | A sequelize model that we are querying (some kind of event always) |
model.imaginePrefix | Sequelize.Model | the table prefix |
obj | object | a context object containing the table name |
obj.table | string | the table that we are replacing in |
obj.database | string | the database that we are replacing in |
q | string | the query string that we are transforming |
PendingUserController
Table projectId | Issuer | emailOrName | PermissionId | Context
Kind: global class
pendingUserController.add()
Adds an issuer/emailOrName permission for project and a context
Kind: instance method of PendingUserController
pendingUserController.delete()
Removes an issuer/emailOrName permission for project and a context
Kind: instance method of PendingUserController
pendingUserController.list()
Gets the issuer, emailOrName, permission and context objects for the projectId
Kind: instance method of PendingUserController
BigQueryQueryer
This is the connection/query engine owner for a big query json layout
Promise
bigQueryQueryer.initProject(projectId) ⇒ inits a database for a given project
Kind: instance method of BigQueryQueryer
Returns: Promise
- resolves when the project has been initialized
Param | Type | Description |
---|---|---|
projectId | string | the id of the project |
JsonQueryer
This is the connection/query engine owner for a big query json layout
Kind: global class
- JsonQueryer
- .initProject(projectId) ⇒
Promise
- .query(model, include, q, opts) ⇒
promise.<array>
- .initProject(projectId) ⇒
Promise
jsonQueryer.initProject(projectId) ⇒ inits a database for a given project
Kind: instance method of JsonQueryer
Returns: Promise
- resolves when the project has been initialized
Param | Type | Description |
---|---|---|
projectId | string | the id of the project |
promise.<array>
jsonQueryer.query(model, include, q, opts) ⇒ queries bigquery with the query built by the query utility
Kind: instance method of JsonQueryer
Returns: promise.<array>
- resolves a promise with the results of the query
Param | Type | Description |
---|---|---|
model | Sequelize.Model | the model to query against |
include | array | an optional set of models to join in the query |
q | object | the query object |
opts | object | a configuration manager for querying |
Models
This is a subtle wrapper around sequelize to create and sync the database.
Kind: global class
new Models(options)
Create Models object
Param | Type | Description |
---|---|---|
options | object | |
options.sequelize | object | the sequelize options |
options.createModels | function | a function( sequelize ) that defines the models. This function should NOT call sync() on sequelize |
models.checkDatabase()
Creates the database if it does not exist
Kind: instance method of Models
models.start()
Starts sequelize
Kind: instance method of Models
models.stop()
Stops sequelize
Kind: instance method of Models
models.destroy()
This only works in test mode. It will drop the database
Kind: instance method of Models
systemId
Returns the project id if the data is loaded
projectId
Returns the project id if the data is loaded
columnTest
This is a regex test that detects if the input string matches the format to reference a column in an aggregate function
Object
DEFAULT_RAW_OPERATORS : the default allowed raw operators and how to translate them in sequelize
String
DEFAULT_DEFAULT_TYPE : the default type to cast to when all else fails
create(options)
Returns an object containing named controllers. This controllers will be installed into the context and will be available for use to access/change data. To use these, there are two steps. The first it to invoke this method. The results should be merged into the context object. The second step it to call init() with the result and the context to 'start' the controllers.
Kind: global function
Param | Type | Description |
---|---|---|
options | object | The system configuration |
init(cxt, controllers)
This will call init(cxt) on all the controllers in the controllers object
Kind: global function
Param | Type | Description |
---|---|---|
cxt | object | the context |
controllers | object | the result returned from create() |
resolveTemplate()
Returns a Template object. idOrTemp can be either an id, a Template object, or a { id }
buildStruct(attrTree) ⇒
This function builds a tree out of a list of requested attributes
Kind: global function
Returns: a list of arguments for a JSON_OBJECT sql function call
Param | Type | Description |
---|---|---|
attrTree | Object | a deep object of attributes to turn into a query |
buildStruct(attrTree) ⇒
This function builds a tree out of a list of requested attributes
Kind: global function
Returns: a list of arguments for a JSON_OBJECT sql function call
Param | Type | Description |
---|---|---|
attrTree | Object | a deep object of attributes to turn into a query |
getServiceConfig()
returns the firebase definition to be sent to a service
simpleLiteralFunctionBuilder(literal) ⇒
This function is a builder for an arithmetic operation handler designed to be plugged back through the json query util
Kind: global function
Returns: a builder function for the arithmetic operator
Param | Type | Description |
---|---|---|
literal | string | the arithmetic operator |
bucketTimeArgs(args) ⇒
Kind: global function
Returns: an object with the resolved bucketSize and column
Param | Type | Description |
---|---|---|
args | Array | an arrray of arguments for a bucket time function |
BUCKET_TIME(args) ⇒
This is an alias that will look like a single sql function, but that actually consists of a set of operations that transform time fields into discrete buckets that can then be grouped/aggregated by bucket/window
Kind: global function
Returns: the resulting function operators to be passed back to the json query util
Param | Type | Description |
---|---|---|
args | array | the arguments for the BUCKET_TIME sql alias |
Promise.<string>
getSqlFromFindAll(the, the) ⇒ a function to get a find all query from a model and some options https://github.com/sequelize/sequelize/issues/2325 <- screw this guy
Kind: global function
Returns: Promise.<string>
- resolves with the query
Param | Type | Description |
---|---|---|
the | Sequelize.Model | model to build the query with |
the | object | sequelize query options |
prepareOptions()
taken from sequelize
buildStruct(attrTree) ⇒
This function builds a tree out of a list of requested attributes
Kind: global function
Returns: a list of arguments for a JSON_OBJECT sql function call
Param | Type | Description |
---|---|---|
attrTree | Object | a deep object of attributes to turn into a query |
getChildrenData()
Returns the child objects in the table. Currently, this will
return all the objects in the list in a object that looks like:
{ startIndex : 0, count :
removeAll()
This will call delete on all children in the table. If the accumBuffer is not null, the update to the db is not performed here, but from the invoker.
getData()
Retrieves the data object form this DataItem, sets it as the current data, and returns it as the result in the callback
delete()
Deletes the data item and all its children. If the accumBuffer is not defined, than this is the root of the delete. All children should be removed, as well as all references. If accumBuffer is present, it's keys should be updated with the new firebase value (normally null). All changes will be pushed at once.
If this class implements a deleteResources( accumBuffer, cb ) method, it will be invoked to allow for the cleanup of any external resources.
changeNetwork()
Changes the networkId of the device. This will remove aliases from the current network and call join/leaveNetwork as needed
changeSimNetwork()
Changes the simNetworkId of the device. This will remove aliases from the current network and call join/leaveNetwork as needed
getMessageRouteForDevice()
This will return a Promise with the 'routes' part of a MessageRoute.
getId()
Returns the id of device with the given network, aliasKey and alias
getNetworkAliases()
Looks up the networkAliases on the object with the given id
getAlias()
Looks up the specific networkAlias on the object with the given id
addAccount()
Accounts are Users that were made by the project. They are deleted when the project is deleted, and the user's username is unique in the project.
acquireProjectAlias()
Attempts to acquire the aliasName for the project. The aliasType determines the kind of alias (like a 'blueprint' alias), the aliasName is the desired human readable name, and the id is the actual if of the referenced object.
The lock will be saved like this: -projectAlias -projectId -aliasType -aliasName : id
removeRef()
Removes the ref between the owner and the reference object.
getRef()
Returns the value stored at the ownerId's refId. This is the last value pasted into addRef for the given ownerId/refId
getRefSubValue()
Returns the value stored at the ownerId's refId[subpath]. This is the last value passed into addRef for the given ownerId/refId
getRefsData()
Returns the ref objects in the table. Currently, this will
return all the objects in the list in a object that looks like:
{ startIndex : 0, count :
getOwnerIds()
Returns the owner ids of the owners that include the ref. Currently, this will
return all the objects in the list in a object that looks like:
{ startIndex : 0, count :
saveWithManualId(rootRef, idFunc(, data, cb)
This will attempt to create a rootRef/
Kind: global function
Param | Description |
---|---|
rootRef | the ref to the containing list |
idFunc( | count, dat ) returns the next id to try |
data | the data to data. data.id will be set to the final id |
cb | the file callback that is invoked. |
deleteResources()
Remove the project alias if it exists. Note, the check of the validity of the alias is performed in the controller
delete()
This assumes the user has been deleted
encodeOpenIdUser()
Encodes the iss and sub into a string useable as a firebase key
keyForOpenIdUser(iss, sub)
Returns the firebase key for the openid
Kind: global function
Param | Type | Description |
---|---|---|
iss | string | the issuer |
sub | string | the subject |
boolean
| string
openIdUserExists(iss, sub) ⇒ Returns the user if for the iss/sub combination, or false if it doesn't exist
Kind: global function
Returns: boolean
| string
- if found, the user id is returned. Otherwise, false is returned
Param | Type |
---|---|
iss | string |
sub | string |
getOpenIdUserForJwt(jwt)
returns the User data or null if they don't exist
Kind: global function
Param | Type | Description |
---|---|---|
jwt | object | The jwt token containing the user's iss and sub |
createOpenIdUser(iss, sub, userInfo, returnExistingUser)
This will create and return the user for the
Kind: global function
Param | Type | Default | Description |
---|---|---|---|
iss | string | ||
sub | string | ||
userInfo | object | the openid connect userInfo object | |
returnExistingUser | boolean | true | if false, this will throw an exception if the user already exists. Otherwise, the existing user will be returned [true] |
deleteOpenIdUser()
This assumes the user has been deleted
create()
This will create a Models object and starts it. If options.createModels is null, this will use the Definitions.createModels by default.
convertToData()
Converts the object into the form:
[ { path :
- { values : [ { path :
, value : }, ...] } - [ { path :
, value : }, ...] - { path :
, value: } - { keyValue : {
: , : }}
setDeviceData()
Sets the realtime data for the device. The data should be one of these formats:
- { values : [ { path :
, value : }, ...] } - [ { path :
, value : }, ...] - { path :
, value: } - { keyValue : {
: , : }}
addDeviceUser(device, userData)
Addes a user to the system.
Kind: global function
Param | Type | Description | |
---|---|---|---|
device | Object | the Device object to add the user to | |
userData | Object | The body should have the following values: | |
userData.id | string | The User id to add (note, not the username) | |
userData.permissions | Object | An object with at least { role : admin' | 'user' } |
userData.metadata | Object | optional user specific data in regards to the system |
createCheckNearestPermission(deviceSource, check)
This will return a function(req, res, next) that will check for read permissions on the devicec and walk the device.parentDeviceId field to check to see if the user has permission to read the device from any of those devices
Kind: global function
Param | Type | Description | |
---|---|---|---|
deviceSource | Object \ | string | . a Device instance or a string used to access the name of the device object off of req or a function(req) to return the device |
check | function | a function( permissionObject, { device, req } ) |
sendMessage(sendMessage, device, message)
This function will send an outbound message to a device on its network
Kind: global function
Param | Type | Description |
---|---|---|
sendMessage | * | the function to use to send the message |
device | * | the device object |
message | * | the message to send to the device |
flatten()
This will flatten either an patch ops array or an object into { 'a/b/c' : 1 }
projectTransfer()
Determines if a project transfer is occuring anf performs it. the next function will be invoked with next( err, [ ..ops..] ), where the 'projectId' element in the ops array will be removed.
appendLookupToResultList(options)
Will look for items to expand and include in the result list. The result object should look like { startIndex, count, total, items, search }. This will append to result, a refs : { XXX : { id : {...} } } object, where XXX is blueprint, network, etc
Kind: global function
Param | Type | Description | ||
---|---|---|---|---|
options | Object | flatten : [true | false] if true, refs will contain { | blueprint> : { |
createGetAccessToken()
Returns a function that will request an access token from the database in dbInfo. dbInfo should contain { db : the database, service: the service, key : the location to place the accessToken in applyTo }
allowUpdate()
Returns false if the patch ops array contains a path that is not in the given valid array
Promise.<Object>
createResult(user, options, authJwt) ⇒ Creates response object for a verification of the previous jwt token
Kind: global function
Returns: Promise.<Object>
- if the user is a User, the resolved object will
contain { profile, idToken, ..other }. If its an ApiAccess object, it will be
the ApiAccess's data
Param | Type | Description | |
---|---|---|---|
user | User \ | ApiAccess | the User or ApiAccess object |
options | Object | the current permissions, used to sanitize the user data | |
authJwt | Object | an object containing the secret, issuer, audience, expiresIn. If the Project exists and has a jwtTokenExpiration the expiresIn will be replaced with that value |
sendDeviceMessage()
Sends the message using the device's routes model or the defaultTopic
joinNetwork(cb)
Kind: global function
Param | Description |
---|---|
cb | function( err, deviceData ) |
leaveNetwork(cb)
Kind: global function
Param | Description |
---|---|
cb | function( err, deviceData ) |
aliasSet(cb)
Kind: global function
Param | Description |
---|---|
cb | function( err, deviceData ) |
sendMessage(network, device, msg, cb)
Attempts to send a message to the given device on the given network.
Kind: global function
Param | Description |
---|---|
network | the data representing the network |
device | the data representing the device |
msg | the message to send |
cb | the callback |
cleanse() ⇒
Removes any project sensitive data from the network.
Kind: global function
Returns: the network data object to use
send()
Uses Request to send a message. Unlike request, this will convert any statusCode < 200 or >= 300 to an error
getTemplateContext()
Used to constuct a template context for use in building the db root. The extra should contain at least systemId and deviceId.
v4()
v4
v1()
v1
encode()
encode
decode()
decode
ensureLength()
ensureLength
Kind: global function
Api: private
padLeft()
padLeft
Kind: global function
Api: private
trimLeft()
trimLeft
Kind: global function
Api: private