Skip to content

Commit 5dbe8b5

Browse files
authored
Merge pull request #2 from sassoftware/feature_cas
Feature custom object
2 parents d92b983 + 9abbf4a commit 5dbe8b5

18 files changed

+323
-255
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## SAS Portal Framework for SAS Viya v1.0.3
4+
5+
- Add: The MAS Module Scorer object now provides the ability to delete MAS Modules
6+
- Add: New utility functions:
7+
- createCASSession, handles the creation of a CAS session
8+
- terminateCASSession, handles the termination of a CAS session
9+
- terminateSASSession, handles the termination of a SAS session
10+
- getAllURLSearchParams, retrieves the parameters in the URL of the page
11+
- Change: The createSASSession utility no longer auto register a window.onbeforeunload for the termination as that can be easily overwritten
12+
- BF: The submitSASCode utility no implements the waiting for a job completion correctly
13+
- Add New Object runCustomCode, a complex new object type that provides the capability to run code on page load, trigger actions and trigger code on page unload
14+
315
## SAS Portal Framework for SAS Viya v1.0.2
416

517
- Add: Documentation page - [SAS Portal Framework Documentation](https://sassoftware.github.io/sas-portal-framework-for-sas-viya/)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ cp -r ./node_modules/bootstrap/scss ./sdk-assets/bootstrap/scss
9292
Download the [Markdown Renderer](https://github.com/zerodevx/zero-md) using npm - used for Text Objects:
9393

9494
```bash
95-
npm install zero-md
95+
npm install zero-md@2
9696
cp -r ./node_modules/zero-md/dist ./sdk-assets/zero-md
9797
```
9898

index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@
9999
<script src="./js/utility/create-model-content.js"></script>
100100
<script src="./js/utility/create-model-version.js"></script>
101101
<script src="./js/utility/delete-sas-viya-content.js"></script>
102+
<script src="./js/utility/delete-mas-module.js"></script>
103+
<script src="./js/utility/create-cas-session.js"></script>
104+
<script src="./js/utility/terminate-cas-session.js"></script>
105+
<script src="./js/utility/terminate-sas-session.js"></script>
106+
<script src="./js/utility/submit-sas-code.js"></script>
107+
<script src="./js/utility/get-sas-job-state.js"></script>
108+
<script src="./js/utility/get-URL-search-parameters.js"></script>
102109

103110
<!-- Import Object Builder -->
104111
<script src="./js/objects/add-text-objects.js"></script>
@@ -108,6 +115,7 @@
108115
<script src="./js/objects/add-masScore-object.js"></script>
109116
<script src="./js/objects/add-client-administrator.js"></script>
110117
<script src="./js/objects/add-vaReport-object.js"></script>
118+
<script src="./js/objects/add-run-custom-code-objects.js"></script>
111119

112120
<!-- Import Page Builing -->
113121
<script src="./js/generate-tabs.js"></script>

js/generate-pages.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ async function generatePages(VIYAHOST, layout, paneContainer, interfaceText) {
119119
interfaceText?.clienAdministrator
120120
);
121121
break;
122+
case 'runCustomCode':
123+
content = await addRunCustomCode(
124+
currentObjectDefinition,
125+
layout?.general?.shorthand
126+
);
127+
break;
122128
case 'endToEnd':
123129
content = await addE2EObject(
124130
currentObjectDefinition,
@@ -138,7 +144,8 @@ async function generatePages(VIYAHOST, layout, paneContainer, interfaceText) {
138144
currentObjectDefinition,
139145
layout?.general?.shorthand,
140146
interfaceText?.promptBuilder
141-
)
147+
);
148+
break;
142149
case 'contentGroupReport':
143150
break;
144151
case 'contentGroupJob':

js/objects/add-masScore-object.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ async function addMASScoreObject(masObject, paneID, masInterfaceText) {
183183

184184
// Add the Refresh Button
185185
let moduleRefreshButton = document.createElement('button');
186+
moduleRefreshButton.setAttribute('id', `${masObject?.id}-accordion-refreshDelete-button`);
186187
moduleRefreshButton.setAttribute('type', 'button');
187188
moduleRefreshButton.setAttribute('class', 'btn btn-primary');
188189
moduleRefreshButton.innerText = `${masInterfaceText?.moduleRefresh}`;
@@ -242,6 +243,28 @@ async function addMASScoreObject(masObject, paneID, masInterfaceText) {
242243
masRightSide.appendChild(masAccordion);
243244
};
244245

246+
// Add the Delete Button
247+
let masDeleteButton = document.createElement('button');
248+
masDeleteButton.setAttribute('type', 'button');
249+
masDeleteButton.setAttribute('id', `${masObject?.id}-accordion-stepDelete-button`);
250+
masDeleteButton.setAttribute('class', 'btn btn-primary');
251+
masDeleteButton.innerText = masInterfaceText?.moduleDelete;
252+
masDeleteButton.onclick = async function () {
253+
let currentModule = document.getElementById(
254+
`${masObject?.id}-module-dropdown`
255+
);
256+
let currentModuleValue = currentModule.options[currentModule.selectedIndex].value;
257+
let masModuleDeletionResponse = await deleteMASModule(VIYA, currentModuleValue);
258+
259+
if (masModuleDeletionResponse === 204) {
260+
window.alert(masInterfaceText?.successfullModuleDeletion)
261+
// Refresh the UI to remove the old content
262+
document.getElementById(`${masObject?.id}-accordion-refreshDelete-button`).click();
263+
} else {
264+
window.alert(masInterfaceText?.failedModuleDeletion)
265+
}
266+
}
267+
245268
// Add the default Submit Button
246269
let masSubmitButton = document.createElement('button');
247270
masSubmitButton.setAttribute('type', 'button');
@@ -377,6 +400,7 @@ async function addMASScoreObject(masObject, paneID, masInterfaceText) {
377400
masLeftSide.appendChild(moduleStepDropdown);
378401
masLeftSide.appendChild(masNewLine1);
379402
masLeftSide.appendChild(moduleRefreshButton);
403+
masLeftSide.appendChild(masDeleteButton);
380404
masLeftSide.appendChild(masSubmitButton);
381405
masLeftSide.appendChild(masNewLine2);
382406
masLeftSide.appendChild(importScoreTableHeader);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Create a Run Custom Code Object
3+
*
4+
* @param {Object} runCustomCodeObject - Contains the definition of the Run Custom Code Object
5+
* @param {String} paneID - The shorthand of the page which will contain the object
6+
* @returns an empty div
7+
*/
8+
async function addRunCustomCode(runCustomCodeObject, paneID) {
9+
// Run Custom Code Container
10+
let runCustomCodeContainer = document.createElement('div');
11+
runCustomCodeContainer.setAttribute('id', `${paneID}-obj-${runCustomCodeObject?.id}`);
12+
// Get URL search paramaters for code evaluation
13+
let searchParams = getAllURLSearchParams();
14+
// retrieve the compute session
15+
let computeContext = await getComputeContext(window.VIYA, `eq(name,'${runCustomCodeObject?.computeContext}')`);
16+
let computeContextID = computeContext[0].id;
17+
// Check if a SAS Session already exists
18+
if(!window.SASSESSION) {
19+
window.SASSESSION = await createSASSession(window.VIYA, computeContextID);
20+
}
21+
// Submit the code
22+
let response = await submitSASCode(window.VIYA, window.SASSESSION, eval(runCustomCodeObject?.code));
23+
// Check if the user as specified an action
24+
if(runCustomCodeObject?.action) {
25+
switch(runCustomCodeObject?.action) {
26+
case 'reloadReport':
27+
document.getElementById(runCustomCodeObject?.actionElement).getReportHandle().then((reportHandle) => {reportHandle.reloadReport()})
28+
break;
29+
case 'refreshData':
30+
document.getElementById(runCustomCodeObject?.actionElement).getReportHandle().then((reportHandle) => {reportHandle.refreshData()})
31+
break;
32+
default:
33+
console.log(`The ${runCustomCodeObject?.action} isn't supported at this time.`);
34+
}
35+
}
36+
// Check if the user wants an unload event
37+
if(eval(runCustomCodeObject?.unloadCode).length > 0) {
38+
window.addEventListener('beforeunload', function (event) {
39+
submitSASCode(window.VIYA, window.SASSESSION, eval(runCustomCodeObject?.unloadCode));
40+
terminateSASSession(window.VIYA, window.SASSESSION);
41+
});
42+
}
43+
44+
return runCustomCodeContainer;
45+
}

js/utility/create-cas-session.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Create a CAS Session
3+
*
4+
* @param {String} VIYAHOST - The Host URL of the SAS Viya Host
5+
* @param {String} casServer - The CAS server on which the session should be created
6+
* @returns {String} - returns the CAS Session ID
7+
*/
8+
async function createCASSession(VIYAHOST, casServer) {
9+
let SESSIONRESPONSE = await fetch(
10+
`${VIYAHOST}/casManagement/servers/${casServer}/sessions`,
11+
{
12+
// mode: 'no-cors',
13+
method: 'post',
14+
headers: {
15+
'Accept': 'application/json',
16+
'X-CSRF-TOKEN': document?.csrfToken != undefined ? document.csrfToken : '',
17+
},
18+
credentials: 'include',
19+
}
20+
);
21+
if (!SESSIONRESPONSE.ok) {
22+
if (
23+
SESSIONRESPONSE.status === 403 &&
24+
SESSIONRESPONSE.headers.get('x-forbidden-reason') === 'CSRF'
25+
) {
26+
let h = SESSIONRESPONSE.headers.get('x-csrf-header');
27+
let t = SESSIONRESPONSE.headers.get('x-csrf-token');
28+
document.csrfToken = t;
29+
SESSIONRESPONSE = await fetch(
30+
`${VIYAHOST}/casManagement/servers/${casServer}/sessions`,
31+
{
32+
// mode: 'no-cors',
33+
method: 'post',
34+
headers: {
35+
'Accept': 'application/json',
36+
'X-CSRF-TOKEN': t,
37+
},
38+
credentials: 'include',
39+
}
40+
);
41+
}
42+
}
43+
44+
let SESSIONJSON = await SESSIONRESPONSE.json();
45+
return SESSIONJSON?.id;
46+
}

js/utility/create-sas-session.js

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Create a SAS Session
2+
* Create a SAS Session - if you use this function you should check if window.SASSESSION already exists, if so use that instead
33
*
44
* @param {String} VIYAHOST - The Host URL of the SAS Viya Host
55
* @param {String} computeContextID - ID of the SAS Compute Context
@@ -15,7 +15,7 @@ async function createSASSession(
1515
VIYAHOST,
1616
computeContextID,
1717
sessionName,
18-
sessionDescription = '',
18+
sessionDescription = '',
1919
sessionAttributes = {},
2020
sessionEnvironmentOptions = [],
2121
sessionEnvironmentAutoexecLines = []
@@ -79,19 +79,5 @@ async function createSASSession(
7979
}
8080

8181
let SESSIONJSON = await SESSIONRESPONSE.json();
82-
83-
// Add an unload function to end the SAS session
84-
window.onbeforeunload = () => {
85-
fetch(`${VIYAHOST}/compute/sessions/${SESSIONJSON.id}`, {
86-
// mode: 'no-cors',
87-
method: 'delete',
88-
headers: {
89-
Accept: '*/*',
90-
credentials: 'include',
91-
},
92-
});
93-
return null;
94-
};
95-
9682
return SESSIONJSON?.id;
9783
}

js/utility/delete-mas-module.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Delete a specifc model content file
3+
*
4+
* @param {String} VIYAHOST - The Host URL of the SAS Viya Host
5+
* @param {String} moduleID - The ID of the MAS module for which information is collected
6+
* @returns {Promise/Object of Client deletion Response} - Returns a Promise that should resolve into a status code
7+
*/
8+
async function deleteMASModule(VIYAHOST, moduleID) {
9+
let DELETEMASMODULERESPONSE = await fetch(
10+
`${VIYAHOST}/microanalyticScore/modules/${moduleID}`,
11+
{
12+
// mode: 'no-cors',
13+
method: 'delete',
14+
headers: {
15+
'Accept': '*/*',
16+
'X-CSRF-TOKEN':
17+
document?.csrfToken != undefined ? document.csrfToken : ''
18+
},
19+
credentials: 'include'
20+
}
21+
);
22+
if (!DELETEMASMODULERESPONSE.ok) {
23+
if (
24+
DELETEMASMODULERESPONSE.status === 403 &&
25+
DELETEMASMODULERESPONSE.headers.get('x-forbidden-reason') === 'CSRF'
26+
) {
27+
let h = DELETEMASMODULERESPONSE.headers.get('x-csrf-header');
28+
let t = DELETEMASMODULERESPONSE.headers.get('x-csrf-token');
29+
document.csrfToken = t;
30+
DELETEMASMODULERESPONSE = await fetch(
31+
`${VIYAHOST}/microanalyticScore/modules/${moduleID}`,
32+
{
33+
// mode: 'no-cors',
34+
method: 'delete',
35+
headers: {
36+
'Accept': '*/*',
37+
'X-CSRF-TOKEN': t,
38+
},
39+
credentials: 'include',
40+
}
41+
);
42+
}
43+
}
44+
45+
return DELETEMASMODULERESPONSE.status
46+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
*
3+
* @returns {Object} - of all the URL paramters
4+
*/
5+
function getAllURLSearchParams() {
6+
return Object.fromEntries(new URLSearchParams(document.location.search))
7+
}

0 commit comments

Comments
 (0)