1
0
mirror of https://github.com/sasjs/adapter.git synced 2025-12-11 01:14:36 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Krishna Acondy
619833db29 Merge pull request #56 from sasjs/performance-improvements
fix(*): Performance improvements
2020-09-01 11:49:34 +01:00
Krishna Acondy
a587d9f6de chore(ci): add lint action 2020-09-01 11:20:46 +01:00
Krishna Acondy
83fb89f779 fix(*): cache job definition code after first fetch, make initial state request before poll 2020-09-01 11:13:52 +01:00
Krishna Acondy
6b98bbce7c chore(types): add code property to Job model 2020-09-01 11:12:56 +01:00
Krishna Acondy
3c2487e423 Merge pull request #49 from sasjs/dependabot/npm_and_yarn/ts-loader-8.0.3
chore(deps-dev): bump ts-loader from 8.0.2 to 8.0.3
2020-08-31 10:23:01 +01:00
dependabot-preview[bot]
0d52af5375 chore(deps-dev): bump ts-loader from 8.0.2 to 8.0.3
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.2 to 8.0.3.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.2...v8.0.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-31 09:04:08 +00:00
Krishna Acondy
d0da343efc Merge pull request #53 from sasjs/dependabot/npm_and_yarn/prettier-2.1.1
chore(deps-dev): bump prettier from 2.0.5 to 2.1.1
2020-08-31 09:58:58 +01:00
dependabot-preview[bot]
54f401a319 chore(deps-dev): bump prettier from 2.0.5 to 2.1.1
Bumps [prettier](https://github.com/prettier/prettier) from 2.0.5 to 2.1.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.0.5...2.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-31 08:24:44 +00:00
Krishna Acondy
5efcb11b7d chore(*): add lint fix command 2020-08-31 09:22:21 +01:00
Krishna Acondy
929d7b993b chore(*): add .prettierrc, fix formatting 2020-08-31 09:15:02 +01:00
Allan Bowe
688221c042 Update example.html 2020-08-28 20:11:04 +02:00
28 changed files with 288 additions and 240 deletions

View File

@@ -21,7 +21,11 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run package:lib
- name: Install Dependencies
run: npm ci
- name: Check code style
run: npm run lint
- name: Build Package
run: npm run package:lib
env:
CI: true

View File

@@ -16,6 +16,8 @@ jobs:
uses: actions/checkout@v2
- name: Install Dependencies
run: npm ci
- name: Check code style
run: npm run lint
- name: Build Project
run: npm run build
- name: Semantic Release

6
.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": false
}

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3,npm/jquery@3.5.1,npm/@sasjs/adapter@1.0.6"></script>
<script src="https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3,npm/jquery@3.5.1,npm/@sasjs/adapter@1"></script>
<script>
var sasJs = new SASjs.default({
appLoc: "/Public/app/readme"
@@ -106,4 +106,4 @@
<canvas id="myChart" style="display: none;"></canvas>
</div>
</body>
</head>
</head>

12
package-lock.json generated
View File

@@ -12495,9 +12495,9 @@
"dev": true
},
"prettier": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz",
"integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.1.tgz",
"integrity": "sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw==",
"dev": true
},
"pretty-format": {
@@ -14258,9 +14258,9 @@
}
},
"ts-loader": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.2.tgz",
"integrity": "sha512-oYT7wOTUawYXQ8XIDsRhziyW0KUEV38jISYlE+9adP6tDtG+O5GkRe4QKQXrHVH4mJJ88DysvEtvGP65wMLlhg==",
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.3.tgz",
"integrity": "sha512-wsqfnVdB7xQiqhqbz2ZPLGHLPZbHVV5Qn/MNFZkCFxRU1miDyxKORucDGxKtsQJ63Rfza0udiUxWF5nHY6bpdQ==",
"dev": true,
"requires": {
"chalk": "^2.3.0",

View File

@@ -5,8 +5,8 @@
"build": "rimraf build && webpack",
"package:lib": "npm run build && cp ./package.json build && cd build && npm version \"5.0.0\" && npm pack",
"publish:lib": "npm run build && cd build && npm publish",
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json",
"lint:fix": "npx prettier --write 'src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}'",
"lint": "npx prettier --check 'src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}'",
"test": "jest",
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build",
"postpublish": "git clean -fd",
@@ -41,11 +41,10 @@
"cp": "^0.2.0",
"jest": "^25.5.4",
"path": "^0.12.7",
"prettier": "^2.0.5",
"rimraf": "^3.0.2",
"semantic-release": "^17.1.1",
"ts-jest": "^25.5.1",
"ts-loader": "^8.0.2",
"ts-loader": "^8.0.3",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typedoc": "^0.17.8",

6
sasjs-tests/.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": false
}

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import React from "react";
import { render } from "@testing-library/react";
import App from "./App";
test('renders learn react link', () => {
test("renders learn react link", () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();

View File

@@ -17,7 +17,7 @@ const App = (): ReactElement<{}> => {
sendArrTests(adapter),
sendObjTests(adapter),
specialCaseTests(adapter),
sasjsRequestTests(adapter),
sasjsRequestTests(adapter)
]);
}
}, [adapter, config]);

View File

@@ -1,8 +1,7 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
"Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #1f2027;
@@ -10,8 +9,7 @@ body {
}
* {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
input {

View File

@@ -11,9 +11,9 @@
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
window.location.hostname === "[::1]" ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
@@ -21,7 +21,7 @@ const isLocalhost = Boolean(
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
@@ -31,7 +31,7 @@ export function register(config) {
return;
}
window.addEventListener('load', () => {
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
@@ -42,8 +42,8 @@ export function register(config) {
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
"This web app is being served cache-first by a service " +
"worker. To learn more, visit https://bit.ly/CRA-PWA"
);
});
} else {
@@ -57,21 +57,21 @@ export function register(config) {
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
"New content is available and will be used when all " +
"tabs for this page are closed. See https://bit.ly/CRA-PWA."
);
// Execute callback
@@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
console.log("Content is cached for offline use.");
// Execute callback
if (config && config.onSuccess) {
@@ -93,25 +93,25 @@ function registerValidSW(swUrl, config) {
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
.catch((error) => {
console.error("Error during service worker registration:", error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' },
headers: { "Service-Worker": "script" }
})
.then(response => {
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
const contentType = response.headers.get("content-type");
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
(contentType != null && contentType.indexOf("javascript") === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
@@ -123,18 +123,18 @@ function checkValidServiceWorker(swUrl, config) {
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
"No internet connection found. App is running in offline mode."
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready
.then(registration => {
.then((registration) => {
registration.unregister();
})
.catch(error => {
.catch((error) => {
console.error(error.message);
});
}

View File

@@ -2,4 +2,4 @@
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
import "@testing-library/jest-dom/extend-expect";

View File

@@ -9,7 +9,7 @@ const defaultConfig: SASjsConfig = {
serverType: ServerType.SASViya,
debug: true,
contextName: "SAS Job Execution compute context",
useComputeApi: false,
useComputeApi: false
};
const customConfig = {
@@ -18,7 +18,7 @@ const customConfig = {
pathSASViya: "viya",
appLoc: "/Public/seedapp",
serverType: ServerType.SAS9,
debug: false,
debug: false
};
export const basicTests = (
@@ -35,7 +35,7 @@ export const basicTests = (
return adapter.logIn(userName, password);
},
assertion: (response: any) =>
response && response.isLoggedIn && response.userName === userName,
response && response.isLoggedIn && response.userName === userName
},
{
title: "Default config",
@@ -54,7 +54,7 @@ export const basicTests = (
sasjsConfig.serverType === defaultConfig.serverType &&
sasjsConfig.debug === defaultConfig.debug
);
},
}
},
{
title: "Custom config",
@@ -72,7 +72,7 @@ export const basicTests = (
sasjsConfig.serverType === customConfig.serverType &&
sasjsConfig.debug === customConfig.debug
);
},
}
},
{
title: "Config overrides",
@@ -92,7 +92,7 @@ export const basicTests = (
sasjsConfig.serverType === defaultConfig.serverType &&
sasjsConfig.debug === false
);
},
},
],
}
}
]
});

View File

@@ -4,7 +4,7 @@ import { TestSuite } from "@sasjs/test-framework";
const stringData: any = { table1: [{ col1: "first col value" }] };
const numericData: any = { table1: [{ col1: 3.14159265 }] };
const multiColumnData: any = {
table1: [{ col1: 42, col2: 1.618, col3: "x", col4: "x" }],
table1: [{ col1: 42, col2: 1.618, col3: "x", col4: "x" }]
};
const multipleRowsWithNulls: any = {
table1: [
@@ -12,8 +12,8 @@ const multipleRowsWithNulls: any = {
{ col1: 42, col2: null, col3: "x", col4: "" },
{ col1: 42, col2: null, col3: "x", col4: "" },
{ col1: 42, col2: 1.62, col3: "x", col4: "x" },
{ col1: 42, col2: 1.62, col3: "x", col4: "x" },
],
{ col1: 42, col2: 1.62, col3: "x", col4: "x" }
]
};
const multipleColumnsWithNulls: any = {
table1: [
@@ -21,8 +21,8 @@ const multipleColumnsWithNulls: any = {
{ col1: 42, col2: null, col3: "x", col4: null },
{ col1: 42, col2: null, col3: "x", col4: null },
{ col1: 42, col2: null, col3: "x", col4: "" },
{ col1: 42, col2: null, col3: "x", col4: "" },
],
{ col1: 42, col2: null, col3: "x", col4: "" }
]
};
const getLongStringData = (length = 32764) => {
@@ -55,7 +55,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0][0] === stringData.table1[0].col1;
},
}
},
{
title: "Long string value",
@@ -67,7 +67,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
assertion: (res: any) => {
const longStringData = getLongStringData();
return res.table1[0][0] === longStringData.table1[0].col1;
},
}
},
{
title: "Overly long string value",
@@ -79,7 +79,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
},
assertion: (error: any) => {
return !!error && !!error.MESSAGE;
},
}
},
{
title: "Single numeric value",
@@ -89,7 +89,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0][0] === numericData.table1[0].col1;
},
}
},
{
title: "Multiple columns",
@@ -104,7 +104,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
res.table1[0][2] === multiColumnData.table1[0].col3 &&
res.table1[0][3] === multiColumnData.table1[0].col4
);
},
}
},
{
title: "Multiple rows with nulls",
@@ -129,7 +129,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
res.table1[index][3] === multipleRowsWithNulls.table1[index].col4;
});
return result;
},
}
},
{
title: "Multiple columns with nulls",
@@ -158,9 +158,9 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
(multipleColumnsWithNulls.table1[index].col4 || "");
});
return result;
},
},
],
}
}
]
});
export const sendObjTests = (adapter: SASjs): TestSuite => ({
@@ -171,11 +171,11 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
description: "Should throw an error",
test: async () => {
const invalidData: any = {
"1 invalid table": [{ col1: 42 }],
"1 invalid table": [{ col1: 42 }]
};
return adapter.request("common/sendObj", invalidData).catch((e) => e);
},
assertion: (error: any) => !!error && !!error.MESSAGE,
assertion: (error: any) => !!error && !!error.MESSAGE
},
{
title: "Single string value",
@@ -185,7 +185,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0].COL1 === stringData.table1[0].col1;
},
}
},
{
title: "Long string value",
@@ -197,7 +197,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
assertion: (res: any) => {
const longStringData = getLongStringData();
return res.table1[0].COL1 === longStringData.table1[0].col1;
},
}
},
{
title: "Overly long string value",
@@ -210,7 +210,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (error: any) => {
return !!error && !!error.MESSAGE;
},
}
},
{
title: "Single numeric value",
@@ -220,7 +220,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0].COL1 === numericData.table1[0].col1;
},
}
},
{
@@ -232,7 +232,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
assertion: (res: any) => {
const data = getLargeObjectData();
return res.table1[9000].BIG === data.table1[9000].big;
},
}
},
{
title: "Multiple columns",
@@ -247,7 +247,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
res.table1[0].COL3 === multiColumnData.table1[0].col3 &&
res.table1[0].COL4 === multiColumnData.table1[0].col4
);
},
}
},
{
title: "Multiple rows with nulls",
@@ -272,7 +272,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
res.table1[index].COL4 === multipleRowsWithNulls.table1[index].col4;
});
return result;
},
}
},
{
title: "Multiple columns with nulls",
@@ -301,7 +301,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
(multipleColumnsWithNulls.table1[index].col4 || "");
});
return result;
},
},
],
}
}
]
});

View File

@@ -19,28 +19,32 @@ export const sasjsRequestTests = (adapter: SASjs): TestSuite => ({
} else {
return requests[0].SASWORK === null;
}
},
}
},
{
title: "Make error and capture log",
description: "Should make an error and capture log",
test: async () => {
return new Promise( async (resolve, reject) => {
adapter.request("common/makeErr", data)
.then((res) => {
//no action here, this request must throw error
})
.catch((err) => {
let sasRequests = adapter.getSasRequests();
let makeErrRequest = sasRequests.find(req => req.serviceLink.includes('makeErr')) || null;
return new Promise(async (resolve, reject) => {
adapter
.request("common/makeErr", data)
.then((res) => {
//no action here, this request must throw error
})
.catch((err) => {
let sasRequests = adapter.getSasRequests();
let makeErrRequest =
sasRequests.find((req) =>
req.serviceLink.includes("makeErr")
) || null;
resolve(!!makeErrRequest);
})
})
resolve(!!makeErrRequest);
});
});
},
assertion: (response) => {
return response;
},
},
],
}
}
]
});

View File

@@ -13,9 +13,9 @@ const specialCharData: any = {
doubleQuote: '"',
crlf: "\r\n",
euro: "€euro",
banghash: "!#banghash",
},
],
banghash: "!#banghash"
}
]
};
const moreSpecialCharData: any = {
@@ -31,9 +31,9 @@ const moreSpecialCharData: any = {
sigma: "Σsigma",
at: "@at",
serbian: "Српски",
dollar: "$",
},
],
dollar: "$"
}
]
};
const getWideData = () => {
@@ -43,7 +43,7 @@ const getWideData = () => {
}
const data: any = {
table1: [cols],
table1: [cols]
};
return data;
@@ -67,7 +67,7 @@ const getLargeDataset = () => {
}
const data: any = {
table1: rows,
table1: rows
};
return data;
@@ -75,7 +75,7 @@ const getLargeDataset = () => {
const errorAndCsrfData: any = {
error: [{ col1: "q", col2: "w", col3: "e", col4: "r" }],
_csrf: [{ col1: "q", col2: "w", col3: "e", col4: "r" }],
_csrf: [{ col1: "q", col2: "w", col3: "e", col4: "r" }]
};
export const specialCaseTests = (adapter: SASjs): TestSuite => ({
@@ -100,7 +100,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table1[0][8] === specialCharData.table1[0].euro &&
res.table1[0][9] === specialCharData.table1[0].banghash
);
},
}
},
{
title: "Other special characters",
@@ -122,7 +122,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table1[0][9] === moreSpecialCharData.table1[0].serbian &&
res.table1[0][10] === moreSpecialCharData.table1[0].dollar
);
},
}
},
{
title: "Wide table with sendArr",
@@ -138,7 +138,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
result && res.table1[0][i] === data.table1[0]["col" + (i + 1)];
}
return result;
},
}
},
{
title: "Wide table with sendObj",
@@ -155,7 +155,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table1[0]["COL" + (i + 1)] === data.table1[0]["col" + (i + 1)];
}
return result;
},
}
},
{
title: "Multiple tables",
@@ -175,7 +175,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table50[0][2] === data.table50[0].col3 &&
res.table50[0][3] === data.table50[0].col4
);
},
}
},
{
title: "Large dataset with sendObj",
@@ -190,7 +190,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
result = result && res.table1[i][0] === data.table1[i][0];
}
return result;
},
}
},
{
title: "Large dataset with sendArr",
@@ -206,7 +206,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
result && res.table1[i][0] === Object.values(data.table1[i])[0];
}
return result;
},
}
},
{
title: "Error and _csrf tables with sendArr",
@@ -225,7 +225,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res._csrf[0][2] === errorAndCsrfData._csrf[0].col3 &&
res._csrf[0][3] === errorAndCsrfData._csrf[0].col4
);
},
}
},
{
title: "Error and _csrf tables with sendObj",
@@ -244,7 +244,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res._csrf[0].COL3 === errorAndCsrfData._csrf[0].col3 &&
res._csrf[0].COL4 === errorAndCsrfData._csrf[0].col4
);
},
},
],
}
}
]
});

View File

@@ -10,7 +10,7 @@ export class FileUploader {
private serverUrl: string,
private jobsPath: string,
private setCsrfTokenWeb: any,
private csrfToken: CsrfToken | null = null,
private csrfToken: CsrfToken | null = null
) {}
private retryCount = 0;
@@ -33,7 +33,7 @@ export class FileUploader {
}${paramsString}`;
const headers = {
"cache-control": "no-cache",
"cache-control": "no-cache"
};
return new Promise((resolve, reject) => {
@@ -49,7 +49,7 @@ export class FileUploader {
method: "POST",
body: formData,
referrerPolicy: "same-origin",
headers,
headers
})
.then(async (response) => {
if (!response.ok) {
@@ -60,7 +60,7 @@ export class FileUploader {
const token = response.headers.get(tokenHeader);
this.csrfToken = {
headerName: tokenHeader,
value: token || "",
value: token || ""
};
this.setCsrfTokenWeb(this.csrfToken);

View File

@@ -10,7 +10,7 @@ export class SAS9ApiClient {
*/
public getConfig() {
return {
serverUrl: this.serverUrl,
serverUrl: this.serverUrl
};
}
@@ -37,9 +37,9 @@ export class SAS9ApiClient {
const executeScriptRequest = {
method: "PUT",
headers: {
Accept: "application/json",
Accept: "application/json"
},
body: `command=${requestPayload}`,
body: `command=${requestPayload}`
};
const executeScriptResponse = await fetch(
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,

View File

@@ -2,7 +2,7 @@ import {
isAuthorizeFormRequired,
parseAndSubmitAuthorizeForm,
convertToCSV,
makeRequest,
makeRequest
} from "./utils";
import * as NodeFormData from "form-data";
import * as path from "path";
@@ -53,7 +53,7 @@ export class SASViyaApiClient {
public getConfig() {
return {
serverUrl: this.serverUrl,
rootFolderName: this.rootFolderName,
rootFolderName: this.rootFolderName
};
}
@@ -73,7 +73,7 @@ export class SASViyaApiClient {
*/
public async getAllContexts(accessToken?: string) {
const headers: any = {
"Content-Type": "application/json",
"Content-Type": "application/json"
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
@@ -88,7 +88,7 @@ export class SASViyaApiClient {
id: context.id,
name: context.name,
version: context.version,
attributes: {},
attributes: {}
}));
}
@@ -98,7 +98,7 @@ export class SASViyaApiClient {
*/
public async getExecutableContexts(accessToken?: string) {
const headers: any = {
"Content-Type": "application/json",
"Content-Type": "application/json"
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
@@ -139,8 +139,8 @@ export class SASViyaApiClient {
name: contextsList[index].name,
version: contextsList[index].version,
attributes: {
sysUserId,
},
sysUserId
}
});
}
});
@@ -155,7 +155,7 @@ export class SASViyaApiClient {
*/
public async createSession(contextName: string, accessToken?: string) {
const headers: any = {
"Content-Type": "application/json",
"Content-Type": "application/json"
};
if (accessToken) {
@@ -178,8 +178,8 @@ export class SASViyaApiClient {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
"Content-Type": "application/json"
}
};
const { result: createdSession } = await this.request<Session>(
`${this.serverUrl}/compute/contexts/${executionContext.id}/sessions`,
@@ -210,7 +210,7 @@ export class SASViyaApiClient {
silent = !debug;
try {
const headers: any = {
"Content-Type": "application/json",
"Content-Type": "application/json"
};
if (accessToken) {
@@ -227,7 +227,7 @@ export class SASViyaApiClient {
_OMITJSONLOG: true,
_OMITSESSIONRESULTS: true,
_OMITTEXTLISTING: true,
_OMITTEXTLOG: true,
_OMITTEXTLOG: true
};
if (debug) {
@@ -242,7 +242,7 @@ export class SASViyaApiClient {
let jobVariables: any = {
SYS_JES_JOB_URI: "",
_program: this.rootFolderName + "/" + jobName,
_program: this.rootFolderName + "/" + jobName
};
let files: any[] = [];
@@ -271,8 +271,8 @@ export class SASViyaApiClient {
description: "Powered by SASjs",
code: linesOfCode,
variables: jobVariables,
arguments: jobArguments,
}),
arguments: jobArguments
})
};
const { result: postedJob, etag } = await this.request<Job>(
@@ -309,7 +309,7 @@ export class SASViyaApiClient {
log = await this.request<any>(
`${this.serverUrl}${logLink.href}/content?limit=10000`,
{
headers,
headers
}
).then((res: any) =>
res.result.items.map((i: any) => i.line).join("\n")
@@ -327,7 +327,7 @@ export class SASViyaApiClient {
{ headers },
"text"
).catch((e) => ({
result: JSON.stringify(e),
result: JSON.stringify(e)
}));
}
@@ -401,8 +401,8 @@ export class SASViyaApiClient {
method: "POST",
body: JSON.stringify({
name: folderName,
type: "folder",
}),
type: "folder"
})
};
createFolderRequest.headers = { "Content-Type": "application/json" };
@@ -449,7 +449,7 @@ export class SASViyaApiClient {
method: "POST",
headers: {
"Content-Type": "application/vnd.sas.job.definition+json",
Accept: "application/vnd.sas.job.definition+json",
Accept: "application/vnd.sas.job.definition+json"
},
body: JSON.stringify({
name: jobName,
@@ -457,18 +457,18 @@ export class SASViyaApiClient {
{
name: "_addjesbeginendmacros",
type: "CHARACTER",
defaultValue: "false",
},
defaultValue: "false"
}
],
type: "Compute",
code,
}),
code
})
};
if (accessToken) {
createJobDefinitionRequest!.headers = {
...createJobDefinitionRequest.headers,
Authorization: `Bearer ${accessToken}`,
Authorization: `Bearer ${accessToken}`
};
}
@@ -487,7 +487,7 @@ export class SASViyaApiClient {
const authCode = await fetch(authUrl, {
referrerPolicy: "same-origin",
credentials: "include",
credentials: "include"
})
.then((response) => response.text())
.then(async (response) => {
@@ -543,7 +543,7 @@ export class SASViyaApiClient {
token = Buffer.from(clientId + ":" + clientSecret).toString("base64");
}
const headers = {
Authorization: "Basic " + token,
Authorization: "Basic " + token
};
let formData;
@@ -562,7 +562,7 @@ export class SASViyaApiClient {
credentials: "include",
headers,
body: formData as any,
referrerPolicy: "same-origin",
referrerPolicy: "same-origin"
}).then((res) => res.json());
return authResponse;
@@ -587,7 +587,7 @@ export class SASViyaApiClient {
token = Buffer.from(clientId + ":" + clientSecret).toString("base64");
}
const headers = {
Authorization: "Basic " + token,
Authorization: "Basic " + token
};
let formData;
@@ -606,7 +606,7 @@ export class SASViyaApiClient {
credentials: "include",
headers,
body: formData as any,
referrerPolicy: "same-origin",
referrerPolicy: "same-origin"
}).then((res) => res.json());
return authResponse;
@@ -626,7 +626,7 @@ export class SASViyaApiClient {
const deleteResponse = await this.request(url, {
method: "DELETE",
credentials: "include",
headers,
headers
});
return deleteResponse;
@@ -675,20 +675,30 @@ export class SASViyaApiClient {
const jobName = sasJob.split("/")[1];
const jobFolder = this.rootFolderMap.get(folderName);
const jobToExecute = jobFolder?.find((item) => item.name === jobName);
const jobDefinitionLink = jobToExecute?.links.find(
(l) => l.rel === "getResource"
);
if (!jobDefinitionLink) {
console.error("Job definition URI was not found.");
throw new Error("Job definition URI was not found.");
if (!jobToExecute) {
throw new Error("Job was not found.");
}
const { result: jobDefinition } = await this.request<JobDefinition>(
`${this.serverUrl}${jobDefinitionLink.href}`,
headers
);
const linesToExecute = jobDefinition.code
.replace(/\r\n/g, "\n")
.split("\n");
let code = jobToExecute?.code;
if (!code) {
const jobDefinitionLink = jobToExecute?.links.find(
(l) => l.rel === "getResource"
);
if (!jobDefinitionLink) {
console.error("Job definition URI was not found.");
throw new Error("Job definition URI was not found.");
}
const { result: jobDefinition } = await this.request<JobDefinition>(
`${this.serverUrl}${jobDefinitionLink.href}`,
headers
);
code = jobDefinition.code;
// Add code to existing job definition
jobToExecute.code = code;
}
const linesToExecute = code.replace(/\r\n/g, "\n").split("\n");
return await this.executeScript(
sasJob,
linesToExecute,
@@ -745,7 +755,7 @@ export class SASViyaApiClient {
(l) => l.rel === "getResource"
)?.href;
const requestInfo: any = {
method: "GET",
method: "GET"
};
const headers: any = { "Content-Type": "application/json" };
if (!!accessToken) {
@@ -765,7 +775,7 @@ export class SASViyaApiClient {
_OMITJSONLOG: true,
_OMITSESSIONRESULTS: true,
_OMITTEXTLISTING: true,
_OMITTEXTLOG: true,
_OMITTEXTLOG: true
};
if (debug) {
@@ -788,8 +798,8 @@ export class SASViyaApiClient {
name: `exec-${jobName}`,
description: "Powered by SASjs",
jobDefinition,
arguments: jobArguments,
}),
arguments: jobArguments
})
};
const { result: postedJob, etag } = await this.request<Job>(
`${this.serverUrl}/jobExecution/jobs?_action=wait`,
@@ -823,7 +833,7 @@ export class SASViyaApiClient {
log = await this.request<any>(
`${this.serverUrl}${logLink.href}/content`,
{
headers,
headers
}
).then((res: any) =>
res.result.items.map((i: any) => i.line).join("\n")
@@ -841,7 +851,7 @@ export class SASViyaApiClient {
const allItems = new Map<string, Job[]>();
const url = "/folders/folders/@item?path=" + this.rootFolderName;
const requestInfo: any = {
method: "GET",
method: "GET"
};
if (accessToken) {
requestInfo.headers = { Authorization: `Bearer ${accessToken}` };
@@ -893,7 +903,7 @@ export class SASViyaApiClient {
private async populateRootFolder(accessToken?: string) {
const url = "/folders/folders/@item?path=" + this.rootFolderName;
const requestInfo: RequestInit = {
method: "GET",
method: "GET"
};
if (accessToken) {
requestInfo.headers = { Authorization: `Bearer ${accessToken}` };
@@ -922,12 +932,29 @@ export class SASViyaApiClient {
let pollCount = 0;
const headers: any = {
"Content-Type": "application/json",
"If-None-Match": etag,
"If-None-Match": etag
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
}
const stateLink = postedJob.links.find((l: any) => l.rel === "state");
if (!stateLink) {
Promise.reject("Job state link was not found.");
}
const { result: state } = await this.request<string>(
`${this.serverUrl}${stateLink.href}?_action=wait&wait=30`,
{
headers
},
"text"
);
const currentState = state.trim();
if (currentState === "completed") {
return Promise.resolve(currentState);
}
return new Promise(async (resolve, _) => {
const interval = setInterval(async () => {
if (
@@ -942,7 +969,7 @@ export class SASViyaApiClient {
const { result: jobState } = await this.request<string>(
`${this.serverUrl}${stateLink.href}?_action=wait&wait=30`,
{
headers,
headers
},
"text"
);
@@ -974,7 +1001,7 @@ export class SASViyaApiClient {
let pollCount = 0;
const headers: any = {
"Content-Type": "application/json",
"If-None-Match": etag,
"If-None-Match": etag
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
@@ -989,7 +1016,7 @@ export class SASViyaApiClient {
const { result: state } = await this.request<string>(
`${this.serverUrl}${stateLink.href}?wait=30`,
{
headers,
headers
},
"text"
);
@@ -1010,7 +1037,7 @@ export class SASViyaApiClient {
private async uploadTables(data: any, accessToken?: string) {
const uploadedFiles = [];
const headers: any = {
"Content-Type": "application/json",
"Content-Type": "application/json"
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
@@ -1027,7 +1054,7 @@ export class SASViyaApiClient {
const createFileRequest = {
method: "POST",
body: csv,
headers,
headers
};
const uploadResponse = await this.request<any>(
@@ -1043,7 +1070,7 @@ export class SASViyaApiClient {
private async getFolderUri(folderPath: string, accessToken?: string) {
const url = "/folders/folders/@item?path=" + folderPath;
const requestInfo: any = {
method: "GET",
method: "GET"
};
if (accessToken) {
requestInfo.headers = { Authorization: `Bearer ${accessToken}` };
@@ -1072,7 +1099,7 @@ export class SASViyaApiClient {
if (this.csrfToken) {
options.headers = {
...options.headers,
[this.csrfToken.headerName]: this.csrfToken.value,
[this.csrfToken.headerName]: this.csrfToken.value
};
}
return await makeRequest<T>(

View File

@@ -2,7 +2,7 @@ import SASjs from "./index";
const adapter = new SASjs();
it("should parse SAS9 source code", async done => {
it("should parse SAS9 source code", async (done) => {
expect(sampleResponse).toBeTruthy();
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse);
expect(parsedSourceCode).toBeTruthy();
@@ -16,7 +16,7 @@ it("should parse SAS9 source code", async done => {
done();
});
it("should parse generated code", async done => {
it("should parse generated code", async (done) => {
expect(sampleResponse).toBeTruthy();
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
sampleResponse

View File

@@ -21,7 +21,7 @@ import {
parseGeneratedCode,
parseWeboutResponse,
needsRetry,
asyncForEach,
asyncForEach
} from "./utils";
import {
SASjsConfig,
@@ -29,7 +29,7 @@ import {
SASjsWaitingRequest,
ServerType,
CsrfToken,
UploadFile,
UploadFile
} from "./types";
import { SASViyaApiClient } from "./SASViyaApiClient";
import { SAS9ApiClient } from "./SAS9ApiClient";
@@ -43,7 +43,7 @@ const defaultConfig: SASjsConfig = {
serverType: ServerType.SASViya,
debug: true,
contextName: "SAS Job Execution compute context",
useComputeApi: false,
useComputeApi: false
};
const requestRetryLimit = 5;
@@ -72,7 +72,7 @@ export default class SASjs {
constructor(config?: any) {
this.sasjsConfig = {
...defaultConfig,
...config,
...config
};
this.setupConfiguration();
@@ -270,7 +270,7 @@ export default class SASjs {
public async setSASjsConfig(config: SASjsConfig) {
this.sasjsConfig = {
...this.sasjsConfig,
...config,
...config
};
await this.setupConfiguration();
}
@@ -295,7 +295,7 @@ export default class SASjs {
return Promise.resolve({
isLoggedIn,
userName: this.userName,
userName: this.userName
});
}
@@ -308,7 +308,7 @@ export default class SASjs {
const loginParams: any = {
_service: "default",
username,
password,
password
};
this.userName = loginParams.username;
@@ -319,7 +319,7 @@ export default class SASjs {
return Promise.resolve({
isLoggedIn,
userName: this.userName,
userName: this.userName
});
}
@@ -336,8 +336,8 @@ export default class SASjs {
referrerPolicy: "same-origin",
body: loginParamsStr,
headers: new Headers({
"Content-Type": "application/x-www-form-urlencoded",
}),
"Content-Type": "application/x-www-form-urlencoded"
})
})
.then((response) => response.text())
.then(async (responseText) => {
@@ -364,7 +364,7 @@ export default class SASjs {
return {
isLoggedIn: loggedIn,
userName: this.userName,
userName: this.userName
};
})
.catch((e) => Promise.reject(e));
@@ -402,7 +402,7 @@ export default class SASjs {
this.setCsrfTokenWeb,
this.csrfTokenWeb
);
return fileUploader.uploadFile(sasJob, files, params);
}
@@ -435,7 +435,7 @@ export default class SASjs {
config = {
...this.sasjsConfig,
...config,
...config
};
sasJob = sasJob.startsWith("/") ? sasJob.replace("/", "") : sasJob;
@@ -526,13 +526,15 @@ export default class SASjs {
// members of type 'folder' should be processed first
if (serviceJson.members[0].members) {
serviceJson.members[0].members.sort((member: {type: string}) => member.type === 'folder' ? -1 : 1)
serviceJson.members[0].members.sort((member: { type: string }) =>
member.type === "folder" ? -1 : 1
);
}
const members =
serviceJson.members[0].name === "services"
? serviceJson.members[0].members
: serviceJson.members
: serviceJson.members;
await this.createFoldersAndServices(
appLoc,
@@ -553,10 +555,10 @@ export default class SASjs {
requestPromise: {
promise: null,
resolve: null,
reject: null,
reject: null
},
SASjob: sasJob,
data,
data
};
sasjsWaitingRequest.requestPromise.promise = new Promise(
@@ -588,7 +590,7 @@ export default class SASjs {
})
.catch(async (response) => {
let error = response.error || response;
if (needsRetry(JSON.stringify(error))) {
if (this.retryCountComputeApi < requestRetryLimit) {
let retryResponse = await this.executeJobViaComputeApi(
@@ -636,10 +638,10 @@ export default class SASjs {
requestPromise: {
promise: null,
resolve: null,
reject: null,
reject: null
},
SASjob: sasJob,
data,
data
};
sasjsWaitingRequest.requestPromise.promise = new Promise(
@@ -720,10 +722,10 @@ export default class SASjs {
requestPromise: {
promise: null,
resolve: null,
reject: null,
reject: null
},
SASjob: sasJob,
data,
data
};
const program = config.appLoc
? config.appLoc.replace(/\/?$/, "/") + sasJob.replace(/^\//, "")
@@ -737,7 +739,7 @@ export default class SASjs {
}`;
const requestParams = {
...this.getRequestParamsWeb(config),
...this.getRequestParamsWeb(config)
};
const formData = new FormData();
@@ -766,7 +768,7 @@ export default class SASjs {
}
const file = new Blob([csv], {
type: "application/csv",
type: "application/csv"
});
formData.append(name, file, `${name}.csv`);
@@ -823,7 +825,7 @@ export default class SASjs {
method: "POST",
body: formData,
referrerPolicy: "same-origin",
headers,
headers
})
.then(async (response) => {
if (!response.ok) {
@@ -834,7 +836,7 @@ export default class SASjs {
const token = response.headers.get(tokenHeader);
this.csrfTokenWeb = {
headerName: tokenHeader,
value: token || "",
value: token || ""
};
}
}
@@ -880,7 +882,7 @@ export default class SASjs {
resolve(JSON.parse(jsonResponseText));
} else {
reject({
MESSAGE: this.parseSAS9ErrorResponse(responseText),
MESSAGE: this.parseSAS9ErrorResponse(responseText)
});
}
} else if (
@@ -924,7 +926,7 @@ export default class SASjs {
return sasjsWaitingRequest.requestPromise.promise;
}
private setCsrfTokenWeb = (csrfToken: CsrfToken) => {
this.csrfTokenWeb = csrfToken;
};
@@ -1056,7 +1058,7 @@ export default class SASjs {
private fetchLogFileContent(logLink: string) {
return new Promise((resolve, reject) => {
fetch(logLink, {
method: "GET",
method: "GET"
})
.then((response: any) => response.text())
.then((response: any) => resolve(response))
@@ -1100,7 +1102,7 @@ export default class SASjs {
timestamp: new Date(),
sourceCode,
generatedCode,
SASWORK: sasWork,
SASWORK: sasWork
});
if (this.sasjsRequests.length > 20) {

View File

@@ -33,7 +33,7 @@ export class SessionManager {
async clearSession(id: string, accessToken?: string) {
const deleteSessionRequest = {
method: "DELETE",
headers: this.getHeaders(accessToken),
headers: this.getHeaders(accessToken)
};
return await this.request<Session>(
`${this.serverUrl}/compute/sessions/${id}`,
@@ -58,7 +58,7 @@ export class SessionManager {
private async createAndWaitForSession(accessToken?: string) {
const createSessionRequest = {
method: "POST",
headers: this.getHeaders(accessToken),
headers: this.getHeaders(accessToken)
};
const { result: createdSession, etag } = await this.request<Session>(
`${this.serverUrl}/compute/contexts/${this.currentContext!.id}/sessions`,
@@ -75,7 +75,7 @@ export class SessionManager {
const { result: contexts } = await this.request<{
items: Context[];
}>(`${this.serverUrl}/compute/contexts`, {
headers: this.getHeaders(accessToken),
headers: this.getHeaders(accessToken)
});
const contextsList =
@@ -99,7 +99,7 @@ export class SessionManager {
private getHeaders(accessToken?: string) {
const headers: any = {
"Content-Type": "application/json",
"Content-Type": "application/json"
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
@@ -117,7 +117,7 @@ export class SessionManager {
let sessionState = session.state;
const headers: any = {
...this.getHeaders(accessToken),
"If-None-Match": etag,
"If-None-Match": etag
};
const stateLink = session.links.find((l: any) => l.rel === "state");
return new Promise(async (resolve, _) => {
@@ -129,7 +129,7 @@ export class SessionManager {
const { result: state } = await this.request<string>(
`${this.serverUrl}${stateLink.href}?wait=30`,
{
headers,
headers
},
"text"
);
@@ -154,7 +154,7 @@ export class SessionManager {
if (this.csrfToken) {
options.headers = {
...options.headers,
[this.csrfToken.headerName]: this.csrfToken.value,
[this.csrfToken.headerName]: this.csrfToken.value
};
}
return await makeRequest<T>(

View File

@@ -6,6 +6,7 @@ export interface Job {
name: string;
uri: string;
createdBy: string;
code?: string;
links: Link[];
results: JobResult;
error?: any;

View File

@@ -4,5 +4,5 @@
*/
export enum ServerType {
SASViya = "SASVIYA",
SAS9 = "SAS9",
SAS9 = "SAS9"
}

View File

@@ -3,7 +3,6 @@
*
*/
export interface UploadFile {
file: File;
fileName: string;
file: File;
fileName: string;
}

View File

@@ -29,12 +29,12 @@ export async function makeRequest<T>(
const token = response.headers.get(tokenHeader);
callback({
headerName: tokenHeader,
value: token || "",
value: token || ""
});
retryRequest = {
...request,
headers: { ...request.headers, [tokenHeader]: token },
headers: { ...request.headers, [tokenHeader]: token }
};
return fetch(url, retryRequest).then((res) => {
etag = res.headers.get("ETag");

View File

@@ -36,7 +36,7 @@ export const parseAndSubmitAuthorizeForm = async (
method: "POST",
credentials: "include",
body: formData,
referrerPolicy: "same-origin",
referrerPolicy: "same-origin"
})
.then((res) => res.text())
.then((res) => {

View File

@@ -1,16 +1,16 @@
export const parseWeboutResponse = (response: string) => {
let sasResponse = "";
let sasResponse = "";
if (response.includes(">>weboutBEGIN<<")) {
try {
sasResponse = response
.split(">>weboutBEGIN<<")[1]
.split(">>weboutEND<<")[0];
} catch (e) {
sasResponse = "";
console.error(e);
}
if (response.includes(">>weboutBEGIN<<")) {
try {
sasResponse = response
.split(">>weboutBEGIN<<")[1]
.split(">>weboutEND<<")[0];
} catch (e) {
sasResponse = "";
console.error(e);
}
}
return sasResponse;
}
return sasResponse;
};