restAF- Building Applications with SAS REST API made simple
SAS® Viya® is like a Swiss Army knife when it comes to allowing all types of
clients to talk to it—Java, Python, Lua, R, and, of course, SAS®.
In addition to using these language-based APIs, SAS also publishes APIs that
conform to REST best practices. The well-designed APIs in SAS Viya make it relatively
easy to build complex web and non-web applications with SAS Viya as the engine of your application.
The applications can be solely based on SAS Viya capabilities or users can do a
mashup of the advanced SAS Viya analytics with their other products and capabilities.
restAF is a light-weight, UI-framework agnostic, JavaScript library designed to be
easy-to-use for app developers. restAF takes advantage of the consistency of the REST APIs
to reduce the responses from the server into a standard form, and it also manages the data on
the client side. This reduction enables developers to interact with the server in a repeating pattern
regardless of which service the application is using.
Use restAF to exploit SAS® Cloud Analytic Services (CAS), compute server, SAS® Visual Analytics,
and other key capabilities of SAS Viya in a very consistent manner with minimal coding.
Introduction
The concepts and usage of restAF are explained through examples. Addtional examples can be found in these repositories.
restAF makes writing applications with SAS REST API simple
- A small set of methods to make API calls
- Reduces the information to readily useable parts
- Manages the data for the application in a central store for one version of truth
- Short learning curve
- Results in productive application developers
With restAF, you focus on your application and not on the nitty-gritty details of setting up HTTP calls, processing the
returned results and HTTP codes, parsing the returned payload, managing the data, and so on.
Key technologies
restAF uses three key libraries(among others) that you are probably familiar with.
- redux-saga
- immutable-js
- axios
I am extremely grateful to the developers of these packages for their wonderful contributions.
Accessing restAF
web Application
In your web application you can access restaf with the following script tag.
<script src="https://unpkg.com/restaf/dist/restaf.min.js"></script>
A global variable called restaf is available for use in your javascript programs.
nodejs application
npm install restaf
and then your program
let restaf = require('restaf')
Cloning and modifying restAF
You can clone as follows
clone https://github.com/sassoftware/restaf
cd restaf
npm install
To build the code run this command
npm run build
Background
restAF reduces the hypermedia returned from the REST API calls to a form that is suitable for rapid application development.
THe next few sections will explain the reduction through examples and how you would use it to write your applications.
REST API response is an hypermedia. In practical terms a hypermedia has the following:
-
Links to
-
Data
- A collection of items
- String
- arrays, objects etc...
- Optionally links to operate on the returned data( delete, copy, etc...)
restAF stores all the response and returns an object referred to as rafObject*. The application will use this object
to retrieve information from it use as it sees fit. The methods correspond to the various component parts of the
response discussed above.
Links
A link has several key parts:
- A URI for the next step (edit, delete, and so on).
- An identification of the link to indicate what this next step is. This is referred to as rel – short for “link relationship.”
You can discern the purpose of that link based on the rel. Some examples are:
- If the rel is “content,” you will guess that that this end point returns the content.
- If the rel is “execute,” you will guess that this end point is associated with some type of execution that is appropriate for that context.
- If the rel is “create,” you will rightly assume that the call will result in something being created.
- Media Types
- The media type used for the payload if the end point needs a payload.
- The media type of the returned data. This information can be used by applications to determine programmatically how to deal with the data either for
processing or display.
The links are reduced to an object called rafLink. You will use rel to locate a rafLink in the rafObject. The primary use of rafLink is as a parameter
to the api calls to the server.
Items
Items are the data. Items can be a collection of items, a text string, an array, SVG, and so on.
Some examples are as follows:
- A collection of items: Each item has some data and links to indicate what you can do with this item (delete for example).
- A string (examples: SVG, status of a job, and so on).
- An array (example: SAS logs).
- Some object.
- And, in some cases, no data only http return codes.
restAF manages the links and data for the application.
Basic flow of an application
The flow of an application using restAF is as follows:
- Initialize restAF - you can have only one instance of restAF per application. The initialization creates a store to maintain the data and handle asynchronous calls
to the server.
- Logon to the server - setups connection to the Viya Server
- Register the services - Inform restAF to setup structures(called folders)to handle data for each of the services your application will be using.
- In the main loop of your application make calls to the server thru store. The calls will return an object that the application will interact with to
retrieve the data(read-only) as and when they are needed in the application.
Introductory Example for a nodejs application
Here is a simple example running data step with compute service. In the example below ignore the following for now:
- prtUtil - some utility functions to dump information to console
- logonPayload - data for logging on to Viya Server - discussed later in this document
let restaf = require('restaf');
Preamble: Application specific code to get configuration information
You might do it differently
let config = require('./config');
let logonPayload = config('raf.env');
Step 1: Initialize the store
let store = restaf.initStore();
Steps 2 and 3: Logon to the server and tell restAF which services you want to use
async function setup (store, logonPayload){
let msg = await store.logon(logonPayload);
let {compute} = await store.addServices('compute');
return compute;
}
// Step 4: The main program
async function mainLoop (store, compute, code) {
let contexts = await store.apiCall(compute.links('contexts'));
let context = contexts.itemsList(0);
let createSession = contexts.itemsCmd(context, 'createSession');
let session = await store.apiCall(createSession);
let payload = {
data: {code: code}
};
let job = await store.apiCall(session.links('execute'), payload);
let status = await store.jobState(job, null, 5, 2);
if (status.data === 'running') {
throw `ERROR: Job did not complete in allotted time`;
} else {
let logLines = await store.apiCall(status.job.links('log'));
logViewer(logLines);
}
return 'restAF is cool or what';
}
Your program
let code = [`data _null_; do i = 1 to 100; x=1; end; run; `];
setup (store, logonPayload)
.then (compute => mainLoop(store, compute, code))
.catch(err =>console.log(err));
A Cool Fact - Notice that in the example above you see no reference to links, content-type, accept type, url's etc... Except in some rare cases you do not have to worry about these - restAF takes care of all those house keeping activities. You just focus on what your app needs to do.
restAF Objects and restAF Links
restAF reduces the information returned by an API call an object called rafObject. This object has several properties amd methods.
One key reduction is the transformation of links into rafLinks objects. rafLink allows restAF to navigate its own structures and make API
calls on behalf of the application.
Initializing restAF
To initialize restAF you will make the following 2 calls:
###initStore
initStore must be called first to get the store object. Only one store per application . This call initializes the store to hold all the application data.
let restaf = require( 'restaf' );
store = restaf.initStore();
If using the umd version of the library, the global 'restaf' will be set when the umd version is brought in using the script tag.
At this point there is no data in the store and it is not attached to any Viya Server.
logon
The next step is to associate Viya server with this instance of restAF.
store.logon( payload )
.then ( msg => console.log( msg ) )
.catch( err => console.log( err ) )
payload is either null or is an object with information to logon to the server. If null,
restAF assumes that your browser session has been authenticated by other means. In an non-browser environment you have to
pass in the valid payload.
Below is a typical payload for a nodejs application
payload = {
authType : 'password',
host : 'http://yourserver:portno',
user : username,
password : user password,
clientID : clientid,
clientSecret: clientsecret
} );
A friendly note to protect the payload information on your client machine
since it has all the key authentication information
in plain text.
In a browser use the initial logon payload will look as follows:
payload = {
authType: 'implicit',
host : 'http://yourserver:portno',
clientId: clientId,
redirect: <The redirect that was specified when creating the clientId>
}
In the url that is the target of the redirect you will pass a null for payload
payload = null
restAF will parse the incoming location information and set up access to the server.
##Adding Services
After the initialization steps restAF still does not know what information to manage.
Now populate restAF by registering the services you want to use in your application.
addServices
addServices method adds a folder for the each service.
It additionally calls the root end point for that service
and populates the folder with the links returned from that call.
The addServices method returns a restAF object( rafObject) which we will discuss next.
All service names are case-sensitive
let {serviceName1, serviceName2,....} = await store.addServices( 'serviceName1', 'serviceName2', .... );
At this point serviceName1 is a rafObject with information for the root end points of that service.
Example
let {compute, casManagement} = await store.addServices( 'compute', 'casManagement' );
##restAF Object(rafObject)
In your application you will be dealing primarily with rafObjects and rafLinks. Every time a result is returned from an
API call(including addServices), restAF will "reduce" the information into a folder and return a rafObject
The folders are immutable objects based on immutable.js. while rafObject is a
standard javaScript object.
The application will retrieve information using the methods of the rafObject.
restAF Links ( rafLinks )
restAF extends all the links returned by the server with information it needs to navigate the store and make the final calls.
For an application the most common use of rafLinks is as an argument to apiCall method of the store to navigate to a new resource.
The other common use is in associating links with UI components like buttons, navigators etc. This will be covered later in this document.
###apiCall - Making REST API calls
The apiCall method on the store object is the most used method for making REST calls.
store.apiCall( rafLink <, payload>)
rafLink is obtained using the links and itemsCmd methods on rafObject.
The payload(optional parameter based on the REST end point) is an object with the following optional keys
- data - If the REST call is a PUT or POST then specify the data payload
- qs - Use this to specify the query parameters
- headers - Use this to specify any special headers for this call. For example the upload action in cas requires the the JSON-Parameters header. Another example is file service's 'create' operation requires content-type to be specified
- action - if you are running a cas action, you must specify the action name. Recommend that you use a fully qualified action name ( actionset.actionname )
- All other information in the payload will be ignored silently. Since all the keys are optional restAF has no way to warn you about missing keys or
extraneous keys.
Examples of payload
To run sas code with compute service
{ data: { code: \[ 'data a; x=1;run;' , 'proc print;run' \] }}
To run datastep in cas
{ action: 'datastep.runCode', data: { code: 'data a; x=1;run;' } }
To run upload action for CAS
let JSON_Parameters = {
casout: {
caslib: 'casuser',
name : 'iris'
},
importOptions: {
fileType: 'csv'
}
};
let payload = {
action : 'table.upload',
headers: { 'JSON-Parameters': JSON_Parameters },
data : readFile( 'iris', 'csv' )
}
To create a file with plain text
let payload = {
data : 'my data',
headers: { 'content-type': 'text/plain' }
}
apiCallAll - Making api Calls in parallel
apiCall method makes a single service call. The apiCallAll method on store allows the programmer to execute multiple service calls in parallel.
A typical use case will be running forecast actions in parallel in different cas sessions.
Another is running jobs in multiple compute server sessions. You can mix and match services in this single call( ex: create files, run cas jobs etc... but this would be an exception rather than a rule
store.apiCallAll( requestArray )
- requestArray is an array with each row of the following format:
{ rafLink: <the rafLink to be called>
payload: <standard payload as described earlier in this document>
}
The method returns when all the calls in the requestArray return successfully or when one of them fails. In that sense apiCallAll behaves like PromiseAll.
The order of the results array is the same as the requestArray. See the example for running parallel sas compute jobs
jobState and jobStateAll
Some services might take a long time to run. Typically these services support a "state" link to query the state of the job. Note that not all services support the 'state' link. Services like compute service and JobExecution support this
In SAS API the state end point returns could be in one of these states.
-
running or pending - This means the job did not complete and is still running or pending.
-
Completed The job completed but the actual state of the job will vary based on the service. Some possible completed states are:
- Job completed with no additional clarification(completed)
- Job completed with warnings(warning)
- Job failed or completed with errors(errors)
- Job failed(failed) - similar to the previous item but some services have chosen to return failed
- probably other completed states now or in later releases
Most of the services that have a "state" end point recommend that you call the 'self' end point after the job completes to get the latest results from the job run.
jobState and jobStateAll will make this additional call on 'self' if none of the jobs are still running. This saves you an additional call
At this point your code has to decide how best to proceed given these states.
To check the status of a single job use jobState method.
store.jobState( rafObject-for-job <,payload> <,maxTries > )
Some services take a query parameter to support long polling - pass that information in the standard payload structure
maxTries is the number of times restAF will try if the job did not return a completed status
The returned object has the following form:
{
data : <completed|running|...|...>,
statusCode: <http code >,
job : <rafObject of the final job>
}
An example
let status = await store.jobState( job, {qs: { timeout: 2} }, 5 );
if (status.data === 'running') {
throw \`ERROR: Job did not complete in allotted time\`;
} else {
viewer(status.job); /* some viewer of results results */
switch(status.data) {
case 'warning': console.log(\`Warning: check your log for warnings\`); break;
case 'error':
throw \`Please correct errors and rerun program\`;
default:
break;
}
}
jobStateAll
jobStateAll method to check the status of multiple jobs the jobs with a single call
The returned value has the form
{
running: <No of jobs still running >
details: >details on the completion ( see note above and see below for the form of this object>
jobState: [
{
job : < the job rafObject. see note below >
statusCode: < The http code from the server >
data : < text returned on completion( 'complete' for example ) >
}
]
}
Some notes
- 'running' will be 0 if all jobs completed and >0 if one or more jobs are still running
- jobState will be an array for jobStateAll
The details object gives you a summary of the returned state. It is easier to explain its structure with some examples
For jobState the detail will have a single key/value. The key will be the state returned from the call. For example if the job returned "failed" then the detail object will be as follows
detail: {
failed: 1
}
For jobStateAll the detail could have multiple key/values. For example if you ran 4 jobs in parallel and 2 completed with state of 'warning' , 1 completed with state of 'completed' and one completed with state of 'error', then the detail will look as follows:
detail: {
warning : 2,
completed: 1,
error : 1
}
store.jobStateAll( array of jobs rafObject, payload, maxTries )
.then ( status => {
if ( status.running !== 0 ) {
...handle slow running jobs...
} else {
... check the detail object and make decisions...
}
} )
The payload parameter can be an array or single object. If single object then this payload will be applied to every rafObject in the first parameter.
The primary purpose of the payload is to set timeout as a query parameter
Maxtries works the same as in jobState
Submit and SubmitStatus methods- run api and handle results in "background".
REST APIs are great when the response to an api call is fast and instantaneous.
In SAS it is not uncommon to have jobs that take a bit to run.
For really long running jobs one should use our batch capabilities.
However many solutions have applications where the user submits requests
that can take from a few seconds to a minute or two to complete.
This presents a problem for the application writers.They have to poll the server at some intervals and
probably at many points in their application code.
The submit method is designed to handle this scenario in a more elegant way.
The parameters to submit are:
store.submit( rafLink, payload, delay, id , <OnCompletion exit>, <progress exit> )
where:
-
raflink - same as for apiCall
-
payload - standard payload or null
-
delay - restAF will check the server after every "delay" seconds for completion and drive the callbacks if specified
-
id - specify an id(string). You will use this to retrieve the information the rafObject associated with this job
-
onCompletion - if non-null restAF will call this exit on completion ( success or failure) of the job.
-
progress - if non-null restAH will call this exist after every call to server to check the status.
The method signature of progress exit is
function progress ( data, id )
where
- data - the current status of the job ( running, completed, failed etc...)
- id - the id you passed to the submit method
The function returns either true or false. If you return true, restAF will stop calling the server for the status
of the job.
The method signature of onCompletion is
function onCompletion( err, jobStatus, id );
where
- err - if non-null then this is the error message
- jobStatus - this has the same structure as the return from jobStatus
- id - the id that you passed to the submit method
You will retrieve the final jobStatus using the submitStatus method of the store
let jobStatus = store.submitStatus(id);
where jobStatus is the same as what jobState returns.
One could write part of the introductory example as follows:
let payload = {
data: {code: [`data _null_; do i = 1 to 10000000; end;run; `]}
};
store.submit( session.links('execute'), payload, 1,
'myJob',onCompletion, progress);
Details on restAF Object
###Properties
All property names are case sensitive
-
type: This indicates which of the results pattern this object represent
-
links: Use the links method below to view and obtain rafLinks for further calls. If type is "links" then this object only has links for other end points.
-
itemsList: The rafObject contains an array of items with id's.
- Use itemsList method to retrieve the id's of the items returned.
- Use the scrollCmds method to determine whether there are more data available and for making the pagination calls.
- Use the items function to retrieve specific items( or all items ) from the collection.
- use the itemsCmd method torRetrieve the cmds( links) associated with specific item
-
itemArray: The returned value is an array of text. Use items methods to retrieve this data
-
items: The exact object type is not known. Use items method to get the information and also use resultType property do decide how to handle that data
-
resultType: The media type of the result
-
route: This is an index into the store for this specific result (see section on route later in this document). This will be useful if you want to pass a reference to a rafObject.
-
status: This the http code returned from the api call
-
statusInfo: An object with details of the status
-
host - the fully qualified Viay host the web app is logged on to.
Methods (function properties)
Use these methods to interact with rafObject. These functions return either objects or string depending on the data. Remember these objects are immutable objects. In the document below the common usage patterns are described.
If the returned value is an rafObject you can use query parameters to drill into a specific value in that object. Examples are provided below
links method
The links method will return an immutable object of rafLinks. You will use this method when the type of the rafObject is "links". But sometimes there will be links even when the type is not "links". In those cases the links are usually actions that operate on the whole collection
rafLink = rafObject.links( relName );
relName is the rel of the resource you are looking for. The call will return a rafLink that you can use in an apiCall as shown below
contentLink = fileObject.links( 'content' );
contentObject = store.apiCall ( contentLink );
Sometimes you need the entire list of links. An example is displaying buttons with the titles of the links. Below is an example of printing the titles of all the links.
allLinks = fileObject.links();
allLinks.forEach( ( l, rel ) => {
console.log(\`Title: ${rel} rel: ${rel} \` );
}
itemsList method
If the rafObject type is 'itemsList' use this method to get the immutable array of id's in the current result object. This is really a convenience method for UI ( ex: showing a list of the id's).
Below is an example listing all the id's
rafObject.itemsList().map( id => console.log(id) )
you can also do the following:
console.log( JSON.stringify(rafObject.itemsList(), null, 4) );
scrollCmds method
This method returns the rafLinks associated with pagination. This method can be used to get a rafLink associated with next, prev, last and first cmds(rel). At any given time the server may return some, all or none of these. To ensure safe programming, always check if the returned rafLink is non-null.
let nextCmd = rafObject.scrollCmds( 'next' );
if ( nextCmd !== null ) {
store.apiCall( nextCmd )
.then ( restafobj => {...do something...} )
} else {
console.log( 'No more data' )
}
In an UI you would want to display buttons for scrolling. A typical code will look like this
let cmds = rafObject.scrollCmds();
cmds.forEach( ( c, cmd ) => {
...make your scrolling menu...
} );
For example here is the code for creating a menubar in Reactjs
function ButtonMenu( props ) {
let { cmds, onClick } = props;
let menu = [];
cmds.forEach( ( c, rel ) => {
menu.push( <button key={rel} onClick={onClick(...)}
className="button"> {rel} </button> );
} );
return (
<div>
{menu}
</div>
)
}
items method
This method gives you access to the data that might have been returned. This could be log for compute server, cas results, tables, status, ods and so on. The combination of resultType and type should give you sufficient information on how to process this data. It is possible to write an intelligent UI that determines the "viewer" to use given these pieces of information. But in a typical scenario the application knows what it is expecting at any specific point in the flow of the application.
The items method takes arguments that determines what is returned.
let result = rafObject.items( <query> )
Let us examine a few typical cases
All Items
let allItems = rafObject.items();
If you get all the items then you need to write the code to manage the items based on the resultType
Get a specific item using the id ( which you will get from itemsList() )
let item = rafObject.items( idOfItem );
**Getting data for an item in a collection **
let data = rafObject.item( idOfItem, 'data' );
itemsCmd
Use this method to obtain the cmds associated with a particular item. Obtain the id of the item by using the itemList method.
The most common usage is to get the rafLink for a specific command for the selected item
Get a specific command associated with a item with a specific id
let item = rafObject.itemsCmd( idOfItem, 'name of command' );
ex:
let deleteCmd = rafObject.itemsCmd( '1234ID', 'delete' );
store.apiCall( deleteCmd )
.then ( f => { do whatever) }
.catch( err => { do whatever) }
Get all commands associated with a item with a specific id
let rafLinks = rafObject.itemsCmd( idOfItem );
You can step thru this object as follows
rafObject.itemsCmd( idOfItem ).forEach( ( c, key) => {
}
On occasion one needs to access to the returned headers like etag. For those scenarios use the responseHeaders method.
Get a specific header
let etag = rafObject.responseHeaders( 'etag' );
Get all headers
let headers = rafObject.responseHeaders();
status
To get the http status returned from the call
let status = rafObject.status();
statusInfo
Use this to get any additional information that might have been returned for the status. Mostly useful if the job failed for some reason
let info = rafObject.statusInfo() ;
console.log( info );
Less used Methods
####raw
Returns the raw response from the server
####config
The http payload sent to the server (useful for debugging)
Running CAS actions
In restAF you will access cas actions in the following manner.
- Add casManagement to restAF using store.addServices.
- Look thru the list of servers returned by casManagement and pick the one you want. More often than not there is probably only one cas server.
- Create a session on that server. restAF will add a rel named 'execute' to the session's links returned from this step.
- Make all cas action calls thru runAction method
- restAF collects all the returned tables under a "tables" object to make handling of returned tables a bit easier
Below is an example showing uploading of a csv to cas and then getting its info
The example below uses the nodejs file i/o. If you are running from browser you will use the File Object.
async function example () {
await store.logon(payload);
let {casManagement} = await store.addServices('casManagement');
let servers = await store.apiCall(casManagement.links('servers'));
let casserver = servers.itemsList(0);
let session = await store.apiCall(servers.itemsCmd(casserver, 'createSession'),
{data: {name: 'mysessionname'}});
/
let JSON_Parameters = {
casout: {
caslib: 'casuser',
name : filename
},
importOptions: {
fileType: fileType
}
};
let p = {
headers: {'JSON-Parameters': JSON_Parameters},
data : readFile(filename, fileType),
action : 'upload'
};
let actionResult = await store.runAction(session.links, payload);
prtUtil.view(actionResult, 'Result of upload action');
let deleteAction = await store.apiCall(session.links('delete'));
return "All Done";
}
function readFile (filename, fileType) {
return fs.readFileSync(`./data/${filename}.${fileType}`);
}
Handling tables returned by CAS
Below is a sample program to print the rows in the CAS tables
let data = result.items('tables', <name of the table>);
let itemRows = data.get('rows');
let columns = [];
data.get('schema').map(s => {
columns.push(s.get('name'));
});
itemRows.map((r)=> {
let row = {};
r.map((value, j) => {
row[columns[j]] = value;
});
console.log(JSON.stringify(row, null, 4));
});
Handling Pagination
restAF handles all the pagination for you. Use the scrollCmds as described above. It assumes that you will use the scrollCmds API to retrieve more information.
For example to get the next set of items do the following
let next = rafObject.scrollCmds( 'next' );
if ( next === null ) {
/\* tell user there is no more data */
} else {
store.apiCall( next )
.then ( nextObj => {
do whatever using teh rafObject methods
} );
.catch ( err => {
handle error conditions
} )
}
Replace next with prev,last ans first as appropriate.
Here is an example of printing to console all the items from a collection
let store = restaf.initStore();
async function example (store, logonPayload, counter) {
await store.logon(logonPayload);
let {files} = await store.addServices('files');
let filesList = await store.apiCall(files.links('files'));
printList(filesList.itemsList());
let next;
while(((next = filesList.scrollCmds('next')) !== null) ) {
filesList = await store.apiCall(next);
printList(filesList.itemsList());
}
return 'All Done';
}
const printList = (itemsList) => console.log(JSON.stringify(itemsList, null, 4));
example(store, payload, 10)
.then (status => console.log(status))
.catch(err => console.log(err));
Route property
Each rafObject has a route property. This is a string that is key to the data pointed to by rafObject.
restAF uses the route to find the data associated with this key. If for whatever reason you need to maintain a "pointer" to a
rafObject or to pass it in as a query parameter you can use this.
Route is useful if you want to pass a specific rafObject to another part of your program through some routing mechanism( ex: react-router). Given a route you can get the
associated rafObject with the rafObject method
let myObject = store.rafObject(route);
Authentication
restAF relies on the Oauth2 authentication supported by SAS Viya.
There are a few use cases as described below:
Authenticated browser session: If you session is already authenticated then pass null to logon method
store,logon( null)
.then ( msg => <do your stuff> )
.catch( err => <error handling> )
Using an existing token
There are situations where a valid token might exist. In that case use the following payload to store.logon
store.logon( {
host: "your viya server',
token: "your token
});
nodejs applications
For nodejs applications you will need to use the password flow authentication method
let payload = {
authType : 'password',
host : 'http://yourserver:portno',
user : username,
password : user password,
clientID : clientid,
clientSecret: clientsecret
} );
store.logon ( payload )
.then ( () => ...do whatever your app does ...)
.catch( err => ...do recovery ... )
Web Applications
Implicit flow
For web applications it is recommended that you use implicit flow authentication.
let payload = {
host : <Viya server host (ex: http://my.example.com)
clientID : <clientid>
redirect : <your redirect>,
authType : 'implicit',
};
store.logon ( payload )
.then ( () => ...do whatever your app does ...)
.catch( err => ...do recovery ... )
In your redirect uri pass null for the logon
store.logon(null)
.then(...)
Additional Store Methods
connection
let c = store.connection();
The connection method return information on the current connection.
{
"type": "trusted",
"host": "http://your-viya-host",
"tokenType": "bearer",
"token": "... your Oauth token ..."
}
getServices
let services = store.getServices();
This returns the list of services including "services" specific to restAF(see below)
getService
let services = store.getService('name of service');
This returns raf object for a service that was setup using addServices. \
setAppData and getAppData
Use this method to request restAF to store your data and to retrieve that data
await store.setAppData( id, data )
where
id - id of this data (string)
data - an object that you want restAF to store
To retrieve the data use the getAppData method
let mydata = store.getAppData( id );
To retieve a specific item pass
let mydata1 = store.getAppData( id, name);
Unlike the other store api methods getAppData returns a standard
javaScript object and not an immutable object.
See example appdata.js in the examples directory.
Lifecycle of restAF store
Warning The store will be lost when you close the browser session. While technically it is possible to save and restore the store this version of restAF does not support persistence.