mirror of
https://github.com/sasjs/adapter.git
synced 2025-12-11 09:24:35 +00:00
Added suggestions
This commit is contained in:
209
example.html
209
example.html
@@ -1,107 +1,114 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||
<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: "/Products/demo/readme"
|
||||
,serverType:"SAS9", debug: "false"
|
||||
});
|
||||
function initSasJs() {
|
||||
$('#loading-spinner').show()
|
||||
// instantiate sasjs with options such as backend app location
|
||||
// login (it's also possible to set an autologin when making requests)
|
||||
sasJs.logIn(
|
||||
$('#username')[0].value
|
||||
,$('#password')[0].value
|
||||
).then((response) => {
|
||||
if (response.isLoggedIn === true) {
|
||||
$('#loading-spinner').hide()
|
||||
$('.login').hide()
|
||||
$('#getdata').show()
|
||||
$('#cars').show()
|
||||
}
|
||||
})
|
||||
}
|
||||
function getData(){
|
||||
$('#loading-spinner').show()
|
||||
$('#myChart').remove();
|
||||
$('#chart-container').append('<canvas id="myChart" style="display: none;"></canvas>')
|
||||
// make a request to a SAS service
|
||||
var type = $("#cars")[0].options[$("#cars")[0].selectedIndex].value;
|
||||
// request data from an endpoint under your appLoc
|
||||
sasJs.request("/common/getdata", {
|
||||
// send data as an array of objects - each object is one row
|
||||
fromjs: [{ type: type }]
|
||||
}).then((response) => {
|
||||
$('#myChart').show();
|
||||
var labels = []
|
||||
var data = []
|
||||
response.areas.map((d) => {
|
||||
labels.push(d.MAKE);
|
||||
data.push(d.AVPRICE);
|
||||
})
|
||||
$('#loading-spinner').hide()
|
||||
initGraph(labels, data, type);
|
||||
})
|
||||
}
|
||||
function initGraph(labels, data, type){
|
||||
var myCanvas = document.getElementById("myChart");
|
||||
var ctx = myCanvas.getContext("2d");
|
||||
var myChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: "Average Invoice Price in USD for " + type + " Cars by Manufacturer",
|
||||
data: data,
|
||||
backgroundColor: "rgba(255,99,132,0.2)",
|
||||
borderColor: "rgba(255,99,132,1)",
|
||||
borderWidth: 1,
|
||||
hoverBackgroundColor: "rgba(255,99,132,0.4)",
|
||||
hoverBorderColor: "rgba(255,99,132,1)",
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
scales: {yAxes: [{ticks: {beginAtZero: true}}]}
|
||||
<head>
|
||||
<meta charset='utf-8' http-equiv='X-UA-Compatible' content='IE=edge' />
|
||||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css' integrity='sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh' crossorigin='anonymous'>
|
||||
<script src='https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3,npm/jquery@3.5.1,npm/@sasjs/adapter@1'></script>
|
||||
<script>
|
||||
const sasJs = new SASjs.default({
|
||||
appLoc: '/Products/demo/readme',
|
||||
serverType:'SAS9',
|
||||
debug: 'false'
|
||||
})
|
||||
|
||||
const initSasJs = () => {
|
||||
$('#loading-spinner').show()
|
||||
|
||||
// instantiate sasJs with options such as backend app location
|
||||
// login (it's also possible to set an auto login when making requests)
|
||||
sasJs.logIn($('#username')[0].value, $('#password')[0].value)
|
||||
.then((response) => {
|
||||
if (response.isLoggedIn === true) {
|
||||
$('#loading-spinner').hide()
|
||||
$('.login').hide()
|
||||
$('#getDataBtn').show()
|
||||
$('#cars').show()
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid" style="text-align: center; margin-top: 10px;">
|
||||
<div class="row">
|
||||
<div class="col-lg-5 col-md-7 col-sm-10 mx-auto mx-auto">
|
||||
<h1>Demo Seed App for <span class="code">SASjs</span></h1>
|
||||
<div class="login" id="login-form">
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="text" id="username" placeholder="Enter username" />
|
||||
|
||||
// make a request to a SAS service
|
||||
const getData = () => {
|
||||
$('#loading-spinner').show()
|
||||
$('#myChart').remove()
|
||||
$('#chart-container').append("<canvas id='myChart' style='display: none'></canvas>")
|
||||
|
||||
const type = $('#cars')[0].options[$('#cars')[0].selectedIndex].value
|
||||
|
||||
// request data from an endpoint under your appLoc
|
||||
// send data as an array of objects - each object is one row
|
||||
sasJs.request('/common/getdata', {fromjs: [{ type: type }]})
|
||||
.then((response) => {
|
||||
$('#myChart').show()
|
||||
$('#loading-spinner').hide()
|
||||
|
||||
const labels = response.areas.map(area => area.MAKE)
|
||||
const data = response.areas.map(area => area.AVPRICE)
|
||||
|
||||
initGraph(labels, data, type)
|
||||
})
|
||||
}
|
||||
|
||||
const initGraph = (labels, data, type) => {
|
||||
const myCanvas = document.getElementById('myChart')
|
||||
const ctx = myCanvas.getContext('2d')
|
||||
|
||||
const myChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: `Average Invoice Price in USD for ${type} Cars by Manufacturer`,
|
||||
data: data,
|
||||
backgroundColor: 'rgba(255,99,132,0.2)',
|
||||
borderColor: 'rgba(255,99,132,1)',
|
||||
borderWidth: 1,
|
||||
hoverBackgroundColor: 'rgba(255,99,132,0.4)',
|
||||
hoverBorderColor: 'rgba(255,99,132,1)',
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
scales: {yAxes: [{ticks: {beginAtZero: true}}]}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class='container-fluid' style='text-align: center; margin-top: 10px'>
|
||||
<div class='row'>
|
||||
<div class='col-lg-5 col-md-7 col-sm-10 mx-auto mx-auto'>
|
||||
<h1>Demo Seed App for <span class='code'>SASjs</span></h1>
|
||||
<div class='login' id='login-form'>
|
||||
<div class='form-group'>
|
||||
<input class='form-control' type='text' id='username' placeholder='Enter username' />
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<input class='form-control' type='password' id='password' placeholder='Enter password' />
|
||||
</div>
|
||||
<button id='login' onclick='initSasJs()' class='login btn btn-primary' style='margin-bottom: 5px'>Log In</button>
|
||||
</div>
|
||||
<select name='cars' id='cars' style='margin-bottom: 5px; display: none' class='form-control'>
|
||||
<option value='Hybrid'>Hybrid</option>
|
||||
<option value='SUV'>SUV</option>
|
||||
<option value='Sedan'>Sedan</option>
|
||||
<option value='Sports'>Sports</option>
|
||||
<option value='Truck'>Truck</option>
|
||||
<option value='Wagon'>Wagon</option>
|
||||
</select>
|
||||
<button id='getDataBtn' onclick='getData()' style='margin-bottom: 5px; display: none' class='btn btn-success'>Get Data</button>
|
||||
<br>
|
||||
<br>
|
||||
<div id='loading-spinner' class='spinner-border text-primary' role='status' style='display: none'>
|
||||
<span class='sr-only'>Loading...</span>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="password" id="password" placeholder="Enter password" />
|
||||
</div>
|
||||
<button id="login" onclick="initSasJs()" class="login btn btn-primary" style="margin-bottom: 5px;">Log In</button>
|
||||
</div>
|
||||
<select name="cars" id="cars" style="margin-bottom: 5px; display: none;" class="form-control">
|
||||
<option value="Hybrid">Hybrid</option>
|
||||
<option value="SUV">SUV</option>
|
||||
<option value="Sedan">Sedan</option>
|
||||
<option value="Sports">Sports</option>
|
||||
<option value="Truck">Truck</option>
|
||||
<option value="Wagon">Wagon</option>
|
||||
</select>
|
||||
<button id="getdata" onclick="getData()" style="margin-bottom: 5px; display: none;" class="btn btn-success">Get Data</button><br><br>
|
||||
<div id="loading-spinner" class="spinner-border text-primary" role="status" style="display: none;">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div><br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chart-container" style="height: 65vh; width: 100%; position: relative; margin: auto;">
|
||||
<canvas id="myChart" style="display: none;"></canvas>
|
||||
</div>
|
||||
</body>
|
||||
<div id='chart-container' style='height: 65vh; width: 100%; position: relative; margin: auto'>
|
||||
<canvas id='myChart' style='display: none'></canvas>
|
||||
</div>
|
||||
</body>
|
||||
</head>
|
||||
@@ -3,49 +3,49 @@
|
||||
*
|
||||
*/
|
||||
export class SAS9ApiClient {
|
||||
constructor(private serverUrl: string) {}
|
||||
constructor(private serverUrl: string) {}
|
||||
|
||||
/**
|
||||
* returns on object containing the server URL
|
||||
*/
|
||||
public getConfig() {
|
||||
return {
|
||||
serverUrl: this.serverUrl,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @returns an object containing the server URL
|
||||
*/
|
||||
public getConfig() {
|
||||
return {
|
||||
serverUrl: this.serverUrl,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates serverurl which is not null
|
||||
* @param serverUrl - the URL of the server.
|
||||
*/
|
||||
public setConfig(serverUrl: string) {
|
||||
if (serverUrl) this.serverUrl = serverUrl;
|
||||
}
|
||||
/**
|
||||
* Updates serverUrl which is not null
|
||||
* @param serverUrl - the URL of the server.
|
||||
*/
|
||||
public setConfig(serverUrl: string) {
|
||||
if (serverUrl) this.serverUrl = serverUrl
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes code on a SAS9 server.
|
||||
* @param linesOfCode - an array of lines of code to execute
|
||||
* @param serverName - the server to execute the code on
|
||||
* @param repositoryName - the repository to execute the code on
|
||||
*/
|
||||
public async executeScript(
|
||||
linesOfCode: string[],
|
||||
serverName: string,
|
||||
repositoryName: string
|
||||
) {
|
||||
const requestPayload = linesOfCode.join("\n");
|
||||
const executeScriptRequest = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: `command=${requestPayload}`,
|
||||
};
|
||||
const executeScriptResponse = await fetch(
|
||||
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
||||
executeScriptRequest
|
||||
).then((res) => res.text());
|
||||
/**
|
||||
* Executes code on a SAS9 server.
|
||||
* @param linesOfCode - an array of lines of code to execute
|
||||
* @param serverName - the server to execute the code on
|
||||
* @param repositoryName - the repository to execute the code on
|
||||
*/
|
||||
public async executeScript(
|
||||
linesOfCode: string[], // FIXME: rename
|
||||
serverName: string,
|
||||
repositoryName: string
|
||||
) {
|
||||
const requestPayload = linesOfCode.join('\n')
|
||||
const executeScriptRequest = {
|
||||
method: 'PUT',
|
||||
headers: {Accept: 'application/json'},
|
||||
body: `command=${requestPayload}`,
|
||||
}
|
||||
// FIXME: use axios instead of fetch
|
||||
const executeScriptResponse = await fetch(
|
||||
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
||||
executeScriptRequest
|
||||
).then((res) => res.text())
|
||||
// FIXME: no catch block
|
||||
|
||||
return executeScriptResponse;
|
||||
}
|
||||
}
|
||||
return executeScriptResponse
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* TODO: needs to be split into logical blocks:
|
||||
* - Folder
|
||||
* - Config
|
||||
* - Context
|
||||
* - Session
|
||||
* - Job
|
||||
* - Auth
|
||||
*/
|
||||
|
||||
import {
|
||||
isAuthorizeFormRequired,
|
||||
parseAndSubmitAuthorizeForm,
|
||||
@@ -32,7 +42,7 @@ export class SASViyaApiClient {
|
||||
if (this.rootFolderMap.size) {
|
||||
return this.rootFolderMap;
|
||||
}
|
||||
|
||||
|
||||
this.populateRootFolderMap();
|
||||
return this.rootFolderMap;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import SASjs from "./index";
|
||||
|
||||
const adapter = new SASjs();
|
||||
|
||||
// FIXME: adapter doesn't have 'parseSAS9SourceCode' and 'parseGeneratedCode'
|
||||
it("should parse SAS9 source code", async done => {
|
||||
expect(sampleResponse).toBeTruthy();
|
||||
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse);
|
||||
|
||||
16
src/SASjs.ts
16
src/SASjs.ts
@@ -1,3 +1,16 @@
|
||||
/**
|
||||
* TODO: needs to be split into logical blocks:
|
||||
* - Execute
|
||||
* - Context
|
||||
* - Session
|
||||
* - Folder and services
|
||||
* - Job
|
||||
* - Auth
|
||||
* - Config
|
||||
* - Debug
|
||||
* - Response
|
||||
*/
|
||||
|
||||
import "isomorphic-fetch";
|
||||
import * as e6p from "es6-promise";
|
||||
(e6p as any).polyfill();
|
||||
@@ -839,6 +852,7 @@ export default class SASjs {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this method never used
|
||||
private fetchLogFileContent(logLink: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(logLink, {
|
||||
@@ -1038,4 +1052,4 @@ export default class SASjs {
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/index.ts
12
src/index.ts
@@ -1,5 +1,7 @@
|
||||
import SASjs from "./SASjs";
|
||||
export * from "./types";
|
||||
export * from "./SASViyaApiClient";
|
||||
export * from "./SAS9ApiClient";
|
||||
export default SASjs;
|
||||
import SASjs from './SASjs'
|
||||
|
||||
export * from './types'
|
||||
export * from './SASViyaApiClient'
|
||||
export * from './SAS9ApiClient'
|
||||
|
||||
export default SASjs
|
||||
@@ -3,4 +3,4 @@ export interface Context {
|
||||
id: string;
|
||||
createdBy: string;
|
||||
version: number;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface CsrfToken {
|
||||
headerName: string;
|
||||
value: string;
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,4 @@ export interface Folder {
|
||||
id: string;
|
||||
uri: string;
|
||||
links: Link[];
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ export interface Job {
|
||||
createdBy: string;
|
||||
links: Link[];
|
||||
results: JobResult;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export interface JobResult {
|
||||
"_webout.json": string;
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,4 @@ export interface Link {
|
||||
href: string;
|
||||
uri: string;
|
||||
type: string;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export class SASjsConfig {
|
||||
* Can be omitted, eg if serving directly from the SAS Web Server or being
|
||||
* streamed.
|
||||
*/
|
||||
// TODO: we should clarify what location we are talking about
|
||||
serverUrl: string = "";
|
||||
pathSAS9: string = "";
|
||||
pathSASViya: string = "";
|
||||
|
||||
@@ -9,4 +9,4 @@ export interface SASjsRequest {
|
||||
generatedCode: string;
|
||||
logFile: string;
|
||||
SASWORK: any;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
* Represents requests that are queued, pending a signon event
|
||||
*
|
||||
*/
|
||||
|
||||
// FIXME: be more specific on type declaration
|
||||
export interface SASjsWaitingRequest {
|
||||
requestPromise: {
|
||||
promise: any;
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
export enum ServerType {
|
||||
SASViya = "SASVIYA",
|
||||
SAS9 = "SAS9",
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export interface Session {
|
||||
id: string;
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,4 @@ export * from "./SASjsConfig";
|
||||
export * from "./SASjsRequest";
|
||||
export * from "./SASjsWaitingRequest";
|
||||
export * from "./ServerType";
|
||||
export * from "./Session";
|
||||
export * from "./Session";
|
||||
@@ -1,5 +1,6 @@
|
||||
// FIXME: use ES6
|
||||
export async function asyncForEach(array: any[], callback: any) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { SASjsRequest } from "../types/SASjsRequest";
|
||||
|
||||
/**
|
||||
* Comparator for SASjs request timestamps
|
||||
*
|
||||
*/
|
||||
export const compareTimestamps = (a: SASjsRequest, b: SASjsRequest) => {
|
||||
return b.timestamp.getTime() - a.timestamp.getTime();
|
||||
|
||||
@@ -3,43 +3,43 @@
|
||||
* @param data - the JSON object to convert.
|
||||
*/
|
||||
export const convertToCSV = (data: any) => {
|
||||
const replacer = (key: any, value: any) => (value === null ? "" : value);
|
||||
const headerFields = Object.keys(data[0]);
|
||||
const replacer = (key: any, value: any) => (value === null ? "" : value); // FIXME: 'key' parameter was not used, why do we compare with null (undefined, NaN)?
|
||||
const headerFields = Object.keys(data[0]); // FIXME: data can be of any type, but we are working with it as with object
|
||||
let csvTest;
|
||||
let invalidString = false;
|
||||
const headers = headerFields.map((field) => {
|
||||
let firstFoundType: string | null = null;
|
||||
let hasMixedTypes: boolean = false;
|
||||
let rowNumError: number = -1;
|
||||
let hasMixedTypes: boolean = false; // FIXME: unnecessary type declaration
|
||||
let rowNumError: number = -1; // FIXME: unnecessary type declaration
|
||||
|
||||
const longestValueForField = data
|
||||
.map((row: any, index: number) => {
|
||||
.map((row: any, index: number) => { // FIXME: row should be of type string | number
|
||||
if (row[field] || row[field] === "") {
|
||||
if (firstFoundType) {
|
||||
let currentFieldType =
|
||||
row[field] === "" || typeof row[field] === "string"
|
||||
let currentFieldType = // FIXME: use const
|
||||
row[field] === "" || typeof row[field] === "string" // FIXME: "" is also of type string
|
||||
? "chars"
|
||||
: "number";
|
||||
|
||||
if (!hasMixedTypes) {
|
||||
hasMixedTypes = currentFieldType !== firstFoundType;
|
||||
rowNumError = hasMixedTypes ? index + 1 : -1;
|
||||
rowNumError = hasMixedTypes ? index + 1 : -1; // TODO: refactor
|
||||
}
|
||||
} else {
|
||||
if (row[field] === "") {
|
||||
firstFoundType = "chars";
|
||||
} else {
|
||||
firstFoundType =
|
||||
typeof row[field] === "string" ? "chars" : "number";
|
||||
typeof row[field] === "string" ? "chars" : "number"; // TODO: refactor
|
||||
}
|
||||
}
|
||||
|
||||
let byteSize;
|
||||
|
||||
if (typeof row[field] === "string") {
|
||||
let doubleQuotesFound = row[field]
|
||||
let doubleQuotesFound = row[field] // FIXME: use const
|
||||
.split("")
|
||||
.filter((char: any) => char === '"');
|
||||
.filter((char: any) => char === '"'); // FIXME: why char is of type any?
|
||||
|
||||
byteSize = getByteSize(row[field]);
|
||||
|
||||
@@ -52,16 +52,18 @@ export const convertToCSV = (data: any) => {
|
||||
}
|
||||
})
|
||||
.sort((a: number, b: number) => b - a)[0];
|
||||
if (longestValueForField && longestValueForField > 32765) {
|
||||
|
||||
if (longestValueForField && longestValueForField > 32765) { // FIXME: longestValueForField is an array and it is not comparable to a number
|
||||
invalidString = true;
|
||||
}
|
||||
|
||||
if (hasMixedTypes) {
|
||||
console.error(
|
||||
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
|
||||
);
|
||||
}
|
||||
|
||||
return `${field}:${firstFoundType === "chars" ? "$" : ""}${
|
||||
return `${field}:${firstFoundType === "chars" ? "$" : ""}${ // TODO: format return string before return statement
|
||||
longestValueForField
|
||||
? longestValueForField
|
||||
: firstFoundType === "chars"
|
||||
@@ -73,10 +75,11 @@ export const convertToCSV = (data: any) => {
|
||||
if (invalidString) {
|
||||
return "ERROR: LARGE STRING LENGTH";
|
||||
}
|
||||
|
||||
csvTest = data.map((row: any) => {
|
||||
const fields = Object.keys(row).map((fieldName, index) => {
|
||||
let value;
|
||||
let containsSpecialChar = false;
|
||||
let containsSpecialChar = false; // FIXME: should be const
|
||||
const currentCell = row[fieldName];
|
||||
|
||||
if (JSON.stringify(currentCell).search(/(\\t|\\n|\\r)/gm) > -1) {
|
||||
@@ -89,7 +92,7 @@ export const convertToCSV = (data: any) => {
|
||||
value = value.replace(/\\\\/gm, "\\");
|
||||
|
||||
if (containsSpecialChar) {
|
||||
if (value.includes(",") || value.includes('"')) {
|
||||
if (value.includes(",") || value.includes('"')) { // FIXME: use `"`
|
||||
value = '"' + value + '"';
|
||||
}
|
||||
} else {
|
||||
@@ -112,6 +115,7 @@ export const convertToCSV = (data: any) => {
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
return fields.join(",");
|
||||
});
|
||||
|
||||
@@ -121,6 +125,7 @@ export const convertToCSV = (data: any) => {
|
||||
return finalCSV;
|
||||
};
|
||||
|
||||
// TODO: refactor
|
||||
const getByteSize = (str: string) => {
|
||||
let byteSize = str.length;
|
||||
for (let i = str.length - 1; i >= 0; i--) {
|
||||
@@ -130,4 +135,4 @@ const getByteSize = (str: string) => {
|
||||
if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate
|
||||
}
|
||||
return byteSize;
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,3 @@
|
||||
export const isAuthorizeFormRequired = (response: string): boolean => {
|
||||
return /<form.+action="(.*Logon\/oauth\/authorize[^"]*).*>/gm.test(response);
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
export const isLogInRequired = (response: string): boolean => {
|
||||
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm;
|
||||
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm; // FIXME: unnecessary type declaration
|
||||
const matches = pattern.test(response);
|
||||
|
||||
return matches;
|
||||
};
|
||||
};
|
||||
@@ -1,2 +1 @@
|
||||
export const isLogInSuccess = (response: string): boolean =>
|
||||
/You have signed in/gm.test(response);
|
||||
export const isLogInSuccess = (response: string): boolean => /You have signed in/gm.test(response); // TODO: maybe we have more reliable way to verify that login was successful?
|
||||
@@ -2,30 +2,33 @@ import { CsrfToken } from "../types";
|
||||
|
||||
export async function makeRequest<T>(
|
||||
url: string,
|
||||
request: RequestInit,
|
||||
request: RequestInit, // Where 'RequestInit' is coming from?
|
||||
callback: (value: CsrfToken) => any,
|
||||
contentType: "text" | "json" = "json"
|
||||
): Promise<T> {
|
||||
const responseTransform =
|
||||
contentType === "json"
|
||||
? (res: Response) => res.json()
|
||||
: (res: Response) => res.text();
|
||||
const result = await fetch(url, request).then((response) => {
|
||||
if (!response.ok) {
|
||||
const responseTransform = contentType === "json" ?
|
||||
(res: Response) => res.json()
|
||||
:
|
||||
(res: Response) => res.text();
|
||||
|
||||
const result = await fetch(url, request).then((response) => { // FIXME: use axios instead of fetch
|
||||
if (!response.ok) { // FIXME we can just check if status === 403
|
||||
if (response.status === 403) {
|
||||
const tokenHeader = response.headers.get("X-CSRF-HEADER");
|
||||
|
||||
if (tokenHeader) {
|
||||
const token = response.headers.get(tokenHeader);
|
||||
const token = response.headers.get(tokenHeader) || ''; // TODO: refactor
|
||||
|
||||
callback({
|
||||
headerName: tokenHeader,
|
||||
value: token || "",
|
||||
value: token,
|
||||
});
|
||||
|
||||
const retryRequest = {
|
||||
...request,
|
||||
headers: { ...request.headers, [tokenHeader]: token },
|
||||
};
|
||||
|
||||
return fetch(url, retryRequest).then(responseTransform);
|
||||
}
|
||||
}
|
||||
@@ -33,5 +36,6 @@ export async function makeRequest<T>(
|
||||
return responseTransform(response);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// TODO: refactor
|
||||
export const needsRetry = (responseText: string): boolean => {
|
||||
return (
|
||||
(responseText.includes('"errorCode":403') &&
|
||||
@@ -8,4 +9,4 @@ export const needsRetry = (responseText: string): boolean => {
|
||||
(responseText.includes('"status":449') &&
|
||||
responseText.includes("Authentication success, retry original request"))
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -6,7 +6,7 @@ export const parseAndSubmitAuthorizeForm = async (
|
||||
const params: any = {};
|
||||
|
||||
const responseBody = response.split("<body>")[1].split("</body>")[0];
|
||||
const bodyElement = document.createElement("div");
|
||||
const bodyElement = document.createElement("div"); // TODO: rename
|
||||
bodyElement.innerHTML = responseBody;
|
||||
|
||||
const form = bodyElement.querySelector("#application_authorization");
|
||||
@@ -24,7 +24,7 @@ export const parseAndSubmitAuthorizeForm = async (
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
for (const key in params) {
|
||||
for (const key in params) { // TODO: use forEach
|
||||
if (params.hasOwnProperty(key)) {
|
||||
formData.append(key, params[key]);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export const parseAndSubmitAuthorizeForm = async (
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (authUrl) {
|
||||
fetch(authUrl, {
|
||||
fetch(authUrl, { // TODO use axios instead of fetch
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: formData,
|
||||
@@ -46,4 +46,4 @@ export const parseAndSubmitAuthorizeForm = async (
|
||||
reject("Auth form url is null");
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
export const parseGeneratedCode = (log: string) => {
|
||||
const startsWith = "MPRINT";
|
||||
const isGeneratedCodeLine = (line: string) =>
|
||||
line.trim().startsWith(startsWith);
|
||||
const isGeneratedCodeLine = (line: string) => line.trim().startsWith(startsWith);
|
||||
const logLines = log.split("\n").filter(isGeneratedCodeLine);
|
||||
|
||||
return logLines.join("\r\n");
|
||||
};
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
export const parseSasViyaLog = (logResponse: { items: any[] }) => {
|
||||
export const parseSasViyaLog = (logResponse: { items: any[] }) => { // TODO: be more specific on type declaration
|
||||
let log;
|
||||
|
||||
try {
|
||||
log = logResponse.items
|
||||
? logResponse.items.map((i) => i.line).join("\n")
|
||||
: JSON.stringify(logResponse);
|
||||
} catch (e) {
|
||||
} catch (e) { // TODO: rename parameter to err or error
|
||||
console.error("An error has occurred while parsing the log response", e);
|
||||
|
||||
log = logResponse;
|
||||
}
|
||||
|
||||
return log;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const parseSourceCode = (log: string): string => {
|
||||
const isSourceCodeLine = (line: string) =>
|
||||
line.trim().substring(0, 10).trimStart().match(/^\d/);
|
||||
const isSourceCodeLine = (line: string) => line.trim().substring(0, 10).trimStart().match(/^\d/);
|
||||
const logLines = log.split("\n").filter(isSourceCodeLine);
|
||||
|
||||
return logLines.join("\r\n");
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export const serialize = (obj: any) => {
|
||||
export const serialize = (obj: any) => { // TODO: be more specific on type declaration
|
||||
const str: any[] = [];
|
||||
for (const p in obj) {
|
||||
|
||||
for (const p in obj) { // FIXME: name variables properly
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
if (obj[p] instanceof Array) {
|
||||
for (let i = 0, n = obj[p].length; i < n; i++) {
|
||||
@@ -11,5 +12,6 @@ export const serialize = (obj: any) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str.join("&");
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user