Connection to the UnityBase server (for asynchronous client like Node.js or Browser)
In case host set to value other than location.host
server must be configured to accept
CORS requests.
This is usually done by setting "HTTPAllowOrigin" server configuration option.
Recommended way to create a UBConnection is UB.connect method
// !! In most case UB.connect should be used to create a connection !!
// But can be created directly, for example in case of multiple connection from one page
const UB = require('@ubitybase/ub-pub')
const UBConnection = UB.UBConnection
// connect using UBIP schema
let conn = new UBConnection({
host: 'http://127.0.0.1:888',
requestAuthParams: function(conn, isRepeat){
if (isRepeat){
throw new UB.UBAbortError('invalid credential')
} else {
return Promise.resolve({authSchema: 'UBIP', login: 'admin'})
}
}
})
conn.query({entity: 'uba_user', method: 'select', fieldList: ['ID', 'name']}).then(UB.logDebug)
// Anonymous connect. Allow access to entity methods, granted by ELS rules to `Anonymous` role
// Request below will be success if we grant a `ubm_navshortcut.select` to `Anonymous` on the server side
let conn = new UBConnection({
host: 'http://127.0.0.1:8888'
})
conn.query({entity: 'ubm_navshortcut', method: 'select', fieldList: ['ID', 'name']}).then(UB.logDebug)
//subscribe to events
conn.on('authorizationFail', function(reason){
// indicate user credential is wrong
})
conn.on('authorized', function(ubConnection, session, authParams){console.debug(arguments)} )
UBConnection mixes an EventEmitter, so you can subscribe for {@link event:authorized authorized}
and {@link event:authorizationFail authorizationFail} events.
# new UBConnection (connectionParams: object)
Arguments:
connectionParams
: objecthost
: stringUnityBase server host
appName
='/': stringUnityBase application to connect to (obsolete)
requestAuthParams
: authParamsCallbackAuth parameters callback
request2fa
: request2faCallbackSecond factor request callback
protocol
: stringeither 'https' or 'http' (default)
allowSessionPersistent
: booleanSee connect for details
defHeaders
: objectXHR request headers, what will be added to each xhr request for this connection (after adding headers from UB.xhr.defaults). Object keys is header names. Example:
{"X-Tenant-ID": "12"}
connection parameters
Members
# appConfig : Object instance
Application settings transferred form a server
# appName : string instance
UB application name
# baseURL : string instance
The base of all urls of your requests. Will be prepended to all urls while call UB.xhr
# defHeaders : object instance
Additional headers what will be added to each XHR request
# domain : UBDomain instance
Domain information. Initialized after promise, returned by function getDomainInfo is resolved
# HMAC_SHA256 instance
Calc HMAC_SHA256 from key and string
var shaAsSting = UB.connection.HMAC_SHA256('secretKey', 'something').toString()
# lastLoginName : string instance
Last successful login name. Filled AFTER first 401 response
# preferredLocale : string instance
The preferred (used in previous user session if any or a default for application) locale
# recorderEnabled : boolean instance
Set it to true
for memorize all requests to recordedXHRs array (for debug only!)
# serverUrl : string instance
UB Server URL with protocol and host
# SHA256 instance
Calc SHA256 from string
var shaAsSting = UB.connection.SHA256('something').toString()
# ubNotifier : UBNotifierWSProtocol instance
WebSocket ubNotifier
protocol instance
Methods
# addNew (serverRequest: object) → Promise.<object> instance
Promise of running UBQL command with addNew
method (asynchronously).
Response "data" is an array of default values for row.
Two difference from UBConnection.query:
- ubRequest.method set to 'addnew'
- requests is always buffered in the 20ms period into one ubql call
Date
& 'DateTime' entity attributes are converted from ISO8601 text representation to javaScript Date object
Arguments:
$App.connection.addNew({entity: 'uba_user', fieldList: ['*']}).then(UB.logDebug)
// [{"entity":"uba_user","fieldList":["ID","isPending"],"method":"addnew",
// "resultData":{"fields":["ID","isPending"],"rowCount": 1, "data":[[332462711046145,0]]}
// }]
# addNewAsObject (serverRequest: object, fieldAliasesopt: Object.<string, string>) → Promise.<object> instance
Promise of running UBQL command with addNew
method (asynchronously).
Result is Object with default values for row.
Arguments:
$App.connection.addNewAsObject({"entity":"uba_user"}).then(UB.logDebug)
// result is {ID: 332462709833729, isPending: false}
# cacheClearAll () → Promise instance
Clear all local cache (indexedDB session & permanent and UBConnection.cachedSessionEntityRequested)
# cacheKeyCalculate (root: string, attributesopt: Array.<string>) → string instance
Calculate cache key for request. This key is used to store data inside UBCache
# cacheOccurrenceRefresh (root: string, cacheType: UBCache.cacheTypes) → Promise instance
Refresh all cache occurrence for root depending on cacheType:
- if
Session
- clear indexedDB for this root. - if
SessionEntity
- remove entry in UBConnection#cachedSessionEntityRequested - else - do nothing
Arguments:
root
: stringRoot part of cache key. The same as in UBConnection#cacheKeyCalculate
cacheType
: UBCache.cacheTypes
# cacheOccurrenceRemove (root: string, cacheType: string) → Promise instance
Remove all cache occurrence for root depending on cacheType:
- clear indexedDB for this root.
- remove entry in UBConnection#cachedSessionEntityRequested
Arguments:
root
: stringRoot part of cache key. The same as in UBConnection#cacheKeyCalculate
cacheType
: stringOne of UBCache#cacheTypes
# checkChannelEncryption (session: UBSession, cfg: object) → boolean instance
# convertResponseDataToJsTypes (serverResponse) → * instance
Convert raw server response data to javaScript data according to attribute types. Called by UBConnection#select Currently only Data/DateTime & boolean conversion done If resultLock present - resultLock.lockTime also converted
Arguments:
serverResponse
:
// convert all string representation of date/dateTime to Date object, integer representation of bool to Boolean
return me.query({entity: 'my_entity', method: 'select'}, true)
.then(me.convertResponseDataToJsTypes.bind(me))
# doDelete (serverRequest: object, allowBufferopt: boolean) → Promise instance
Promise of running UBQL command with delete method (asynchronously). Difference from UBConnection.query:
- ubRequest.method set to 'delete' by default
- if necessary it will clear cache
Arguments:
$App.connection.doDelete({
entity: 'uba_user', fieldList: ['ID','name'], execParams: {ID: 1, name:'newName'}
}).then(UB.logDebug)
# doFilterAndSort (cachedData: TubCachedData, ubql: UBQL) → object instance
Call a LocalDataStore#doFilterAndSort - see a parameters there
Arguments:
cachedData
: TubCachedDataubql
: UBQL
# emitEntityChanged (entityCode: string, payload: object) instance
Emit ${entityCode}:changed
event. In case entity has a unity mixin - emit also for unityEntity
# get (url: string, configopt: object) → Promise.<XHRResponse> instance
The same as UB.get but with authorization
Return:
Future object
Arguments:
// call entity method using rest syntax
const certResp = await UB.connection.get('/rest/uba_usercertificate/getCertificate?ID=334607980199937')
const certBin = certResp.data
# getAppInfo () → Promise instance
Retrieve application information. Usually this is first method developer must call after create connection
Return:
Promise resolved to result of getAppInfo method
# getDocument (params: object, optionsopt: object) → Promise instance
Retrieve content of document
type attribute field from server. Usage samples:
Return:
Resolved to document content (either ArrayBuffer in case options.resultIsBinary===true or text/json)
Arguments:
params
: objectentity
: stringCode of entity to retrieve from
attribute
: stringdocument
type attribute codeid
: numberInstance ID
forceMime
: stringIf passed and server support transformation from source MIME type to
forceMime
server perform transformation and return document representation in the passed MIMErevision
: numberOptional revision of the document (if supported by server-side store configuration). Default is current revision.
fileName
: stringFor dirty document should be passed - getDocument endpoint uses this file extension to create a correct Content-Type header.
If not passed - dirty document returned with Content-Type: application/octet-stream. For non-dirty documents Content-Type retrieved from JSON in DB.
isDirty
: booleanOptional ability to retrieve document in dirty state
store
: string????
options
: objectAdditional request options
//Retrieve content of document as string using GET
$App.connection.getDocument({
entity:'ubm_form',
attribute: 'formDef',
ID: 100000232003
}).then(function(result){console.log(typeof result)}); // string
//The same, but using POST for bypass cache
$App.connection.getDocument({
entity:'ubm_form',
attribute: 'formDef',
ID: 100000232003
}, {
bypassCache: true
}).then(function(result){console.log(typeof result)}); // string
//Retrieve content of document as ArrayBuffer and bypass cache
$App.connection.getDocument({
entity:'ubm_form',
attribute: 'formDef',
ID: 100000232003
}, {
bypassCache: true, resultIsBinary: true
}).then(function(result){
console.log('Result is', typeof result, 'of length' , result.byteLength, 'bytes'); //output: Result is ArrayBuffer of length 2741 bytes
let uiArr = new Uint8Array(result) // view into ArrayButter as on the array of byte
console.log('First byte of result is ', uiArr[0])
})
# getDocumentURL (params: object) → Promise.<string> instance
Get a http link to the "Document" attribute content which is valid for the duration of the user session.
This link can be used, for example, in HTML tag and so on.
Used in $App.downloadDocument
method to download a BLOB content
and in FileRenderer
Vue component to display a BLOB content in browser.
Return:
Document URL (valid for the duration of the user session)
Arguments:
params
: objectentity
: stringCode of entity to retrieve from
attribute
: stringdocument
type attribute codeID
: numberInstance ID
revision
: numberRevision of the document. We strongly recommend to pass this argument for correct HTTP cache work
isDirty
: booleanSet it to
true
to retrieve a document in dirty statefileName
: stringFor dirty document should be passed - getDocument endpoint uses this file extension to create a correct Content-Type header. If not passed - dirty document returned with Content-Type: application/octet-stream.
//Retrieve content of document as string using GET
const docURL = await UB.connection.getDocumentURL({
entity:'ubm_form',
attribute: 'formDef',
ID: 100000232003,
revision: 22,
})
// result is alike "/getDocument?entity=ubm_form&attribute=formDef&ID=100000232003&revision=22&session_signature=cbe83ece60126ee4a20d40c2"
# getDomainInfo () → Promise instance
Retrieve domain information from server. Promise resolve instance of UBDomain
# getPreferredUData (userNameopt: string) → object | undefined instance
Return preferred uData (stored in localStorage for specified user).
Preferred uData passed as prefUData
URL param during /auth
handshake and can be used in server-side Session.on('login') handler
Arguments:
userName
: stringif not passed - localStorage.LAST_LOGIN is used (if sets)
# insert (serverRequest: object, allowBufferopt: boolean) → Promise instance
Promise of running UnityBase UBQL command with insert
method (asynchronously).
Difference from UBConnection.query:
- ubRequest.method set to 'insert'
Date
& 'DateTime' entity attributes are converted from ISO8601 text representation to javaScript Date object- if necessary it will clear cache
Arguments:
$App.connection.insert({
entity: 'uba_user', fieldList: ['ID','name'], execParams: {ID: 1, name:'newName'}
}).then(UB.logDebug);
# insertAsObject (serverRequest: object, fieldAliasesopt: Object.<string, string>, allowBufferopt: boolean) → Promise.<object> instance
Promise of running UnityBase UBQL command with insert
method (asynchronously).
In case fieldList
is passed - result will contains new values for attributes specified in fieldList
as Object, otherwise - null
Arguments:
$App.connection.insertAsObject({
entity:"uba_user",
fieldList:['ID', 'name', 'mi_modifyDate'],
execParams: {name: 'insertedName'}
}).then(UB.logDebug)
// {ID: 332462911062017, mi_modifyDate: Tue Apr 23 2019 17:04:30 GMT+0300 (Eastern European Summer Time), name: "insertedname"}
# isAuthorized () → boolean instance
Is user currently logged in. There is no guaranty what session actually exist in server
# logout (reasonParamsopt: object) instance
Log out user from server
Arguments:
reasonParams
= {}: object
# pki () → Promise.<UbPkiInterface> instance
Inject encryption implementation and return a promise to object what implements a UbPkiInterface
# post (url: string, data: *, configopt: object) → Promise.<XHRResponse> instance
The same as UB.post but with authorization
Return:
Future object
# query (ubq: object, allowBufferopt: boolean) → Promise instance
Promise of running UBQL command(s) (asynchronously). The difference from UBConnection.post is:
- ability to buffer request: can merge several
query
in the 20ms period into one ubql call
For well known UnityBase methods use aliases (addNew, select, insert, update, doDelete)
Arguments:
//this two execution is passed to single ubql server execution
$App.connection.query({entity: 'uba_user', method: 'select', fieldList: ['*']}, true).then(UB.logDebug);
$App.connection.query({entity: 'ubm_navshortcut', method: 'select', fieldList: ['*']}, true).then(UB.logDebug);
//but this request is passed in separate ubql (because allowBuffer false in first request
$App.connection.query({entity: 'uba_user', method: 'select', fieldList: ['*']}).then(UB.logDebug);
$App.connection.query({entity: 'ubm_desktop', method: 'select', fieldList: ['*']}, true).then(UB.logDebug);
# queryAsObject (ubq: object, fieldAliasesopt: Object.<string, string>, allowBufferopt: boolean) → Promise.<(Array | null)> instance
Promise of running UBQL command(s) (asynchronously).
Result is array of objects or null.
The difference from UBConnection.post is:
- ability to buffer request: can merge several
query
in the 20ms period into one ubql call
For well known UnityBase methods use aliases (addNew, select, insert, update, doDelete)
Arguments:
//this two execution is passed to single ubql server execution
$App.connection.queryAsObject({entity: 'uba_user', method: 'select', fieldList: ['*']}, true).then(UB.logDebug);
$App.connection.queryAsObject({entity: 'ubm_navshortcut', method: 'select', fieldList: ['*']}, true).then(UB.logDebug);
//but this request is passed in separate ubql (because allowBuffer false in first request
$App.connection.queryAsObject({entity: 'uba_user', method: 'select', fieldList: ['*']}).then(UB.logDebug);
$App.connection.queryAsObject({entity: 'ubm_desktop', method: 'select', fieldList: ['*']}, true).then(UB.logDebug);
# Repository (entityCodeOrUBQL: String) → ClientRepository instance
Create a new instance of repository
# runTrans (ubRequestArray: Array.<ubRequest>) → Promise instance
Group several ubRequest into one server request (executed in singe transaction on server side)
$App.connection.runTrans([
{ entity: 'my_entity', method: 'update', ID: 1, execParams: {code: 'newCode'} },
{ entity: 'my_entity', method: 'update', ID: 2, execParams: {code: 'newCodeforElementWithID=2'} },
]).then(UB.logDebug);
Return:
Resolved to response.data
# runTransAsObject (ubRequestArray: Array.<ubRequest>, fieldAliasesArrayopt: Array.<Object.<string, string>>) → Promise.<Array.<object>> instance
Group several ubRequest into one server request (executed in singe transaction on server side)
Each response will be returned in the same array position as corresponding request.
In case response contains resultData
property of type {data: fields: } it will be converted to array-of-object dataStore format
In case method is insert or update array is replaced by first element. Example below use one entity, but real app can use any combination of entities and methods
Arguments:
$App.connection.runTransAsObject([
{entity: "tst_aclrls", method: 'insert', fieldList: ['ID', 'caption'], execParams: {caption: 'inserted1'}},
{entity: "tst_aclrls", method: 'insert', opaqueParam: 'insertWoFieldList', execParams: {caption: 'inserted2'}},
{entity: "tst_aclrls", method: 'update', fieldList: ['ID', 'caption'], execParams: {ID: 332463213805569, caption: 'updated1'}},
{entity: "tst_aclrls", method: 'delete', execParams: {ID: 332463213805572}}]
).then(UB.logDebug)
// result is:
[{
"entity": "tst_aclrls","method": "insert","fieldList": ["ID","caption"],"execParams": {"caption": "inserted1","ID": 332463256010753},
"resultData": {"ID": 332463256010753,"caption": "inserted1"}
}, {
"entity": "tst_aclrls","method": "insert","opaqueParam": "insertWoFieldList","execParams": {"caption": "inserted2","ID": 332463256010756},"fieldList": []
}, {
"entity": "tst_aclrls","method": "update","fieldList": ["ID","caption"],"execParams": {"ID": 332463213805569,"caption": "updated1"},
"resultData": {"ID": 332463213805569,"caption": "updated1"}
}, {
"entity": "tst_aclrls","method": "delete","execParams": {"ID": 332463213805572},
"ID": 332463213805572
}]
# select (serverRequest: object, bypassCacheopt: boolean) → Promise instance
Promise of running UBQL (asynchronously). Two difference from UBConnection.query:
- ubRequest.method by default set to 'select'
- requests is always buffered in the 20ms period into one ubql call
Date
& 'DateTime' entity attributes are converted from ISO8601 text representation to javaScript Date object- if request entity is cached - cache used
Arguments:
serverRequest
: objectentity
: stringEntity to execute the method
method
: stringMethod of entity to executed. Default to 'select'
ID
: numberif passed - request bypass cache, where & order list is ignored. Can be used to load single record from server
fieldList
: Array.<string>whereList
: objectexecParams
: objectoptions
: objectlockType
: stringalsNeed
: boolean__skipOptimisticLock
: booleanIn case this parameter true and in the buffered
Request to execute
bypassCache
= false: booleanDo not use cache while request even if entity cached. If
__mip_disablecache: true
is passed in serverRequest cache is also disabled.
//retrieve users
$App.connection.select({entity: 'uba_user', fieldList: ['*']}).then(UB.logDebug);
//retrieve users and desktops and then both done - do something
Promise.all($App.connection.select({entity: 'uba_user', fieldList: ['ID', 'name']})
$App.connection.select({entity: 'ubm_desktop', fieldList: ['ID', 'code']})
).then(UB.logDebug);
# serverErrorByCode (errorNum: number) → string instance
Return server-side error message by error number
Arguments:
errorNum
: number
# setDocument (content: *, params: object, onProgressopt: function) → Promise.<object> instance
Saves a file content to the TEMP store of the specified entity attribute of Document type.
Should be called before insert of update. Result of this function is what shall be assigned to the attribute value during insert/update operation.
Return:
Promise resolved blob store metadata
Arguments:
content
: *BLOB attribute content
params
: objectentity
: stringEntity name
attribute
: stringEntity attribute name
id
: numberID of the record
origName
: stringchunkSizeMb
: numberChunks size in Mb. Can be set for each request individually.
- If not defined - uiSettings.adminUI.uploadChunkSizeMb is used
- if === 0 - chunked upload will be disabled
fileName
: stringIf not specified,
params.origName
will be usedencoding
: stringEncoding of
data
. Either omit for binary data or set tobase64
for base64 encoded data
Additional parameters
onProgress
: functionOptional onProgress callback
# setPreferredUData (preferredUData: object) instance
Sets currently logged-in user preferred uData (stored in localStorage for specified user).
Preferred uData passed as prefUData
URL param during /auth
handshake and can be used in server-side Session.on('login') handler
# setRequest2faFunction (onRequest2fa: request2faCallback) instance
Allow to override a connection onRequest2fa function passed as config to UBConnection instance
Arguments:
onRequest2fa
: request2faCallbackFunction with the same signature as requestAuthParams parameter in UBConnection constructor
# setRequestAuthParamsFunction (authParamsFunc: authParamsCallback) instance
Allow to override a connection requestAuthParams function passed as config to UBConnection instance
Arguments:
authParamsFunc
: authParamsCallbackFunction with the same signature as requestAuthParams parameter in UBConnection constructor
# setUiTag (uiTag: string) instance
Sets UI tag for connection.
This tag will be added to a ubql HTTP request as uitag=${uiTag}
and can be used to track from which part of UI request is generated
Recommended naming convention for tags are:
- nsc-${shortcutCode} for something executed from navshortcut
- frm-${formCode} for forms
- afm-${entity} for auto-forms
- rpt-${reportCode} for reports
Arguments:
uiTag
: string
# switchCurrentSession (session: UBSession) instance
Switch current session. Use only on server side
Arguments:
session
: UBSession
# update (serverRequest: object, allowBufferopt: boolean) → Promise.<object> instance
Promise of running UBQL command with update
method (asynchronously).
Difference from UBConnection.query:
- ubRequest.method set to 'update'
Date
& 'DateTime' entity attributes are converted from ISO8601 text representation to javaScript Date object- if necessary it will clear cache
In case fieldList
is passed - result will contains updated values for attributes specified in fieldList
in Array representation
Arguments:
$App.connection.update({
entity: 'uba_user',
fieldList: ['ID','name', 'mi_modifyDate'],
execParams: {ID: 332462122205200, name:'test', mi_modifyDate:"2019-04-23T13:00:00Z"}
}).then(UB.logDebug);
// [{"entity":"uba_user","fieldList":["ID","name","mi_modifyDate"],
// "execParams":{"ID":332462122205200,"name":"test","mi_modifyDate":"2019-04-23T13:03:51Z","mi_modifyUser":10},
// "method":"update",
// "resultData":{"fields":["ID","name","mi_modifyDate"],"rowCount": 1,
// "data":[[332462122205200,"test","2019-04-23T13:03:51Z"]]}
// }]
# updateAsObject (serverRequest: object, fieldAliasesopt: Object.<string, string>, allowBufferopt: boolean) → Promise.<object> instance
Promise of running UBQL command with update
method (asynchronously).
In case fieldList
is passed - result will contain updated values for attributes specified in fieldList
as Object;
If
fieldList
is not passed or empty - returnnull
Arguments:
$App.connection.updateAsObject({
entity: 'uba_user',
fieldList: ['ID','name','mi_modifyDate', 'isPending'],
execParams: {ID: 33246, name:'newName', mi_modifyDate:"2019-04-23T13:00:00Z"}
}).then(UB.logDebug);
// {"ID": 332462122205200, "name": newName", "mi_modifyDate": new Date("2019-04-23T13:03:51Z"), isPending: false}
# userCanChangePassword () → boolean instance
Is auth schema for logged-in user allows password changing (currently - only UB and CERT* with requireUserName)
# userData (keyopt: string) → * instance
Return custom data for logged-in user, or {lang: 'en', login: 'anonymous'} in case not logged in
If key is provided - return only key part of user data. For a list of possible keys see Session.uData in server side documentation
Arguments:
key
: stringOptional key
$App.connection.userData('lang');
// or the same but dedicated alias
$App.connection.userLang()
# userLang () → string instance
Return current user language or 'en' in case not logged in
# userLogin () → string instance
Return current user logon name or 'anonymous' in case not logged in
# xhr (config: object) → Promise.<XHRResponse> instance
Shortcut method to perform authorized/encrypted request to application we connected. Will:
- add Authorization header for non-anonymous sessions
- add UBConnection#baseURL to config.url
- call UB.xhr
- in case server return 401 clear current authorization, call UBConnection#authorize and repeat the request
By default, xhr
retrieve data in JSON format, but for ubql
endpoint can accept Content-Type
header and
serialize DataStore
in one of:
text/xml; charset=UTF-8
application/vnd.oasis.opendocument.spreadsheet
text/csv; charset=UTF-8
text/html; charset=UTF-8
// get response as CSV and save it to file
const { data } = await UB.connection.xhr({
method: 'POST',
url: 'ubql',
data: [UB.Repository.attrs('*').ubql()],
responseType: 'blob',
headers: { 'Content-Type': 'text/csv; charset=UTF-8' }
})
window.saveAs(data, `${fileName}.csv`)
Events
# defineLoginName --> (conn: UBConnection, urlParams: object, certInfo: object)
Fired for UBConnection instance in case authentication type CERT and simpleCertAuth is true just after private key is loaded and certificate is parsed but before auth handshake starts.
Here you can extract username from certificate. By default, it is EDPOU or DRFO or email.
Arguments:
conn
: UBConnectionurlParams
: objectcertInfo
: object
# entity_name:changed --> (ubqlResponse: object)
Fires after successful response for update/insert/delete for entity received
const entityChangedHandler = function ({entity, method, resultData}) {
console.log(`Someone call ${method} for ${entity} with ID ${resultData.ID}`)
}
// inside initialization of some form = subscribe to changes
UB.connection.on('uba_user:changed', entityChangedHandler)
// *IMPORTANT* inside form destroy - unsubscribe
UB.connection.removeListener('uba_user:changed', entityChangedHandler)
Arguments:
ubqlResponse
: object
# entity_name:refresh --> (payload: object)
Fires just after form is refreshed using processing.refresh()
// @param {THTTPRequest} req
UB.connection.on('uba_user:refresh', function (data) {
console.log(`Someone call refresh for User with ID ${data.ID}`
})
# passwordExpired --> ()
Fired for UBConnection instance in case user password is expired.
The only valid endpoint after this is changePassword
Accept 1 arg `(connection: UBConnection)