mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-17 17:10:05 +00:00
Merge pull request #57 from sasjs/change-code-style
chore(*): change code style to remove semicolons
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"semi": true,
|
"semi": false,
|
||||||
"singleQuote": false
|
"singleQuote": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { isLogInRequired, needsRetry } from "./utils";
|
import { isLogInRequired, needsRetry } from "./utils"
|
||||||
import { CsrfToken } from "./types/CsrfToken";
|
import { CsrfToken } from "./types/CsrfToken"
|
||||||
import { UploadFile } from "./types/UploadFile";
|
import { UploadFile } from "./types/UploadFile"
|
||||||
|
|
||||||
const requestRetryLimit = 5;
|
const requestRetryLimit = 5
|
||||||
|
|
||||||
export class FileUploader {
|
export class FileUploader {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -12,38 +12,38 @@ export class FileUploader {
|
|||||||
private setCsrfTokenWeb: any,
|
private setCsrfTokenWeb: any,
|
||||||
private csrfToken: CsrfToken | null = null
|
private csrfToken: CsrfToken | null = null
|
||||||
) {}
|
) {}
|
||||||
private retryCount = 0;
|
private retryCount = 0
|
||||||
|
|
||||||
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
|
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
|
||||||
if (files?.length < 1) throw new Error("Atleast one file must be provided");
|
if (files?.length < 1) throw new Error("Atleast one file must be provided")
|
||||||
|
|
||||||
let paramsString = "";
|
let paramsString = ""
|
||||||
|
|
||||||
for (let param in params) {
|
for (let param in params) {
|
||||||
if (params.hasOwnProperty(param)) {
|
if (params.hasOwnProperty(param)) {
|
||||||
paramsString += `&${param}=${params[param]}`;
|
paramsString += `&${param}=${params[param]}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const program = this.appLoc
|
const program = this.appLoc
|
||||||
? this.appLoc.replace(/\/?$/, "/") + sasJob.replace(/^\//, "")
|
? this.appLoc.replace(/\/?$/, "/") + sasJob.replace(/^\//, "")
|
||||||
: sasJob;
|
: sasJob
|
||||||
const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
|
const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
|
||||||
"_program=" + program
|
"_program=" + program
|
||||||
}${paramsString}`;
|
}${paramsString}`
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
"cache-control": "no-cache"
|
"cache-control": "no-cache"
|
||||||
};
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const formData = new FormData();
|
const formData = new FormData()
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
formData.append("file", file.file, file.fileName);
|
formData.append("file", file.file, file.fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.csrfToken) formData.append("_csrf", this.csrfToken.value);
|
if (this.csrfToken) formData.append("_csrf", this.csrfToken.value)
|
||||||
|
|
||||||
fetch(uploadUrl, {
|
fetch(uploadUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -54,47 +54,47 @@ export class FileUploader {
|
|||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
const tokenHeader = response.headers.get("X-CSRF-HEADER");
|
const tokenHeader = response.headers.get("X-CSRF-HEADER")
|
||||||
|
|
||||||
if (tokenHeader) {
|
if (tokenHeader) {
|
||||||
const token = response.headers.get(tokenHeader);
|
const token = response.headers.get(tokenHeader)
|
||||||
this.csrfToken = {
|
this.csrfToken = {
|
||||||
headerName: tokenHeader,
|
headerName: tokenHeader,
|
||||||
value: token || ""
|
value: token || ""
|
||||||
};
|
}
|
||||||
|
|
||||||
this.setCsrfTokenWeb(this.csrfToken);
|
this.setCsrfTokenWeb(this.csrfToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.text();
|
return response.text()
|
||||||
})
|
})
|
||||||
.then((responseText) => {
|
.then((responseText) => {
|
||||||
if (isLogInRequired(responseText))
|
if (isLogInRequired(responseText))
|
||||||
reject("You must be logged in to upload a fle");
|
reject("You must be logged in to upload a fle")
|
||||||
|
|
||||||
if (needsRetry(responseText)) {
|
if (needsRetry(responseText)) {
|
||||||
if (this.retryCount < requestRetryLimit) {
|
if (this.retryCount < requestRetryLimit) {
|
||||||
this.retryCount++;
|
this.retryCount++
|
||||||
this.uploadFile(sasJob, files, params).then(
|
this.uploadFile(sasJob, files, params).then(
|
||||||
(res: any) => resolve(res),
|
(res: any) => resolve(res),
|
||||||
(err: any) => reject(err)
|
(err: any) => reject(err)
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
this.retryCount = 0;
|
this.retryCount = 0
|
||||||
reject(responseText);
|
reject(responseText)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.retryCount = 0;
|
this.retryCount = 0
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resolve(JSON.parse(responseText));
|
resolve(JSON.parse(responseText))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class SAS9ApiClient {
|
|||||||
public getConfig() {
|
public getConfig() {
|
||||||
return {
|
return {
|
||||||
serverUrl: this.serverUrl
|
serverUrl: this.serverUrl
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,7 +19,7 @@ export class SAS9ApiClient {
|
|||||||
* @param serverUrl - the URL of the server.
|
* @param serverUrl - the URL of the server.
|
||||||
*/
|
*/
|
||||||
public setConfig(serverUrl: string) {
|
public setConfig(serverUrl: string) {
|
||||||
if (serverUrl) this.serverUrl = serverUrl;
|
if (serverUrl) this.serverUrl = serverUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,19 +33,19 @@ export class SAS9ApiClient {
|
|||||||
serverName: string,
|
serverName: string,
|
||||||
repositoryName: string
|
repositoryName: string
|
||||||
) {
|
) {
|
||||||
const requestPayload = linesOfCode.join("\n");
|
const requestPayload = linesOfCode.join("\n")
|
||||||
const executeScriptRequest = {
|
const executeScriptRequest = {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json"
|
Accept: "application/json"
|
||||||
},
|
},
|
||||||
body: `command=${requestPayload}`
|
body: `command=${requestPayload}`
|
||||||
};
|
}
|
||||||
const executeScriptResponse = await fetch(
|
const executeScriptResponse = await fetch(
|
||||||
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
||||||
executeScriptRequest
|
executeScriptRequest
|
||||||
).then((res) => res.text());
|
).then((res) => res.text())
|
||||||
|
|
||||||
return executeScriptResponse;
|
return executeScriptResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,36 @@
|
|||||||
import SASjs from "./index";
|
import SASjs from "./index"
|
||||||
|
|
||||||
const adapter = new SASjs();
|
const adapter = new SASjs()
|
||||||
|
|
||||||
it("should parse SAS9 source code", async (done) => {
|
it("should parse SAS9 source code", async (done) => {
|
||||||
expect(sampleResponse).toBeTruthy();
|
expect(sampleResponse).toBeTruthy()
|
||||||
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse);
|
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse)
|
||||||
expect(parsedSourceCode).toBeTruthy();
|
expect(parsedSourceCode).toBeTruthy()
|
||||||
const sourceCodeLines = parsedSourceCode.split("\r\n");
|
const sourceCodeLines = parsedSourceCode.split("\r\n")
|
||||||
expect(sourceCodeLines.length).toEqual(5);
|
expect(sourceCodeLines.length).toEqual(5)
|
||||||
expect(sourceCodeLines[0].startsWith("6")).toBeTruthy();
|
expect(sourceCodeLines[0].startsWith("6")).toBeTruthy()
|
||||||
expect(sourceCodeLines[1].startsWith("7")).toBeTruthy();
|
expect(sourceCodeLines[1].startsWith("7")).toBeTruthy()
|
||||||
expect(sourceCodeLines[2].startsWith("8")).toBeTruthy();
|
expect(sourceCodeLines[2].startsWith("8")).toBeTruthy()
|
||||||
expect(sourceCodeLines[3].startsWith("9")).toBeTruthy();
|
expect(sourceCodeLines[3].startsWith("9")).toBeTruthy()
|
||||||
expect(sourceCodeLines[4].startsWith("10")).toBeTruthy();
|
expect(sourceCodeLines[4].startsWith("10")).toBeTruthy()
|
||||||
done();
|
done()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should parse generated code", async (done) => {
|
it("should parse generated code", async (done) => {
|
||||||
expect(sampleResponse).toBeTruthy();
|
expect(sampleResponse).toBeTruthy()
|
||||||
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
|
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
|
||||||
sampleResponse
|
sampleResponse
|
||||||
);
|
)
|
||||||
expect(parsedGeneratedCode).toBeTruthy();
|
expect(parsedGeneratedCode).toBeTruthy()
|
||||||
const generatedCodeLines = parsedGeneratedCode.split("\r\n");
|
const generatedCodeLines = parsedGeneratedCode.split("\r\n")
|
||||||
expect(generatedCodeLines.length).toEqual(5);
|
expect(generatedCodeLines.length).toEqual(5)
|
||||||
expect(generatedCodeLines[0].startsWith("MPRINT(MM_WEBIN)")).toBeTruthy();
|
expect(generatedCodeLines[0].startsWith("MPRINT(MM_WEBIN)")).toBeTruthy()
|
||||||
expect(generatedCodeLines[1].startsWith("MPRINT(MM_WEBLEFT)")).toBeTruthy();
|
expect(generatedCodeLines[1].startsWith("MPRINT(MM_WEBLEFT)")).toBeTruthy()
|
||||||
expect(generatedCodeLines[2].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
|
expect(generatedCodeLines[2].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy()
|
||||||
expect(generatedCodeLines[3].startsWith("MPRINT(MM_WEBRIGHT)")).toBeTruthy();
|
expect(generatedCodeLines[3].startsWith("MPRINT(MM_WEBRIGHT)")).toBeTruthy()
|
||||||
expect(generatedCodeLines[4].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
|
expect(generatedCodeLines[4].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy()
|
||||||
done();
|
done()
|
||||||
});
|
})
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
const sampleResponse = `<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
|
const sampleResponse = `<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
|
||||||
@@ -44,5 +44,5 @@ MPRINT(MM_WEBLEFT): filename _temp temp lrecl=999999;
|
|||||||
MPRINT(MM_WEBOUT): data _null_;
|
MPRINT(MM_WEBOUT): data _null_;
|
||||||
MPRINT(MM_WEBRIGHT): file _temp;
|
MPRINT(MM_WEBRIGHT): file _temp;
|
||||||
MPRINT(MM_WEBOUT): if upcase(symget('_debug'))='LOG' then put '>>weboutBEGIN<<';
|
MPRINT(MM_WEBOUT): if upcase(symget('_debug'))='LOG' then put '>>weboutBEGIN<<';
|
||||||
`;
|
`
|
||||||
/* tslint:enable */
|
/* tslint:enable */
|
||||||
|
|||||||
656
src/SASjs.ts
656
src/SASjs.ts
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
import { Session, Context, CsrfToken } from "./types";
|
import { Session, Context, CsrfToken } from "./types"
|
||||||
import { asyncForEach, makeRequest } from "./utils";
|
import { asyncForEach, makeRequest } from "./utils"
|
||||||
|
|
||||||
const MAX_SESSION_COUNT = 1;
|
const MAX_SESSION_COUNT = 1
|
||||||
|
|
||||||
export class SessionManager {
|
export class SessionManager {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -9,49 +9,49 @@ export class SessionManager {
|
|||||||
private contextName: string,
|
private contextName: string,
|
||||||
private setCsrfToken: (csrfToken: CsrfToken) => void
|
private setCsrfToken: (csrfToken: CsrfToken) => void
|
||||||
) {}
|
) {}
|
||||||
private sessions: Session[] = [];
|
private sessions: Session[] = []
|
||||||
private currentContext: Context | null = null;
|
private currentContext: Context | null = null
|
||||||
private csrfToken: CsrfToken | null = null;
|
private csrfToken: CsrfToken | null = null
|
||||||
|
|
||||||
async getSession(accessToken?: string) {
|
async getSession(accessToken?: string) {
|
||||||
await this.createSessions(accessToken);
|
await this.createSessions(accessToken)
|
||||||
this.createAndWaitForSession(accessToken);
|
this.createAndWaitForSession(accessToken)
|
||||||
const session = this.sessions.pop();
|
const session = this.sessions.pop()
|
||||||
const secondsSinceSessionCreation =
|
const secondsSinceSessionCreation =
|
||||||
(new Date().getTime() - new Date(session!.creationTimeStamp).getTime()) /
|
(new Date().getTime() - new Date(session!.creationTimeStamp).getTime()) /
|
||||||
1000;
|
1000
|
||||||
if (
|
if (
|
||||||
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
|
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
|
||||||
) {
|
) {
|
||||||
await this.createSessions(accessToken);
|
await this.createSessions(accessToken)
|
||||||
const freshSession = this.sessions.pop();
|
const freshSession = this.sessions.pop()
|
||||||
return freshSession;
|
return freshSession
|
||||||
}
|
}
|
||||||
return session;
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
async clearSession(id: string, accessToken?: string) {
|
async clearSession(id: string, accessToken?: string) {
|
||||||
const deleteSessionRequest = {
|
const deleteSessionRequest = {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: this.getHeaders(accessToken)
|
headers: this.getHeaders(accessToken)
|
||||||
};
|
}
|
||||||
return await this.request<Session>(
|
return await this.request<Session>(
|
||||||
`${this.serverUrl}/compute/sessions/${id}`,
|
`${this.serverUrl}/compute/sessions/${id}`,
|
||||||
deleteSessionRequest
|
deleteSessionRequest
|
||||||
).then(() => {
|
).then(() => {
|
||||||
this.sessions = this.sessions.filter((s) => s.id !== id);
|
this.sessions = this.sessions.filter((s) => s.id !== id)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSessions(accessToken?: string) {
|
private async createSessions(accessToken?: string) {
|
||||||
if (!this.sessions.length) {
|
if (!this.sessions.length) {
|
||||||
if (!this.currentContext) {
|
if (!this.currentContext) {
|
||||||
await this.setCurrentContext(accessToken);
|
await this.setCurrentContext(accessToken)
|
||||||
}
|
}
|
||||||
await asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
|
await asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
|
||||||
const createdSession = await this.createAndWaitForSession(accessToken);
|
const createdSession = await this.createAndWaitForSession(accessToken)
|
||||||
this.sessions.push(createdSession);
|
this.sessions.push(createdSession)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,53 +59,53 @@ export class SessionManager {
|
|||||||
const createSessionRequest = {
|
const createSessionRequest = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: this.getHeaders(accessToken)
|
headers: this.getHeaders(accessToken)
|
||||||
};
|
}
|
||||||
const { result: createdSession, etag } = await this.request<Session>(
|
const { result: createdSession, etag } = await this.request<Session>(
|
||||||
`${this.serverUrl}/compute/contexts/${this.currentContext!.id}/sessions`,
|
`${this.serverUrl}/compute/contexts/${this.currentContext!.id}/sessions`,
|
||||||
createSessionRequest
|
createSessionRequest
|
||||||
);
|
)
|
||||||
|
|
||||||
await this.waitForSession(createdSession, etag);
|
await this.waitForSession(createdSession, etag)
|
||||||
this.sessions.push(createdSession);
|
this.sessions.push(createdSession)
|
||||||
return createdSession;
|
return createdSession
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setCurrentContext(accessToken?: string) {
|
private async setCurrentContext(accessToken?: string) {
|
||||||
if (!this.currentContext) {
|
if (!this.currentContext) {
|
||||||
const { result: contexts } = await this.request<{
|
const { result: contexts } = await this.request<{
|
||||||
items: Context[];
|
items: Context[]
|
||||||
}>(`${this.serverUrl}/compute/contexts`, {
|
}>(`${this.serverUrl}/compute/contexts`, {
|
||||||
headers: this.getHeaders(accessToken)
|
headers: this.getHeaders(accessToken)
|
||||||
});
|
})
|
||||||
|
|
||||||
const contextsList =
|
const contextsList =
|
||||||
contexts && contexts.items && contexts.items.length
|
contexts && contexts.items && contexts.items.length
|
||||||
? contexts.items
|
? contexts.items
|
||||||
: [];
|
: []
|
||||||
|
|
||||||
const currentContext = contextsList.find(
|
const currentContext = contextsList.find(
|
||||||
(c: any) => c.name === this.contextName
|
(c: any) => c.name === this.contextName
|
||||||
);
|
)
|
||||||
|
|
||||||
if (!currentContext) {
|
if (!currentContext) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The context ${this.contextName} was not found on the server ${this.serverUrl}`
|
`The context ${this.contextName} was not found on the server ${this.serverUrl}`
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentContext = currentContext;
|
this.currentContext = currentContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getHeaders(accessToken?: string) {
|
private getHeaders(accessToken?: string) {
|
||||||
const headers: any = {
|
const headers: any = {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
};
|
}
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
headers.Authorization = `Bearer ${accessToken}`;
|
headers.Authorization = `Bearer ${accessToken}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers;
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
private async waitForSession(
|
private async waitForSession(
|
||||||
@@ -114,17 +114,17 @@ export class SessionManager {
|
|||||||
accessToken?: string,
|
accessToken?: string,
|
||||||
silent = false
|
silent = false
|
||||||
) {
|
) {
|
||||||
let sessionState = session.state;
|
let sessionState = session.state
|
||||||
const headers: any = {
|
const headers: any = {
|
||||||
...this.getHeaders(accessToken),
|
...this.getHeaders(accessToken),
|
||||||
"If-None-Match": etag
|
"If-None-Match": etag
|
||||||
};
|
}
|
||||||
const stateLink = session.links.find((l: any) => l.rel === "state");
|
const stateLink = session.links.find((l: any) => l.rel === "state")
|
||||||
return new Promise(async (resolve, _) => {
|
return new Promise(async (resolve, _) => {
|
||||||
if (sessionState === "pending") {
|
if (sessionState === "pending") {
|
||||||
if (stateLink) {
|
if (stateLink) {
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
console.log("Polling session status... \n");
|
console.log("Polling session status... \n")
|
||||||
}
|
}
|
||||||
const { result: state } = await this.request<string>(
|
const { result: state } = await this.request<string>(
|
||||||
`${this.serverUrl}${stateLink.href}?wait=30`,
|
`${this.serverUrl}${stateLink.href}?wait=30`,
|
||||||
@@ -132,18 +132,18 @@ export class SessionManager {
|
|||||||
headers
|
headers
|
||||||
},
|
},
|
||||||
"text"
|
"text"
|
||||||
);
|
)
|
||||||
|
|
||||||
sessionState = state.trim();
|
sessionState = state.trim()
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
console.log(`Current state: ${sessionState}\n`);
|
console.log(`Current state: ${sessionState}\n`)
|
||||||
}
|
}
|
||||||
resolve(sessionState);
|
resolve(sessionState)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve(sessionState);
|
resolve(sessionState)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async request<T>(
|
private async request<T>(
|
||||||
@@ -155,16 +155,16 @@ export class SessionManager {
|
|||||||
options.headers = {
|
options.headers = {
|
||||||
...options.headers,
|
...options.headers,
|
||||||
[this.csrfToken.headerName]: this.csrfToken.value
|
[this.csrfToken.headerName]: this.csrfToken.value
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
return await makeRequest<T>(
|
return await makeRequest<T>(
|
||||||
url,
|
url,
|
||||||
options,
|
options,
|
||||||
(token) => {
|
(token) => {
|
||||||
this.csrfToken = token;
|
this.csrfToken = token
|
||||||
this.setCsrfToken(token);
|
this.setCsrfToken(token)
|
||||||
},
|
},
|
||||||
contentType
|
contentType
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/index.ts
10
src/index.ts
@@ -1,5 +1,5 @@
|
|||||||
import SASjs from "./SASjs";
|
import SASjs from "./SASjs"
|
||||||
export * from "./types";
|
export * from "./types"
|
||||||
export * from "./SASViyaApiClient";
|
export * from "./SASViyaApiClient"
|
||||||
export * from "./SAS9ApiClient";
|
export * from "./SAS9ApiClient"
|
||||||
export default SASjs;
|
export default SASjs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface Context {
|
export interface Context {
|
||||||
name: string;
|
name: string
|
||||||
id: string;
|
id: string
|
||||||
createdBy: string;
|
createdBy: string
|
||||||
version: number;
|
version: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface CsrfToken {
|
export interface CsrfToken {
|
||||||
headerName: string;
|
headerName: string
|
||||||
value: string;
|
value: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from "./Link"
|
||||||
|
|
||||||
export interface Folder {
|
export interface Folder {
|
||||||
id: string;
|
id: string
|
||||||
uri: string;
|
uri: string
|
||||||
links: Link[];
|
links: Link[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from "./Link"
|
||||||
import { JobResult } from "./JobResult";
|
import { JobResult } from "./JobResult"
|
||||||
|
|
||||||
export interface Job {
|
export interface Job {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
uri: string;
|
uri: string
|
||||||
createdBy: string;
|
createdBy: string
|
||||||
code?: string;
|
code?: string
|
||||||
links: Link[];
|
links: Link[]
|
||||||
results: JobResult;
|
results: JobResult
|
||||||
error?: any;
|
error?: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export interface JobDefinition {
|
export interface JobDefinition {
|
||||||
code: string;
|
code: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export interface JobResult {
|
export interface JobResult {
|
||||||
"_webout.json": string;
|
"_webout.json": string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export interface Link {
|
export interface Link {
|
||||||
method: string;
|
method: string
|
||||||
rel: string;
|
rel: string
|
||||||
href: string;
|
href: string
|
||||||
uri: string;
|
uri: string
|
||||||
type: string;
|
type: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ServerType } from "./ServerType";
|
import { ServerType } from "./ServerType"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the configuration for the SASjs instance.
|
* Specifies the configuration for the SASjs instance.
|
||||||
@@ -10,22 +10,22 @@ export class SASjsConfig {
|
|||||||
* Can be omitted, eg if serving directly from the SAS Web Server or being
|
* Can be omitted, eg if serving directly from the SAS Web Server or being
|
||||||
* streamed.
|
* streamed.
|
||||||
*/
|
*/
|
||||||
serverUrl: string = "";
|
serverUrl: string = ""
|
||||||
pathSAS9: string = "";
|
pathSAS9: string = ""
|
||||||
pathSASViya: string = "";
|
pathSASViya: string = ""
|
||||||
/**
|
/**
|
||||||
* The appLoc is the parent folder under which the SAS services (STPs or Job
|
* The appLoc is the parent folder under which the SAS services (STPs or Job
|
||||||
* Execution Services) are stored.
|
* Execution Services) are stored.
|
||||||
*/
|
*/
|
||||||
appLoc: string = "";
|
appLoc: string = ""
|
||||||
/**
|
/**
|
||||||
* Can be SAS9 or SASVIYA
|
* Can be SAS9 or SASVIYA
|
||||||
*/
|
*/
|
||||||
serverType: ServerType | null = null;
|
serverType: ServerType | null = null
|
||||||
/**
|
/**
|
||||||
* Set to `true` to enable additional debugging.
|
* Set to `true` to enable additional debugging.
|
||||||
*/
|
*/
|
||||||
debug: boolean = true;
|
debug: boolean = true
|
||||||
contextName: string = "";
|
contextName: string = ""
|
||||||
useComputeApi = false;
|
useComputeApi = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export interface SASjsRequest {
|
export interface SASjsRequest {
|
||||||
serviceLink: string;
|
serviceLink: string
|
||||||
timestamp: Date;
|
timestamp: Date
|
||||||
sourceCode: string;
|
sourceCode: string
|
||||||
generatedCode: string;
|
generatedCode: string
|
||||||
logFile: string;
|
logFile: string
|
||||||
SASWORK: any;
|
SASWORK: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
export interface SASjsWaitingRequest {
|
export interface SASjsWaitingRequest {
|
||||||
requestPromise: {
|
requestPromise: {
|
||||||
promise: any;
|
promise: any
|
||||||
resolve: any;
|
resolve: any
|
||||||
reject: any;
|
reject: any
|
||||||
};
|
}
|
||||||
SASjob: string;
|
SASjob: string
|
||||||
data: any;
|
data: any
|
||||||
config?: any;
|
config?: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from "./Link"
|
||||||
|
|
||||||
export interface Session {
|
export interface Session {
|
||||||
id: string;
|
id: string
|
||||||
state: string;
|
state: string
|
||||||
links: Link[];
|
links: Link[]
|
||||||
attributes: {
|
attributes: {
|
||||||
sessionInactiveTimeout: number;
|
sessionInactiveTimeout: number
|
||||||
};
|
}
|
||||||
creationTimeStamp: string;
|
creationTimeStamp: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export interface UploadFile {
|
export interface UploadFile {
|
||||||
file: File;
|
file: File
|
||||||
fileName: string;
|
fileName: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
export * from "./Context";
|
export * from "./Context"
|
||||||
export * from "./CsrfToken";
|
export * from "./CsrfToken"
|
||||||
export * from "./Folder";
|
export * from "./Folder"
|
||||||
export * from "./Job";
|
export * from "./Job"
|
||||||
export * from "./Link";
|
export * from "./Link"
|
||||||
export * from "./SASjsConfig";
|
export * from "./SASjsConfig"
|
||||||
export * from "./SASjsRequest";
|
export * from "./SASjsRequest"
|
||||||
export * from "./SASjsWaitingRequest";
|
export * from "./SASjsWaitingRequest"
|
||||||
export * from "./ServerType";
|
export * from "./ServerType"
|
||||||
export * from "./Session";
|
export * from "./Session"
|
||||||
export * from "./UploadFile";
|
export * from "./UploadFile"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export async function asyncForEach(array: any[], callback: any) {
|
export async function asyncForEach(array: any[], callback: any) {
|
||||||
for (let index = 0; index < array.length; index++) {
|
for (let index = 0; index < array.length; index++) {
|
||||||
await callback(array[index], index, array);
|
await callback(array[index], index, array)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { SASjsRequest } from "../types/SASjsRequest";
|
import { SASjsRequest } from "../types/SASjsRequest"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comparator for SASjs request timestamps
|
* Comparator for SASjs request timestamps
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const compareTimestamps = (a: SASjsRequest, b: SASjsRequest) => {
|
export const compareTimestamps = (a: SASjsRequest, b: SASjsRequest) => {
|
||||||
return b.timestamp.getTime() - a.timestamp.getTime();
|
return b.timestamp.getTime() - a.timestamp.getTime()
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
* @param data - the JSON object to convert.
|
* @param data - the JSON object to convert.
|
||||||
*/
|
*/
|
||||||
export const convertToCSV = (data: any) => {
|
export const convertToCSV = (data: any) => {
|
||||||
const replacer = (key: any, value: any) => (value === null ? "" : value);
|
const replacer = (key: any, value: any) => (value === null ? "" : value)
|
||||||
const headerFields = Object.keys(data[0]);
|
const headerFields = Object.keys(data[0])
|
||||||
let csvTest;
|
let csvTest
|
||||||
let invalidString = false;
|
let invalidString = false
|
||||||
const headers = headerFields.map((field) => {
|
const headers = headerFields.map((field) => {
|
||||||
let firstFoundType: string | null = null;
|
let firstFoundType: string | null = null
|
||||||
let hasMixedTypes: boolean = false;
|
let hasMixedTypes: boolean = false
|
||||||
let rowNumError: number = -1;
|
let rowNumError: number = -1
|
||||||
|
|
||||||
const longestValueForField = data
|
const longestValueForField = data
|
||||||
.map((row: any, index: number) => {
|
.map((row: any, index: number) => {
|
||||||
@@ -19,46 +19,46 @@ export const convertToCSV = (data: any) => {
|
|||||||
let currentFieldType =
|
let currentFieldType =
|
||||||
row[field] === "" || typeof row[field] === "string"
|
row[field] === "" || typeof row[field] === "string"
|
||||||
? "chars"
|
? "chars"
|
||||||
: "number";
|
: "number"
|
||||||
|
|
||||||
if (!hasMixedTypes) {
|
if (!hasMixedTypes) {
|
||||||
hasMixedTypes = currentFieldType !== firstFoundType;
|
hasMixedTypes = currentFieldType !== firstFoundType
|
||||||
rowNumError = hasMixedTypes ? index + 1 : -1;
|
rowNumError = hasMixedTypes ? index + 1 : -1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (row[field] === "") {
|
if (row[field] === "") {
|
||||||
firstFoundType = "chars";
|
firstFoundType = "chars"
|
||||||
} else {
|
} else {
|
||||||
firstFoundType =
|
firstFoundType =
|
||||||
typeof row[field] === "string" ? "chars" : "number";
|
typeof row[field] === "string" ? "chars" : "number"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let byteSize;
|
let byteSize
|
||||||
|
|
||||||
if (typeof row[field] === "string") {
|
if (typeof row[field] === "string") {
|
||||||
let doubleQuotesFound = row[field]
|
let doubleQuotesFound = row[field]
|
||||||
.split("")
|
.split("")
|
||||||
.filter((char: any) => char === '"');
|
.filter((char: any) => char === '"')
|
||||||
|
|
||||||
byteSize = getByteSize(row[field]);
|
byteSize = getByteSize(row[field])
|
||||||
|
|
||||||
if (doubleQuotesFound.length > 0) {
|
if (doubleQuotesFound.length > 0) {
|
||||||
byteSize += doubleQuotesFound.length;
|
byteSize += doubleQuotesFound.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return byteSize;
|
return byteSize
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sort((a: number, b: number) => b - a)[0];
|
.sort((a: number, b: number) => b - a)[0]
|
||||||
if (longestValueForField && longestValueForField > 32765) {
|
if (longestValueForField && longestValueForField > 32765) {
|
||||||
invalidString = true;
|
invalidString = true
|
||||||
}
|
}
|
||||||
if (hasMixedTypes) {
|
if (hasMixedTypes) {
|
||||||
console.error(
|
console.error(
|
||||||
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
|
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${field}:${firstFoundType === "chars" ? "$" : ""}${
|
return `${field}:${firstFoundType === "chars" ? "$" : ""}${
|
||||||
@@ -67,30 +67,30 @@ export const convertToCSV = (data: any) => {
|
|||||||
: firstFoundType === "chars"
|
: firstFoundType === "chars"
|
||||||
? "1"
|
? "1"
|
||||||
: "best"
|
: "best"
|
||||||
}.`;
|
}.`
|
||||||
});
|
})
|
||||||
|
|
||||||
if (invalidString) {
|
if (invalidString) {
|
||||||
return "ERROR: LARGE STRING LENGTH";
|
return "ERROR: LARGE STRING LENGTH"
|
||||||
}
|
}
|
||||||
csvTest = data.map((row: any) => {
|
csvTest = data.map((row: any) => {
|
||||||
const fields = Object.keys(row).map((fieldName, index) => {
|
const fields = Object.keys(row).map((fieldName, index) => {
|
||||||
let value;
|
let value
|
||||||
let containsSpecialChar = false;
|
let containsSpecialChar = false
|
||||||
const currentCell = row[fieldName];
|
const currentCell = row[fieldName]
|
||||||
|
|
||||||
if (JSON.stringify(currentCell).search(/(\\t|\\n|\\r)/gm) > -1) {
|
if (JSON.stringify(currentCell).search(/(\\t|\\n|\\r)/gm) > -1) {
|
||||||
value = currentCell.toString();
|
value = currentCell.toString()
|
||||||
containsSpecialChar = true;
|
containsSpecialChar = true
|
||||||
} else {
|
} else {
|
||||||
value = JSON.stringify(currentCell, replacer);
|
value = JSON.stringify(currentCell, replacer)
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value.replace(/\\\\/gm, "\\");
|
value = value.replace(/\\\\/gm, "\\")
|
||||||
|
|
||||||
if (containsSpecialChar) {
|
if (containsSpecialChar) {
|
||||||
if (value.includes(",") || value.includes('"')) {
|
if (value.includes(",") || value.includes('"')) {
|
||||||
value = '"' + value + '"';
|
value = '"' + value + '"'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
@@ -98,36 +98,36 @@ export const convertToCSV = (data: any) => {
|
|||||||
value.includes('"') &&
|
value.includes('"') &&
|
||||||
!value.includes('\\"')
|
!value.includes('\\"')
|
||||||
) {
|
) {
|
||||||
value = value.substring(1, value.length - 1);
|
value = value.substring(1, value.length - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value.replace(/\\"/gm, '""');
|
value = value.replace(/\\"/gm, '""')
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value.replace(/\r\n/gm, "\n");
|
value = value.replace(/\r\n/gm, "\n")
|
||||||
|
|
||||||
if (value === "" && headers[index].includes("best")) {
|
if (value === "" && headers[index].includes("best")) {
|
||||||
value = ".";
|
value = "."
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value
|
||||||
});
|
})
|
||||||
return fields.join(",");
|
return fields.join(",")
|
||||||
});
|
})
|
||||||
|
|
||||||
let finalCSV =
|
let finalCSV =
|
||||||
headers.join(",").replace(/,/g, " ") + "\r\n" + csvTest.join("\r\n");
|
headers.join(",").replace(/,/g, " ") + "\r\n" + csvTest.join("\r\n")
|
||||||
|
|
||||||
return finalCSV;
|
return finalCSV
|
||||||
};
|
}
|
||||||
|
|
||||||
const getByteSize = (str: string) => {
|
const getByteSize = (str: string) => {
|
||||||
let byteSize = str.length;
|
let byteSize = str.length
|
||||||
for (let i = str.length - 1; i >= 0; i--) {
|
for (let i = str.length - 1; i >= 0; i--) {
|
||||||
const code = str.charCodeAt(i);
|
const code = str.charCodeAt(i)
|
||||||
if (code > 0x7f && code <= 0x7ff) byteSize++;
|
if (code > 0x7f && code <= 0x7ff) byteSize++
|
||||||
else if (code > 0x7ff && code <= 0xffff) byteSize += 2;
|
else if (code > 0x7ff && code <= 0xffff) byteSize += 2
|
||||||
if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate
|
if (code >= 0xdc00 && code <= 0xdfff) i-- //trail surrogate
|
||||||
}
|
}
|
||||||
return byteSize;
|
return byteSize
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { convertToCSV } from "./convertToCsv";
|
import { convertToCSV } from "./convertToCsv"
|
||||||
import { splitChunks } from "./splitChunks";
|
import { splitChunks } from "./splitChunks"
|
||||||
|
|
||||||
export const formatDataForRequest = (data: any) => {
|
export const formatDataForRequest = (data: any) => {
|
||||||
const sasjsTables = [];
|
const sasjsTables = []
|
||||||
let tableCounter = 0;
|
let tableCounter = 0
|
||||||
const result: any = {};
|
const result: any = {}
|
||||||
|
|
||||||
for (const tableName in data) {
|
for (const tableName in data) {
|
||||||
tableCounter++;
|
tableCounter++
|
||||||
sasjsTables.push(tableName);
|
sasjsTables.push(tableName)
|
||||||
const csv = convertToCSV(data[tableName]);
|
const csv = convertToCSV(data[tableName])
|
||||||
if (csv === "ERROR: LARGE STRING LENGTH") {
|
if (csv === "ERROR: LARGE STRING LENGTH") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"The max length of a string value in SASjs is 32765 characters."
|
"The max length of a string value in SASjs is 32765 characters."
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
// if csv has length more then 16k, send in chunks
|
// if csv has length more then 16k, send in chunks
|
||||||
if (csv.length > 16000) {
|
if (csv.length > 16000) {
|
||||||
const csvChunks = splitChunks(csv);
|
const csvChunks = splitChunks(csv)
|
||||||
// append chunks to form data with same key
|
// append chunks to form data with same key
|
||||||
result[`sasjs${tableCounter}data0`] = csvChunks.length;
|
result[`sasjs${tableCounter}data0`] = csvChunks.length
|
||||||
csvChunks.forEach((chunk, index) => {
|
csvChunks.forEach((chunk, index) => {
|
||||||
result[`sasjs${tableCounter}data${index + 1}`] = chunk;
|
result[`sasjs${tableCounter}data${index + 1}`] = chunk
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
result[`sasjs${tableCounter}data`] = csv;
|
result[`sasjs${tableCounter}data`] = csv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result["sasjs_tables"] = sasjsTables.join(" ");
|
result["sasjs_tables"] = sasjsTables.join(" ")
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
export * from "./asyncForEach";
|
export * from "./asyncForEach"
|
||||||
export * from "./compareTimestamps";
|
export * from "./compareTimestamps"
|
||||||
export * from "./convertToCsv";
|
export * from "./convertToCsv"
|
||||||
export * from "./isAuthorizeFormRequired";
|
export * from "./isAuthorizeFormRequired"
|
||||||
export * from "./isLoginRequired";
|
export * from "./isLoginRequired"
|
||||||
export * from "./isLoginSuccess";
|
export * from "./isLoginSuccess"
|
||||||
export * from "./makeRequest";
|
export * from "./makeRequest"
|
||||||
export * from "./needsRetry";
|
export * from "./needsRetry"
|
||||||
export * from "./parseAndSubmitAuthorizeForm";
|
export * from "./parseAndSubmitAuthorizeForm"
|
||||||
export * from "./parseGeneratedCode";
|
export * from "./parseGeneratedCode"
|
||||||
export * from "./parseSourceCode";
|
export * from "./parseSourceCode"
|
||||||
export * from "./parseSasViyaLog";
|
export * from "./parseSasViyaLog"
|
||||||
export * from "./serialize";
|
export * from "./serialize"
|
||||||
export * from "./splitChunks";
|
export * from "./splitChunks"
|
||||||
export * from "./parseWeboutResponse";
|
export * from "./parseWeboutResponse"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export const isAuthorizeFormRequired = (response: string): boolean => {
|
export const isAuthorizeFormRequired = (response: string): boolean => {
|
||||||
return /<form.+action="(.*Logon\/oauth\/authorize[^"]*).*>/gm.test(response);
|
return /<form.+action="(.*Logon\/oauth\/authorize[^"]*).*>/gm.test(response)
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
export function isIEorEdgeOrOldFirefox() {
|
export function isIEorEdgeOrOldFirefox() {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
const ua = window.navigator.userAgent;
|
const ua = window.navigator.userAgent
|
||||||
|
|
||||||
if (ua.indexOf("Firefox") > 0) {
|
if (ua.indexOf("Firefox") > 0) {
|
||||||
const version = parseInt(
|
const version = parseInt(
|
||||||
ua.substring(ua.lastIndexOf("Firefox/") + 8, ua.length),
|
ua.substring(ua.lastIndexOf("Firefox/") + 8, ua.length),
|
||||||
10
|
10
|
||||||
);
|
)
|
||||||
return version <= 60;
|
return version <= 60
|
||||||
}
|
}
|
||||||
|
|
||||||
const msie = ua.indexOf("MSIE ");
|
const msie = ua.indexOf("MSIE ")
|
||||||
if (msie > 0) {
|
if (msie > 0) {
|
||||||
// IE 10 or older => return version number
|
// IE 10 or older => return version number
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const trident = ua.indexOf("Trident/");
|
const trident = ua.indexOf("Trident/")
|
||||||
if (trident > 0) {
|
if (trident > 0) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const edge = ua.indexOf("Edge/");
|
const edge = ua.indexOf("Edge/")
|
||||||
if (edge > 0) {
|
if (edge > 0) {
|
||||||
// Edge (IE 12+) => return version number
|
// Edge (IE 12+) => return version number
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// other browser
|
// other browser
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const isLogInRequired = (response: string): boolean => {
|
export const isLogInRequired = (response: string): boolean => {
|
||||||
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm;
|
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm
|
||||||
const matches = pattern.test(response);
|
const matches = pattern.test(response)
|
||||||
return matches;
|
return matches
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export const isLogInSuccess = (response: string): boolean =>
|
export const isLogInSuccess = (response: string): boolean =>
|
||||||
/You have signed in/gm.test(response);
|
/You have signed in/gm.test(response)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { CsrfToken } from "../types";
|
import { CsrfToken } from "../types"
|
||||||
import { needsRetry } from "./needsRetry";
|
import { needsRetry } from "./needsRetry"
|
||||||
|
|
||||||
let retryCount: number = 0;
|
let retryCount: number = 0
|
||||||
let retryLimit: number = 5;
|
let retryLimit: number = 5
|
||||||
|
|
||||||
export async function makeRequest<T>(
|
export async function makeRequest<T>(
|
||||||
url: string,
|
url: string,
|
||||||
@@ -10,98 +10,98 @@ export async function makeRequest<T>(
|
|||||||
callback: (value: CsrfToken) => any,
|
callback: (value: CsrfToken) => any,
|
||||||
contentType: "text" | "json" = "json"
|
contentType: "text" | "json" = "json"
|
||||||
): Promise<{ result: T; etag: string | null }> {
|
): Promise<{ result: T; etag: string | null }> {
|
||||||
let retryRequest: any = null;
|
let retryRequest: any = null
|
||||||
|
|
||||||
const responseTransform =
|
const responseTransform =
|
||||||
contentType === "json"
|
contentType === "json"
|
||||||
? (res: Response) => res.json()
|
? (res: Response) => res.json()
|
||||||
: (res: Response) => res.text();
|
: (res: Response) => res.text()
|
||||||
let etag = null;
|
let etag = null
|
||||||
const result = await fetch(url, request).then(async (response) => {
|
const result = await fetch(url, request).then(async (response) => {
|
||||||
if (response.redirected && response.url.includes("SASLogon/login")) {
|
if (response.redirected && response.url.includes("SASLogon/login")) {
|
||||||
return Promise.reject({ status: 401 });
|
return Promise.reject({ status: 401 })
|
||||||
}
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
const tokenHeader = response.headers.get("X-CSRF-HEADER");
|
const tokenHeader = response.headers.get("X-CSRF-HEADER")
|
||||||
|
|
||||||
if (tokenHeader) {
|
if (tokenHeader) {
|
||||||
const token = response.headers.get(tokenHeader);
|
const token = response.headers.get(tokenHeader)
|
||||||
callback({
|
callback({
|
||||||
headerName: tokenHeader,
|
headerName: tokenHeader,
|
||||||
value: token || ""
|
value: token || ""
|
||||||
});
|
})
|
||||||
|
|
||||||
retryRequest = {
|
retryRequest = {
|
||||||
...request,
|
...request,
|
||||||
headers: { ...request.headers, [tokenHeader]: token }
|
headers: { ...request.headers, [tokenHeader]: token }
|
||||||
};
|
}
|
||||||
return fetch(url, retryRequest).then((res) => {
|
return fetch(url, retryRequest).then((res) => {
|
||||||
etag = res.headers.get("ETag");
|
etag = res.headers.get("ETag")
|
||||||
return responseTransform(res);
|
return responseTransform(res)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const body = await response.text();
|
const body = await response.text()
|
||||||
|
|
||||||
if (needsRetry(body)) {
|
if (needsRetry(body)) {
|
||||||
if (retryCount < retryLimit) {
|
if (retryCount < retryLimit) {
|
||||||
retryCount++;
|
retryCount++
|
||||||
let retryResponse = await makeRequest(
|
let retryResponse = await makeRequest(
|
||||||
url,
|
url,
|
||||||
retryRequest || request,
|
retryRequest || request,
|
||||||
callback,
|
callback,
|
||||||
contentType
|
contentType
|
||||||
);
|
)
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
etag = retryResponse.etag;
|
etag = retryResponse.etag
|
||||||
return retryResponse.result;
|
return retryResponse.result
|
||||||
} else {
|
} else {
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
throw new Error("Request retry limit exceeded");
|
throw new Error("Request retry limit exceeded")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject({ status: response.status, body });
|
return Promise.reject({ status: response.status, body })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
const responseTransformed = await responseTransform(response);
|
const responseTransformed = await responseTransform(response)
|
||||||
let responseText = "";
|
let responseText = ""
|
||||||
|
|
||||||
if (typeof responseTransformed === "string") {
|
if (typeof responseTransformed === "string") {
|
||||||
responseText = responseTransformed;
|
responseText = responseTransformed
|
||||||
} else {
|
} else {
|
||||||
responseText = JSON.stringify(responseTransformed);
|
responseText = JSON.stringify(responseTransformed)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsRetry(responseText)) {
|
if (needsRetry(responseText)) {
|
||||||
if (retryCount < retryLimit) {
|
if (retryCount < retryLimit) {
|
||||||
retryCount++;
|
retryCount++
|
||||||
const retryResponse = await makeRequest(
|
const retryResponse = await makeRequest(
|
||||||
url,
|
url,
|
||||||
retryRequest || request,
|
retryRequest || request,
|
||||||
callback,
|
callback,
|
||||||
contentType
|
contentType
|
||||||
);
|
)
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
etag = retryResponse.etag;
|
etag = retryResponse.etag
|
||||||
return retryResponse.result;
|
return retryResponse.result
|
||||||
} else {
|
} else {
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
throw new Error("Request retry limit exceeded");
|
throw new Error("Request retry limit exceeded")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
etag = response.headers.get("ETag");
|
etag = response.headers.get("ETag")
|
||||||
return responseTransformed;
|
return responseTransformed
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return { result, etag };
|
return { result, etag }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ export const needsRetry = (responseText: string): boolean => {
|
|||||||
responseText.includes(
|
responseText.includes(
|
||||||
"Authentication success, retry original request"
|
"Authentication success, retry original request"
|
||||||
)))
|
)))
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -2,31 +2,31 @@ export const parseAndSubmitAuthorizeForm = async (
|
|||||||
response: string,
|
response: string,
|
||||||
serverUrl: string
|
serverUrl: string
|
||||||
) => {
|
) => {
|
||||||
let authUrl: string | null = null;
|
let authUrl: string | null = null
|
||||||
const params: any = {};
|
const params: any = {}
|
||||||
|
|
||||||
const responseBody = response.split("<body>")[1].split("</body>")[0];
|
const responseBody = response.split("<body>")[1].split("</body>")[0]
|
||||||
const bodyElement = document.createElement("div");
|
const bodyElement = document.createElement("div")
|
||||||
bodyElement.innerHTML = responseBody;
|
bodyElement.innerHTML = responseBody
|
||||||
|
|
||||||
const form = bodyElement.querySelector("#application_authorization");
|
const form = bodyElement.querySelector("#application_authorization")
|
||||||
authUrl = form ? serverUrl + form.getAttribute("action") : null;
|
authUrl = form ? serverUrl + form.getAttribute("action") : null
|
||||||
|
|
||||||
const inputs: any = form?.querySelectorAll("input");
|
const inputs: any = form?.querySelectorAll("input")
|
||||||
|
|
||||||
for (const input of inputs) {
|
for (const input of inputs) {
|
||||||
if (input.name === "user_oauth_approval") {
|
if (input.name === "user_oauth_approval") {
|
||||||
input.value = "true";
|
input.value = "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
params[input.name] = input.value;
|
params[input.name] = input.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData()
|
||||||
|
|
||||||
for (const key in params) {
|
for (const key in params) {
|
||||||
if (params.hasOwnProperty(key)) {
|
if (params.hasOwnProperty(key)) {
|
||||||
formData.append(key, params[key]);
|
formData.append(key, params[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ export const parseAndSubmitAuthorizeForm = async (
|
|||||||
})
|
})
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
resolve(res);
|
resolve(res)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
reject("Auth form url is null");
|
reject("Auth form url is null")
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const parseGeneratedCode = (log: string) => {
|
export const parseGeneratedCode = (log: string) => {
|
||||||
const startsWith = "MPRINT";
|
const startsWith = "MPRINT"
|
||||||
const isGeneratedCodeLine = (line: string) =>
|
const isGeneratedCodeLine = (line: string) =>
|
||||||
line.trim().startsWith(startsWith);
|
line.trim().startsWith(startsWith)
|
||||||
const logLines = log.split("\n").filter(isGeneratedCodeLine);
|
const logLines = log.split("\n").filter(isGeneratedCodeLine)
|
||||||
return logLines.join("\r\n");
|
return logLines.join("\r\n")
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
export const parseSasViyaLog = (logResponse: { items: any[] }) => {
|
export const parseSasViyaLog = (logResponse: { items: any[] }) => {
|
||||||
let log;
|
let log
|
||||||
try {
|
try {
|
||||||
log = logResponse.items
|
log = logResponse.items
|
||||||
? logResponse.items.map((i) => i.line).join("\n")
|
? logResponse.items.map((i) => i.line).join("\n")
|
||||||
: JSON.stringify(logResponse);
|
: JSON.stringify(logResponse)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("An error has occurred while parsing the log response", e);
|
console.error("An error has occurred while parsing the log response", e)
|
||||||
log = logResponse;
|
log = logResponse
|
||||||
}
|
}
|
||||||
return log;
|
return log
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const parseSourceCode = (log: string): string => {
|
export const parseSourceCode = (log: string): string => {
|
||||||
const isSourceCodeLine = (line: string) =>
|
const isSourceCodeLine = (line: string) =>
|
||||||
line.trim().substring(0, 10).trimStart().match(/^\d/);
|
line.trim().substring(0, 10).trimStart().match(/^\d/)
|
||||||
const logLines = log.split("\n").filter(isSourceCodeLine);
|
const logLines = log.split("\n").filter(isSourceCodeLine)
|
||||||
return logLines.join("\r\n");
|
return logLines.join("\r\n")
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
export const parseWeboutResponse = (response: string) => {
|
export const parseWeboutResponse = (response: string) => {
|
||||||
let sasResponse = "";
|
let sasResponse = ""
|
||||||
|
|
||||||
if (response.includes(">>weboutBEGIN<<")) {
|
if (response.includes(">>weboutBEGIN<<")) {
|
||||||
try {
|
try {
|
||||||
sasResponse = response
|
sasResponse = response
|
||||||
.split(">>weboutBEGIN<<")[1]
|
.split(">>weboutBEGIN<<")[1]
|
||||||
.split(">>weboutEND<<")[0];
|
.split(">>weboutEND<<")[0]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sasResponse = "";
|
sasResponse = ""
|
||||||
console.error(e);
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sasResponse;
|
return sasResponse
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
export const serialize = (obj: any) => {
|
export const serialize = (obj: any) => {
|
||||||
const str: any[] = [];
|
const str: any[] = []
|
||||||
for (const p in obj) {
|
for (const p in obj) {
|
||||||
if (obj.hasOwnProperty(p)) {
|
if (obj.hasOwnProperty(p)) {
|
||||||
if (obj[p] instanceof Array) {
|
if (obj[p] instanceof Array) {
|
||||||
for (let i = 0, n = obj[p].length; i < n; i++) {
|
for (let i = 0, n = obj[p].length; i < n; i++) {
|
||||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p][i]));
|
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p][i]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return str.join("&");
|
return str.join("&")
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
export const splitChunks = (content: string) => {
|
export const splitChunks = (content: string) => {
|
||||||
const size = 16000;
|
const size = 16000
|
||||||
|
|
||||||
const numChunks = Math.ceil(content.length / size);
|
const numChunks = Math.ceil(content.length / size)
|
||||||
const chunks = new Array(numChunks);
|
const chunks = new Array(numChunks)
|
||||||
|
|
||||||
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
|
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
|
||||||
chunks[i] = content.substr(o, size);
|
chunks[i] = content.substr(o, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunks;
|
return chunks
|
||||||
};
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user