1
0
mirror of https://github.com/sasjs/adapter.git synced 2026-01-05 19:50:06 +00:00

Merge branch 'master' into issue-186

This commit is contained in:
Mihajlo Medjedovic
2020-12-23 14:23:26 +01:00
40 changed files with 6245 additions and 2983 deletions

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
SERVER_URL=https://server.com
DEFAULT_COMPUTE_CONTEXT=SAS Job Execution compute context

9
.github/reviewer-lottery.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
groups:
- name: SASjs Devs # name of the group
reviewers: 1 # how many reviewers do you want to assign?
usernames: # github usernames of the reviewers
- krishna-acondy
- YuryShkoda
- saadjutt01
- medjedovicm
- allanbowe

13
.github/workflows/assign-reviewer.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
name: 'Assign Reviewer'
on:
pull_request:
types: [opened]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: uesteibar/reviewer-lottery@v1
with:
repo-token: ${{ secrets.GH_TOKEN }}

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
node_modules
build
.env

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1673
docs/classes/root.sasjs.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -76,7 +76,7 @@
<section class="tsd-index-section ">
<h3>Modules</h3>
<ul class="tsd-index-list">
<li class="tsd-kind-module tsd-is-not-exported"><a href="modules/reflection-817.html" class="tsd-kind-icon"><em>Module</em></a></li>
<li class="tsd-kind-module tsd-is-not-exported"><a href="modules/reflection-830.html" class="tsd-kind-icon"><em>Module</em></a></li>
<li class="tsd-kind-module"><a href="modules/types.html" class="tsd-kind-icon">types</a></li>
<li class="tsd-kind-module"><a href="modules/utils.html" class="tsd-kind-icon">utils</a></li>
</ul>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

125
docs/modules/root.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4590
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,7 @@
"@types/isomorphic-fetch": "0.0.35",
"@types/jest": "^26.0.15",
"cp": "^0.2.0",
"dotenv": "^8.2.0",
"jest": "^25.5.4",
"path": "^0.12.7",
"rimraf": "^3.0.2",
@@ -57,6 +58,7 @@
},
"main": "index.js",
"dependencies": {
"@sasjs/utils": "^1.5.0",
"es6-promise": "^4.2.8",
"form-data": "^3.0.0",
"isomorphic-fetch": "^2.2.1"

View File

@@ -16,12 +16,13 @@ import {
Folder,
CsrfToken,
EditContextInput,
ErrorResponse,
JobDefinition,
PollOptions
} from './types'
import { formatDataForRequest } from './utils/formatDataForRequest'
import { SessionManager } from './SessionManager'
import { timestampToYYYYMMDDHHMMSS } from '@sasjs/utils/time'
import { Logger, LogLevel } from '@sasjs/utils/logger'
/**
* A client for interfacing with the SAS Viya REST API.
@@ -430,6 +431,7 @@ export class SASViyaApiClient {
* @param expectWebout - when set to true, the automatic _webout fileref will be checked for content, and that content returned. This fileref is used when the Job contains a SASjs web request (as opposed to executing arbitrary SAS code).
* @param waitForResult - when set to true, function will return the session
* @param pollOptions - an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { MAX_POLL_COUNT: 24 * 60 * 60, POLL_INTERVAL: 1000 }.
* @param printPid - a boolean that indicates whether the function should print (PID) of the started job.
*/
public async executeScript(
jobPath: string,
@@ -440,7 +442,8 @@ export class SASViyaApiClient {
debug: boolean = false,
expectWebout = false,
waitForResult = true,
pollOptions?: PollOptions
pollOptions?: PollOptions,
printPid = false
): Promise<any> {
try {
const headers: any = {
@@ -460,6 +463,28 @@ export class SASViyaApiClient {
executionSessionId = session!.id
if (printPid) {
const { result: jobIdVariable } = await this.sessionManager.getVariable(
executionSessionId,
'SYSJOBID',
accessToken
)
if (jobIdVariable && jobIdVariable.value) {
const relativeJobPath = this.rootFolderName
? jobPath.split(this.rootFolderName).join('').replace(/^\//, '')
: jobPath
const logger = new Logger(debug ? LogLevel.Debug : LogLevel.Info)
logger.info(
`Triggered '${relativeJobPath}' with PID ${
jobIdVariable.value
} at ${timestampToYYYYMMDDHHMMSS()}`
)
}
}
const jobArguments: { [key: string]: any } = {
_contextName: contextName,
_OMITJSONLISTING: true,
@@ -958,6 +983,7 @@ export class SASViyaApiClient {
* @param waitForResult - a boolean indicating if the function should wait for a result.
* @param expectWebout - a boolean indicating whether to expect a _webout response.
* @param pollOptions - an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { MAX_POLL_COUNT: 24 * 60 * 60, POLL_INTERVAL: 1000 }.
* @param printPid - a boolean that indicates whether the function should print (PID) of the started job.
*/
public async executeComputeJob(
sasJob: string,
@@ -967,7 +993,8 @@ export class SASViyaApiClient {
accessToken?: string,
waitForResult = true,
expectWebout = false,
pollOptions?: PollOptions
pollOptions?: PollOptions,
printPid = false
) {
if (isRelativePath(sasJob) && !this.rootFolderName) {
throw new Error(
@@ -1053,7 +1080,8 @@ export class SASViyaApiClient {
debug,
expectWebout,
waitForResult,
pollOptions
pollOptions,
printPid
)
}

View File

@@ -734,6 +734,7 @@ export default class SASjs {
* The access token is not required when the user is authenticated via the browser.
* @param waitForResult - a boolean that indicates whether the function needs to wait for execution to complete.
* @param pollOptions - an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { MAX_POLL_COUNT: 24 * 60 * 60, POLL_INTERVAL: 1000 }.
* @param printPid - a boolean that indicates whether the function should print (PID) of the started job.
*/
public async startComputeJob(
sasJob: string,
@@ -741,7 +742,8 @@ export default class SASjs {
config: any = {},
accessToken?: string,
waitForResult?: boolean,
pollOptions?: PollOptions
pollOptions?: PollOptions,
printPid = false
) {
config = {
...this.sasjsConfig,
@@ -763,7 +765,8 @@ export default class SASjs {
accessToken,
!!waitForResult,
false,
pollOptions
pollOptions,
printPid
)
}

View File

@@ -1,5 +1,6 @@
import { Session, Context, CsrfToken } from './types'
import { Session, Context, CsrfToken, SessionVariable } from './types'
import { asyncForEach, makeRequest, isUrl } from './utils'
import { prefixMessage } from '@sasjs/utils/error'
const MAX_SESSION_COUNT = 1
const RETRY_LIMIT: number = 3
@@ -265,4 +266,21 @@ export class SessionManager {
throw err
})
}
async getVariable(sessionId: string, variable: string, accessToken?: string) {
const getSessionVariable = {
method: 'GET',
headers: this.getHeaders(accessToken)
}
return await this.request<SessionVariable>(
`${this.serverUrl}/compute/sessions/${sessionId}/variables/${variable}`,
getSessionVariable
).catch((err) => {
throw prefixMessage(
err,
`Error while fetching session variable '${variable}'.`
)
})
}
}

View File

@@ -0,0 +1,53 @@
import { SessionManager } from '../SessionManager'
import * as dotenv from 'dotenv'
describe('SessionManager', () => {
dotenv.config()
let originalFetch: any
const sessionManager = new SessionManager(
process.env.SERVER_URL as string,
process.env.DEFAULT_COMPUTE_CONTEXT as string,
() => {}
)
beforeAll(() => {
originalFetch = (global as any).fetch
})
afterEach(() => {
;(global as any).fetch = originalFetch
})
describe('getVariable', () => {
it('should fetch session variable', async () => {
const sampleResponse = {
ok: true,
links: [],
name: 'SYSJOBID',
scope: 'GLOBAL',
value: '25218',
version: 1
}
;(global as any).fetch = jest.fn().mockImplementation(() =>
Promise.resolve({
ok: true,
headers: { get: () => '' },
json: () => Promise.resolve(sampleResponse)
})
)
const expectedResponse = { etag: '', result: sampleResponse }
await expect(
sessionManager.getVariable(
'fakeSessionId',
'SYSJOBID',
'fakeAccessToken'
)
).resolves.toEqual(expectedResponse)
})
})
})

View File

@@ -9,3 +9,7 @@ export interface Session {
}
creationTimeStamp: string
}
export interface SessionVariable {
value: string
}