{"id":23331086,"url":"https://github.com/sassoftware/restaf","last_synced_at":"2025-04-09T16:09:35.977Z","repository":{"id":30502642,"uuid":"125293040","full_name":"sassoftware/restaf","owner":"sassoftware","description":"A simple framework for building applications with SAS REST APIs, supported with SAS Viya","archived":false,"fork":false,"pushed_at":"2025-02-24T13:13:18.000Z","size":235377,"stargazers_count":17,"open_issues_count":3,"forks_count":9,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-29T06:04:55.947Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sassoftware.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-15T00:59:30.000Z","updated_at":"2024-11-17T22:15:31.000Z","dependencies_parsed_at":"2023-11-08T02:10:24.562Z","dependency_job_id":"b808b2c2-33cd-487f-bb7f-725994b3e276","html_url":"https://github.com/sassoftware/restaf","commit_stats":{"total_commits":75,"total_committers":1,"mean_commits":75.0,"dds":0.0,"last_synced_commit":"949f3f94e2ff6c3920f1a1337c8dabe5b670b28a"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Frestaf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Frestaf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Frestaf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Frestaf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sassoftware","download_url":"https://codeload.github.com/sassoftware/restaf/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247947857,"owners_count":21023066,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-20T22:30:49.440Z","updated_at":"2025-04-09T16:09:35.956Z","avatar_url":"https://github.com/sassoftware.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# restAF- Building Applications with SAS REST API made simple\n\nSAS® Viya® is like a Swiss Army knife when it comes to allowing all types of\nclients to talk to it—Java, Python, Lua, R, and, of course, SAS®.\nIn addition to using these language-based APIs, SAS also publishes APIs that\nconform to REST best practices. The well-designed APIs in SAS Viya make it relatively\neasy to build complex web and non-web applications with SAS Viya as the engine of your application.\nThe applications can be solely based on SAS Viya capabilities or users can do a\nmashup of the advanced SAS Viya analytics with their other products and capabilities.\nrestAF is a light-weight, UI-framework agnostic, JavaScript library designed to be\neasy-to-use for app developers. restAF takes advantage of the consistency of the REST APIs\nto reduce the responses from the server into a standard form, and it also manages the data on\n the client side. This reduction enables developers to interact with the server in a repeating pattern\n regardless of which service the application is using.\n\nUse restAF to exploit SAS® Cloud Analytic Services (CAS), compute server, SAS® Visual Analytics,\nand other key capabilities of SAS Viya in a very consistent manner with minimal coding.\n\n\n\n\n## Introduction\n\nThe concepts and usage of restAF are explained through examples. Additional examples can be found in these repositories.\n\n* [A collection of nodejs based examples](https://github.com/sassoftware/restaf-demos)\n* [A collection of web apps using restAF](https://github.com/sassoftware/restaf-uidemos)\n* [a SAS API explorer built with restAF](https://github.com/sassoftware/restaf-apiexplorer)\n* [react components using restAF](https://github.com/sassoftware/restaf-uicomponents)\n\nrestAF makes writing applications with SAS REST API simple\n* A small set of methods to make API calls\n* Reduces the information to readily useable parts\n* Manages the data for the application in a central store for one version of truth\n* Short learning curve\n* Results in productive application developers\n\nWith restAF, you focus on your application and not on the nitty-gritty details of setting up HTTP calls, processing the\nreturned results and HTTP codes, parsing the returned payload, managing the data, and so on.\n\n\n## Key technologies\nrestAF uses three key libraries(among others) that you are probably familiar with.\n\n1. [redux-saga](https://redux-saga.js.org/)\n2. [immutable-js](https://facebook.github.io/immutable-js/docs/#/)\n3. [axios](https://github.com/axios/axios)\n\n\n## Accessing restAF\n\n### web Application\n\nIn your web application you can access restaf with the following script tag.\n\n```\n \u003cscript src=\"https://unpkg.com/restaf/dist/restaf.min.js\"\u003e\u003c/script\u003e\n \n```\nA global variable called restaf is available for use in your javascript programs.\n\n### nodejs application\n\n```\nnpm install restaf\n```\n\nand then your program\n\n```\nlet restaf = require('restaf')\n\n```\n\n### Cloning and modifying restAF\nYou can clone as follows\n\n```\nclone https://github.com/sassoftware/restaf\ncd restaf\nnpm install\n```\nTo build the code run this command\n\n```\nnpm run build\n```\n\n## Background\n\nrestAF reduces the hypermedia returned from the REST API calls to a form that is suitable for rapid application development.\nTHe next few sections will explain the reduction through examples and how you would use it to write your applications.\n\nREST API response is an hypermedia. In practical terms a hypermedia has the following:\n\n* Links to\n  - What you can do to with this resource\n\n  - Where you can go next\n     - Examples:  Get a list of items, create a new resource etc...\n     - Optionally information(links) on paginating through the data\n\n* Data\n  - A collection of items\n  - String\n  - arrays, objects etc...\n     - Optionally links to operate on the returned data( delete, copy, etc...)\n\n\nrestAF stores all the response and returns an object referred to as **rafObject***. The application will use this object\nto retrieve information from it use as it sees fit. The methods correspond to the various component parts of the\nresponse discussed above.\n\n### Links \nA link has several key  parts:\n   1.\tA URI for the next step (edit, delete, and so on).\n   2.\tAn identification of the link to indicate what this next step is. This is referred to as **rel**  – short for “link relationship.”\nYou can discern the purpose of that link based on the rel. Some examples are:\n\n- If the rel is “content,” you will guess that that this end point  returns the content.\n- 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.\n- If the rel is “create,” you will rightly assume that the call will result in something being created.\n\n3. Media Types\n   - The media type used for the payload if the end point needs a payload.\n   - 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\n   processing or display.\n\nThe 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\nto the api calls to the server.\n\n### Items\nItems are the data. Items can be a collection of items, a text string, an array, SVG, and so on.\nSome examples are as follows:\n\n- A collection of items: Each item has some data and links to indicate what you can do with this item (delete for example).\n- A string (examples: SVG, status of a job, and so on).\n- An array (example: SAS logs).\n- Some object.\n- And, in some cases, no data only http return codes.\n\nrestAF manages the links and data for the application.\n\n## Basic flow of an application\n\nThe flow of an application using restAF is as follows:\n\n1.  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\n    to the server.\n2.  Logon to the server - setups connection to the Viya Server\n3.  Register the services - Inform restAF to setup structures(called folders)to handle data for each of the services your application will be using.\n4.  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\n    retrieve the data(read-only) as and when they are needed in the application.\n\n\n### Introductory Example for a nodejs application\n\nHere is a simple example running data step with compute service. In the example below ignore the following for now:\n\n*   prtUtil - some utility functions to dump information to console\n*   logonPayload - data for logging on to Viya Server - discussed later in this document\n\n```javascript\nlet restaf   = require('restaf');\n```\n\nPreamble: Application specific code to get configuration information\nYou might do it differently\n\n```javascript\nlet config = require('./config');\nlet logonPayload = config('raf.env');\n```\n\n\nStep 1: Initialize the store\n```javascript\nlet store = restaf.initStore();\n```\n\nSteps 2 and 3: Logon to the server and tell restAF which services you want to use\n```javascript\nasync function setup (store, logonPayload){\n    let msg       = await store.logon(logonPayload);\n    let {compute} = await store.addServices('compute');\n    return compute;\n}\n```\n\n// Step 4: The main program\n\n```javascript\nasync function mainLoop (store, compute, code) {\n\n    // get the list of contexts for compute server\n    // This of contexts as your SAS configs\n    let contexts = await store.apiCall(compute.links('contexts'));\n\n    // lookup the name of the context and create a SAS session with that information\n    // In the example we pick the first context that is returned.\n    // the itemList function returns the list of contexts\n    let context       = contexts.itemsList(0);\n    let createSession = contexts.itemsCmd(context, 'createSession');\n    let session       = await store.apiCall(createSession);\n\n    // Define the payload\n    // This is your SAS program\n    let payload = {\n            data: {code: code}\n    };\n\n    // Execute the data step and wait for completion\n    // All calls to the server are managed by the store you created in step 1\n    let job    = await store.apiCall(session.links('execute'), payload);\n    let status = await store.jobState(job, null, 5, 2);\n\n    // Check the final status of job and view the logs\n    if (status.data === 'running') {\n        throw `ERROR: Job did not complete in allotted time`;\n    } else {\n        let logLines = await store.apiCall(status.job.links('log'));\n        // print the log\n        logViewer(logLines);\n    }\n    return 'restAF is cool or what';\n}\n```\n\nYour program\n\n```javascript\nlet code =  [`data _null_; do i = 1 to 100; x=1; end; run; `];\n\nsetup (store, logonPayload)\n    .then (compute =\u003e mainLoop(store, compute, code))\n    .catch(err =\u003econsole.log(err));\n\n\n```\n\n**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._\n\n## restAF Objects and restAF Links\n\nrestAF reduces the information returned by an API call an object called rafObject. This object has several properties amd methods. \nOne key reduction is the transformation of links into rafLinks objects. rafLink allows restAF to navigate its own structures and make API\n calls on behalf of the application.\n\n## Initializing restAF\n\nTo initialize restAF you will make the following 2 calls:\n\n###initStore\n\ninitStore 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.\n\n```javascript\nlet restaf = require( 'restaf' );\nstore = restaf.initStore();\n```\n\n_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._\n\nAt this point there is no data in the store and it is not attached to any Viya Server.\n\n### logon\n\nThe next step is to associate Viya server with this instance of restAF.\n\n```javascript\n     store.logon( payload )\n        .then ( msg =\u003e console.log( msg ) )\n        .catch( err =\u003e console.log( err ) )\n```\n\npayload is either null or is an object with information to logon to the server. If null,\nrestAF assumes that your browser session has been authenticated by other means. In an non-browser environment you have to\npass in the valid payload.\n\nBelow is a typical payload for a nodejs application\n\n```javascript\n\n     payload = {\n        authType    : 'password',\n        host        : 'http://yourserver:portno',\n        user        : username,\n        password    : user password,\n        clientID    : clientid,  /* get this from your admin */\n        clientSecret: clientsecret /* get this from your admin */\n        } );\n```\n\nA friendly note to protect the payload information on your client machine\nsince it has all the key authentication information\nin plain text.\n\nIn a browser use the initial logon payload will look as follows:\n\n```javascript\n    payload = {\n       authType: 'implicit',\n       host    : 'http://yourserver:portno',\n       clientId: clientId, /* get this from your admin */\n       redirect: \u003cThe redirect that was specified when creating the clientId\u003e\n       }\n```\nIn the url that is the target of the redirect you will pass a null for payload\n\n```javascript\n    payload = null\n```\n\nrestAF will parse the incoming location information and set up access to the server.\n\n\n##Adding Services\n\nAfter the initialization steps restAF still does not know what information to manage.\nNow populate restAF by registering the services you want to use in your application.\n\n### addServices\n\naddServices method adds a folder for the each service.\nIt additionally calls the root end point for that service\nand populates the folder with the links returned from that call.\nThe addServices method returns a restAF object( rafObject) which we will discuss next.\n\n**All service names are case-sensitive**\n\n```javascript\n let {serviceName1, serviceName2,....} = await store.addServices( 'serviceName1', 'serviceName2', ....  );\n``` \n\nAt this point serviceName1 is a rafObject with information for the root end points of that service.\n#### Example\n\n```javascript\nlet {compute, casManagement} = await store.addServices( 'compute', 'casManagement' );\n```\n\n\n##restAF Object(rafObject)\n\nIn your application you will be dealing primarily with rafObjects and rafLinks. Every time a result is returned from an\nAPI call(including addServices), restAF will \"reduce\" the information into a folder and return a rafObject\n\nThe folders are immutable objects based on [immutable.js.](https://facebook.github.io/immutable-js/) while rafObject is a\nstandard javaScript object.\n\nThe application will retrieve information using the methods of the rafObject.\n\n### restAF Links ( rafLinks )\n\n\nrestAF extends all the links returned by the server with information it needs to navigate the store and make the final calls. \nFor an application the most common use of rafLinks is as an argument to apiCall method of the store to navigate to a new resource.\nThe other common use is in associating links with UI components like buttons, navigators etc. This will be covered later in this document.\n\n###apiCall - Making REST API  calls\n\nThe apiCall method on the store object is the most used method for making REST calls.\n\n```javascript\n    store.apiCall( rafLink \u003c, payload\u003e)\n```\n\nrafLink is obtained using the links and itemsCmd methods on rafObject.\n\nThe payload(optional parameter based on the REST end point) is an object with the following optional keys\n\n*   data - If the REST call is a PUT or POST then specify the data payload\n*   qs - Use this to specify the query parameters\n*   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\n*   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 )\n*   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\nextraneous keys.\n\n\n### Examples of payload\n\n_To run sas code with compute service_\n```json\n    { data: { code: \\[ 'data a; x=1;run;' , 'proc print;run' \\] }}\n```\n\n_To run datastep in cas_\n\n    { action: 'datastep.runCode', data: { code: 'data a; x=1;run;' } }\n\n_To run upload action for CAS_\n\n```javascript\n    let JSON_Parameters = {\n                 casout: {\n                     caslib: 'casuser', /* a valid caslib */\n                     name  : 'iris' /* name of output file on cas server */\n                 },\n\n                 importOptions: {\n                     fileType: 'csv' /* type of the file being uploaded */\n                 }\n         };\n    let payload = {\n         action : 'table.upload',\n         headers: { 'JSON-Parameters': JSON_Parameters },\n         data   : readFile( 'iris', 'csv' )\n     }\n```\n\n_To create a file with plain text_\n\n```javascript\n    let payload = {\n       data   : 'my data',\n       headers: { 'content-type': 'text/plain' }\n    }\n```\n\n### apiCallAll - Making api Calls in parallel\n\napiCall method makes a single service call. The apiCallAll method on store allows the programmer to execute multiple service calls in parallel. \nA typical use case will be running forecast actions in parallel in different cas sessions. \nAnother 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\n\n```javascript\n    store.apiCallAll( requestArray )\n```\n\n\n*   requestArray is an array with each row of the following format:\n\n```javascript\n                { rafLink: \u003cthe rafLink to be called\u003e\n                  payload: \u003cstandard payload as described earlier in this document\u003e\n                }\n```\n\n\nThe method returns when all the calls in the requestArray return successfully or when one of them fails. In that sense apiCallAll behaves like PromiseAll.\nThe order of the results array is the same as the requestArray. See the example for running parallel sas compute jobs\n\n\n### jobState and jobStateAll\n\nSome 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\n\nIn SAS API the state end point returns could be in one of these states.\n\n*   **running or pending** \\- This means the job did not complete and is still running or pending.\n*   **Completed** The job completed but the actual state of the job will vary based on the service. Some possible completed states are:\n\n    *   Job completed with no additional clarification(completed)\n    *   Job completed with warnings(warning)\n    *   Job failed or completed with errors(errors)\n    *   Job failed(failed) - similar to the previous item but some services have chosen to return failed\n    *   probably other completed states now or in later releases\n\nMost 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. \njobState and jobStateAll will make this additional call on 'self' if none of the jobs are still running. This saves you an additional call\n\nAt this point your code has to decide how best to proceed given these states.\n\n### To check the status of a single job use jobState method.\n\n```javascript\n    store.jobState( rafObject-for-job \u003c,payload\u003e \u003c,maxTries \u003e )\n```\n\n\nSome services take a query parameter to support long polling - pass that information in the standard payload structure\n\nmaxTries is the number of times restAF will try if the job did not return a completed status\n\nThe returned object has the following form:\n\n```json\n    {\n    data      : \u003ccompleted|running|...|...\u003e,\n    statusCode: \u003chttp code \u003e,\n    job       : \u003crafObject of the final job\u003e\n    }\n```\n\nAn example\n```javascript\n    /* pass a timeout of 2 seconds and try a max of 5 times */\n    let status = await store.jobState( job, {qs: { timeout: 2} }, 5 );\n    if (status.data === 'running') {\n       throw \\`ERROR: Job did not complete in allotted time\\`;\n    } else {\n      viewer(status.job); /* some viewer of results results */\n      switch(status.data) {\n        case 'warning': console.log(\\`Warning: check your log for warnings\\`); break;\n        case 'error':\n            throw \\`Please correct errors and rerun program\\`;\n        default:\n           break;\n      }\n    }\n\n```\n### jobStateAll\n\njobStateAll method to check the status of multiple jobs the jobs with a single call\n\nThe returned value has the form\n```json\n {\n    running: \u003cNo of jobs still running \u003e\n    details: \u003edetails on the completion ( see note above and see below for the form of this object\u003e\n    jobState: [\n          {\n             job       : \u003c the job rafObject. see note below \u003e\n             statusCode: \u003c The http code from the server  \u003e\n            data      : \u003c text returned on completion( 'complete' for example ) \u003e\n          }\n       ]\n}\n```\n\nSome notes\n\n*   'running' will be 0 if all jobs completed and \u003e0 if one or more jobs are still running\n*   jobState will be an array for jobStateAll\n\nThe details object gives you a summary of the returned state. It is easier to explain its structure with some examples\n\nFor 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\n\ndetail: {\n   failed: 1\n}\n\nFor 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:\n\n```json\ndetail: {\n  warning  : 2,\n  completed: 1,\n  error    : 1\n}\n```\n\n```javascript\n        store.jobStateAll( array of jobs rafObject, payload, maxTries )\n        .then ( status =\u003e {\n             if ( status.running !== 0 ) {\n                ...handle slow running jobs...\n             } else {\n              ... check the detail object and make decisions...\n             }\n\n        } )\n```\n\nThe 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. \nThe primary purpose of the payload is to set timeout as a query parameter\n\nMaxtries works the same as in jobState\n\n### Submit and SubmitStatus methods- run api and handle results in \"background\".\n\nREST APIs are great when the response to an api call is fast and instantaneous.\n In SAS it is not uncommon to have jobs that take a bit to run.\n For really long running jobs one should use our batch capabilities.\n However many solutions have applications where the user submits requests\n that can take from a few seconds to a minute or two to complete.\n\nThis presents a problem for the application writers.They have to poll the server at some intervals and\nprobably at many points in their application code.\n\nThe submit method is designed to handle this scenario in a more elegant way.\n\nThe parameters to submit are:\n\n```javascript\n    store.submit( rafLink, payload, delay, id , \u003cOnCompletion exit\u003e, \u003cprogress exit\u003e )\n```\n \n where:\n\n- raflink - same as for apiCall \n\n- payload - standard payload or null\n\n- delay - restAF will check the server after every \"delay\" seconds for completion and drive the callbacks if specified\n\n- id - specify an id(string). You will use this to retrieve the information the rafObject associated with this job\n\n- onCompletion - if non-null restAF will call this exit on completion ( success or failure) of the job.\n\n- progress     - if non-null restAH will call this exist after every call to server to check the status.\n\n\nThe method signature of progress exit is\n\n```javascript\n    function progress ( data, id )\n```\nwhere\n\n- data - the current status of the job ( running, completed, failed etc...)\n- id   - the id you passed to the submit method\n\nThe function returns either true or false. If you return true, restAF will stop calling the server for the status\nof the job.\n\n\nThe method signature of onCompletion is\n\n```javascript\n    function onCompletion( err, jobStatus, id );\n```\nwhere\n\n- err       - if non-null then this is the error message\n- jobStatus - this has the same structure as the return from jobStatus\n- id        - the id that you passed to the submit method\n\nYou will retrieve the final jobStatus using the **submitStatus** method of the store\n\n```javascript\n    let jobStatus = store.submitStatus(id);\n```\n where jobStatus is the same as what jobState returns.\n\nOne could write part of the introductory example as follows:\n\n```javascript\nlet payload = {\n        data: {code: [`data _null_; do i = 1 to 10000000; end;run; `]}\n    };\nstore.submit( session.links('execute'), payload, 1,\n             'myJob',onCompletion, progress);\n```\n\n## Details on restAF Object\n\n###Properties\n\n\nAll property names are case sensitive\n\n1.  **type:** This indicates which of the results pattern this object represent\n\n    1.  **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.\n    2.  **itemsList:** The rafObject contains an array of items with id's.\n        1.  Use itemsList method to retrieve the id's of the items returned.\n        2.  Use the scrollCmds method to determine whether there are more data available and for making the pagination calls.\n        3.  Use the items function to retrieve specific items( or all items ) from the collection.\n        4.  use the itemsCmd method torRetrieve the cmds( links) associated with specific item\n\n    3.  **itemArray:** The returned value is an array of text. Use items methods to retrieve this data\n    4.  **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\n\n3.  **resultType:** The media type of the result\n4.  **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.\n6.  **status:** This the http code returned from the api call\n7.  **statusInfo:** An object with details of the status\n8.  **host** - the fully qualified Viay host the web app is logged on to.\n\n\nMethods (function properties)\n-----------------------------\n\nUse 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.\n\nIf the returned value is an rafObject you can use query parameters to drill into a specific value in that object. Examples are provided below\n\n#### links method\n\nThe 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\n\n```javascript\n    rafLink = rafObject.links( relName );\n```\n\n\nrelName 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\n\n```javascript\n     contentLink   = fileObject.links( 'content' );\n     contentObject = store.apiCall ( contentLink );\n```\n\nSometimes 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.\n\n```javascript\n    allLinks = fileObject.links();\n    allLinks.forEach( ( l, rel ) =\u003e {\n       console.log(\\`Title: ${rel}  rel: ${rel} \\` );\n    }\n```\n\n#### itemsList method\n\nIf 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).\n\nBelow is an example listing all the id's\n\n```javascript\n    rafObject.itemsList().map( id =\u003e console.log(id) )\n```\n\nyou can also do the following:\n\n```javascript\n    console.log( JSON.stringify(rafObject.itemsList(), null, 4) );\n```\n\n\n#### scrollCmds method\n\nThis 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.\n\n```javascript\n    let nextCmd = rafObject.scrollCmds( 'next' );\n    if ( nextCmd !== null ) {\n        store.apiCall( nextCmd )\n           .then ( restafobj =\u003e {...do something...} )\n    } else {\n        console.log( 'No more data' )\n    }\n```\n\nIn an UI you would want to display buttons for scrolling. A typical code will look like this\n\n```javascript\n    let cmds = rafObject.scrollCmds();\n\n    cmds.forEach( ( c, cmd ) =\u003e {\n        ...make your scrolling menu...\n    } );\n```\n\nFor example here is the code for creating a menubar in Reactjs\n\n```javascript\n    function ButtonMenu( props ) {\n        let { cmds, onClick } = props;\n        let menu = [];\n        cmds.forEach( ( c, rel )  =\u003e {\n            menu.push( \u003cbutton key={rel} onClick={onClick(...)}\n                               className=\"button\"\u003e {rel} \u003c/button\u003e );\n        } );\n\n        return (\n            \u003cdiv\u003e\n                {menu}\n            \u003c/div\u003e\n        )\n    }\n```\n\n#### items method\n\nThis 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.\n\nThe items method takes arguments that determines what is returned.\n\n```javascript\n    let result = rafObject.items( \u003cquery\u003e )\n```\n\nLet us examine a few typical cases\n\n**All Items**\n\n```javascript\nlet allItems = rafObject.items();\n```\n\nIf you get all the items then you need to write the code to manage the items based on the resultType\n\n**Get a specific item using the id ( which you will get from itemsList() )**\n\n```javascript\n    let item = rafObject.items( idOfItem );\n```\n\n**Getting data for an item in a collection **\n\n```javascript\n    let data = rafObject.item( idOfItem, 'data' );\n```\n\n\n#### itemsCmd\n\nUse this method to obtain the cmds associated with a particular item. Obtain the id of the item by using the itemList method.\nThe most common usage is to get the rafLink for a specific command for the selected item\n\n**Get a specific command associated with a item with a specific id**\n\n```javascript\n    let item = rafObject.itemsCmd( idOfItem, 'name of command' );\n    ex:\n    let deleteCmd = rafObject.itemsCmd( '1234ID', 'delete' );\n\n    store.apiCall( deleteCmd )\n    .then ( f =\u003e { do whatever) }\n    .catch( err =\u003e { do whatever) }\n```\n\n**Get all commands associated with a item with a specific id**\n\n```javascript\n    let rafLinks = rafObject.itemsCmd( idOfItem );\n```\n\nYou can step thru this object as follows\n\n```javascript\n    rafObject.itemsCmd( idOfItem ).forEach( ( c, key) =\u003e {\n    // c is the current cmd information\n    // key is the name of the command( createSession, delete etc... )\n    }\n```\n\n#### responseHeaders\n\nOn occasion one needs to access to the returned headers like etag. For those scenarios use the responseHeaders method.\n\n**Get a specific header**\n\n```javascript\n    let etag = rafObject.responseHeaders( 'etag' );\n```\n**Get all headers**\n\n```javascript\n    let headers = rafObject.responseHeaders();\n```\n\n#### status\n\nTo get the http status returned from the call\n\n```javascript\n       let status = rafObject.status();\n```\n\n#### statusInfo\n\nUse this to get any additional information that might have been returned for the status. Mostly useful if the job failed for some reason\n\n        let info = rafObject.statusInfo() ;\n        console.log( info );\n\n### Less used Methods\n\n####raw\nReturns the raw response from the server\n\n####config\nThe http payload sent to the server (useful for debugging)\n\n\nRunning CAS actions\n-------------------\n\n\nIn restAF you will access cas actions in the following manner.\n\n*   Add casManagement to restAF using store.addServices.\n*   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.\n*   Create a session on that server. restAF will add a rel named 'execute' to the session's links returned from this step.\n*   Make all cas action calls thru runAction method\n*   restAF collects all the returned tables under a \"tables\" object to make handling of returned tables a bit easier\n\nBelow is an example showing uploading of a csv to cas and then getting its info\n\nThe example below uses the nodejs file i/o. If you are running from browser you will use the File Object.\n\n```javascript\n    async function example () {\n        // setup\n\n        await store.logon(payload);\n        let {casManagement} = await store.addServices('casManagement');\n        let servers         = await store.apiCall(casManagement.links('servers'));\n        let casserver       = servers.itemsList(0);\n        let session          = await store.apiCall(servers.itemsCmd(casserver, 'createSession'),\n                                          {data: {name: 'mysessionname'}});\n        /\n        // setup header for upload and the rest of the payload\n        let JSON_Parameters = {\n            casout: {\n                caslib: 'casuser', /* a valid caslib */\n                name  : filename /* name of output file on cas server */\n            },\n\n            importOptions: {\n                fileType: fileType /* type of the file being uploaded */\n            }\n        };\n        let p = {\n            headers: {'JSON-Parameters': JSON_Parameters},\n            data   : readFile(filename, fileType),/* read the csv file from somewhere */\n            action : 'upload'\n        };\n\n        let actionResult = await store.runAction(session.links, payload);\n        prtUtil.view(actionResult, 'Result of upload action');\n\n        let deleteAction = await store.apiCall(session.links('delete'));\n        return \"All Done\";\n        }\n\n        function readFile (filename, fileType) {\n           return fs.readFileSync(`./data/${filename}.${fileType}`);\n        }\n```\n\n#### Handling tables returned by CAS\nBelow is a sample program to print the rows in the CAS tables\n\n```javascript\n        let data = result.items('tables', \u003cname of the table\u003e);\n        let itemRows = data.get('rows');\n        let columns = [];\n        data.get('schema').map(s =\u003e {\n            columns.push(s.get('name'));\n        });\n\n        itemRows.map((r)=\u003e {\n            let row = {};\n            r.map((value, j) =\u003e {\n                row[columns[j]] = value;\n            });\n            console.log(JSON.stringify(row, null, 4));\n        });\n\n```\n\nHandling Pagination\n-------------------\n\nrestAF 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.\n\nFor example to get the next set of items do the following\n\n```javascript\n    let next = rafObject.scrollCmds( 'next' );\n    if ( next === null ) {\n      /\\* tell user there is no more data */\n    } else {\n       store.apiCall( next )\n       .then ( nextObj =\u003e {\n         do whatever using teh rafObject methods\n        } );\n       .catch ( err =\u003e {\n         handle error conditions\n        } )\n    }\n```\n\nReplace next with prev,last ans first as appropriate.\n\nHere is an example of printing to console all the items from a collection\n\n```javascript\n    let store = restaf.initStore();\n\n    // Pagination\n\n    async function example (store, logonPayload, counter) {\n        await store.logon(logonPayload);\n        let {files} = await store.addServices('files');\n\n        let filesList = await store.apiCall(files.links('files'));\n        printList(filesList.itemsList());\n        let next;\n        // do this loop while the service returns the next link\n        while(((next = filesList.scrollCmds('next')) !== null) )  {\n            filesList = await store.apiCall(next);\n            printList(filesList.itemsList());\n        }\n\n        return 'All Done';\n    }\n\n    const printList =  (itemsList) =\u003e console.log(JSON.stringify(itemsList, null, 4));\n\n    example(store, payload, 10)\n       .then (status =\u003e console.log(status))\n       .catch(err =\u003e console.log(err));\n\n```\n\nRoute property\n--------------\n\nEach rafObject has a route property. This is a string that is key to the data pointed to by rafObject.\nrestAF uses the route to find the data associated with this key. If for whatever reason you need to maintain a \"pointer\" to a\nrafObject or to pass it in as a query parameter you can use this.\n\nRoute 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\nassociated rafObject with the **rafObject** method\n\n```javascript\n   let myObject = store.rafObject(route);\n```\n\n## Authentication\n\nrestAF relies on the Oauth2 authentication supported by SAS Viya.\n\nThere are a few use cases as described below:\n\n### Authenticated browser session: If you session is already authenticated then pass null to logon method\n\n```javascript\n       store,logon( null)\n       .then ( msg =\u003e \u003cdo your stuff\u003e )\n       .catch( err =\u003e \u003cerror handling\u003e )\n```\n\n### Using an existing token\n\nThere are situations where a valid token might exist.  In that case use the following payload to store.logon\n\n```javascript\n        store.logon( {\n            host: \"your viya server',\n            token: \"your token\n        });\n```\n\n### nodejs applications\n\nFor nodejs applications you will need to use the password flow authentication method\n\n```javascript\n       let  payload = {\n            authType    : 'password',\n            host        : 'http://yourserver:portno',\n            user        : username,\n            password    : user password,\n            clientID    : clientid,  /* get this from your admin */\n            clientSecret: clientsecret /* get this from your admin */\n            } );\n        store.logon  ( payload )\n            .then ( () =\u003e ...do whatever your app does ...)\n            .catch( err =\u003e ...do recovery ... )\n```\n\n### Web Applications\n\n#### Implicit flow\nFor web applications it is recommended that you use implicit flow authentication.\n\n```javascript\n        let payload = {\n            host        : \u003cViya server host (ex: http://my.example.com)\n            clientID    : \u003cclientid\u003e\n            redirect    : \u003cyour redirect\u003e,\n            authType    : 'implicit',\n        };\n\n        store.logon  ( payload )\n            .then ( () =\u003e ...do whatever your app does ...)\n            .catch( err =\u003e ...do recovery ... )\n\n```\nIn your redirect uri pass null for the logon\n\n```javascript\nstore.logon(null)\n.then(...)\n\n```\n\n\n## Additional Store Methods\n\n### connection\n\n    let c = store.connection();\nThe connection method return information on the current connection.\n\n```json\n{\n    \"type\": \"trusted\",\n    \"host\": \"http://your-viya-host\",\n    \"tokenType\": \"bearer\",\n    \"token\": \"... your Oauth token ...\"\n}\n```\n\n\n### getServices\n```javascript\n     let services = store.getServices();\n```\n\nThis returns the list of services including \"services\" specific to restAF(see below)\n\n### getService\n\n```javascript\n     let services = store.getService('name of service');\n```\n\nThis returns raf object for a service that was setup using addServices. \\\n\n### setAppData and getAppData\n\nUse this method to request restAF to store your data and to retrieve that data\n\n```javascript\n    await store.setAppData( id, data )\n       where\n           id   - id of this data (string)\n           data - an object that you want restAF to store\n```\n\nTo retrieve the data use the getAppData method\n\n```javascript\n      let mydata = store.getAppData( id );\n```\n\nTo retieve a specific item pass\n\n```javascript\n      let mydata1 = store.getAppData( id, name);\n```\n\nUnlike the other store api methods getAppData returns a standard\njavaScript object and not an immutable object.\n\nSee example appdata.js in the examples directory.\n\nLifecycle of restAF store\n----------------------\n\n**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.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsassoftware%2Frestaf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsassoftware%2Frestaf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsassoftware%2Frestaf/lists"}