From ac6cd7be82f8bd8a127432f0d4a3ddadb971b062 Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Wed, 30 Jun 2021 16:55:09 +0300 Subject: [PATCH 1/3] fix(session): fixed polling session state --- src/SessionManager.ts | 24 +++++++++--------------- src/request/RequestClient.ts | 3 +++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/SessionManager.ts b/src/SessionManager.ts index 93a411d..914a686 100644 --- a/src/SessionManager.ts +++ b/src/SessionManager.ts @@ -6,10 +6,6 @@ import { RequestClient } from './request/RequestClient' const MAX_SESSION_COUNT = 1 const RETRY_LIMIT: number = 3 let RETRY_COUNT: number = 0 -const INTERNAL_SAS_ERROR = { - status: 304, - message: 'Not Modified' -} export class SessionManager { constructor( @@ -164,7 +160,7 @@ export class SessionManager { const stateLink = session.links.find((l: any) => l.rel === 'state') - return new Promise(async (resolve, _) => { + return new Promise(async (resolve, reject) => { if ( sessionState === 'pending' || sessionState === 'running' || @@ -182,7 +178,7 @@ export class SessionManager { etag!, accessToken ).catch((err) => { - throw err + throw prefixMessage(err, 'Error while getting session state.') }) sessionState = state.trim() @@ -196,13 +192,14 @@ export class SessionManager { // There is an internal error present in SAS Viya 3.5 // Retry to wait for a session status in such case of SAS internal error - if ( - sessionState === INTERNAL_SAS_ERROR.message && - RETRY_COUNT < RETRY_LIMIT - ) { - RETRY_COUNT++ + if (!sessionState) { + if (RETRY_COUNT < RETRY_LIMIT) { + RETRY_COUNT++ - resolve(this.waitForSession(session, etag, accessToken)) + resolve(this.waitForSession(session, etag, accessToken)) + } else { + reject('Could not get session state.') + } } resolve(sessionState) @@ -222,9 +219,6 @@ export class SessionManager { .get(url, accessToken, 'text/plain', { 'If-None-Match': etag }) .then((res) => res.result as string) .catch((err) => { - if (err.status === INTERNAL_SAS_ERROR.status) - return INTERNAL_SAS_ERROR.message - throw err }) } diff --git a/src/request/RequestClient.ts b/src/request/RequestClient.ts index 580c1fa..c5e6e70 100644 --- a/src/request/RequestClient.ts +++ b/src/request/RequestClient.ts @@ -63,6 +63,9 @@ export class RequestClient implements HttpClient { baseURL: baseUrl }) } + + this.httpClient.defaults.validateStatus = (status) => + status >= 200 && status < 305 } public getCsrfToken(type: 'general' | 'file' = 'general') { From 5cb5bbdb55f05bdc872bb2e0cf7b15c1b3edb911 Mon Sep 17 00:00:00 2001 From: Krishna Acondy Date: Wed, 30 Jun 2021 15:19:12 +0100 Subject: [PATCH 2/3] fix(execution): refresh tokens before fetching results --- src/SASViyaApiClient.ts | 83 +++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/src/SASViyaApiClient.ts b/src/SASViyaApiClient.ts index 6f1a972..e33c613 100644 --- a/src/SASViyaApiClient.ts +++ b/src/SASViyaApiClient.ts @@ -290,16 +290,14 @@ export class SASViyaApiClient { printPid = false, variables?: MacroVar ): Promise { - const { access_token } = authConfig || {} + let access_token = (authConfig || {}).access_token + if (authConfig) { + ;({ access_token } = await this.getTokens(authConfig)) + } + const logger = process.logger || console try { - const headers: any = { - 'Content-Type': 'application/json' - } - - if (access_token) headers.Authorization = `Bearer ${access_token}` - let executionSessionId: string const session = await this.sessionManager @@ -443,6 +441,10 @@ export class SASViyaApiClient { throw prefixMessage(err, 'Error while polling job status. ') }) + if (authConfig) { + ;({ access_token } = await this.getTokens(authConfig)) + } + const { result: currentJob } = await this.requestClient .get( `/compute/sessions/${executionSessionId}/jobs/${postedJob.id}`, @@ -886,7 +888,10 @@ export class SASViyaApiClient { printPid = false, variables?: MacroVar ) { - let { access_token } = authConfig || {} + let access_token = (authConfig || {}).access_token + if (authConfig) { + ;({ access_token } = await this.getTokens(authConfig)) + } if (isRelativePath(sasJob) && !this.rootFolderName) { throw new Error( @@ -913,12 +918,6 @@ export class SASViyaApiClient { ) } - const headers: any = { 'Content-Type': 'application/json' } - - if (!!access_token) { - headers.Authorization = `Bearer ${access_token}` - } - const jobToExecute = jobFolder?.find((item) => item.name === jobName) if (!jobToExecute) { @@ -985,7 +984,10 @@ export class SASViyaApiClient { data?: any, authConfig?: AuthConfig ) { - let { access_token } = authConfig || {} + let access_token = (authConfig || {}).access_token + if (authConfig) { + ;({ access_token } = await this.getTokens(authConfig)) + } if (isRelativePath(sasJob) && !this.rootFolderName) { throw new Error( 'Relative paths cannot be used without specifying a root folder name.' @@ -1145,21 +1147,9 @@ export class SASViyaApiClient { let POLL_INTERVAL = 300 let MAX_POLL_COUNT = 1000 let MAX_ERROR_COUNT = 5 - let { access_token, refresh_token, client, secret } = authConfig || {} - if (access_token && refresh_token) { - if ( - client && - secret && - refresh_token && - (isAccessTokenExpiring(access_token) || - isRefreshTokenExpiring(refresh_token)) - ) { - ;({ access_token, refresh_token } = await this.refreshTokens( - client, - secret, - refresh_token - )) - } + let access_token = (authConfig || {}).access_token + if (authConfig) { + ;({ access_token } = await this.getTokens(authConfig)) } if (pollOptions) { @@ -1213,20 +1203,8 @@ export class SASViyaApiClient { postedJobState === 'pending' || postedJobState === 'unavailable' ) { - if (access_token && refresh_token) { - if ( - client && - secret && - refresh_token && - (isAccessTokenExpiring(access_token) || - isRefreshTokenExpiring(refresh_token)) - ) { - ;({ access_token, refresh_token } = await this.refreshTokens( - client, - secret, - refresh_token - )) - } + if (authConfig) { + ;({ access_token } = await this.getTokens(authConfig)) } if (stateLink) { @@ -1510,4 +1488,21 @@ export class SASViyaApiClient { return movedFolder } + + private async getTokens(authConfig: AuthConfig): Promise { + const logger = process.logger || console + let { access_token, refresh_token, client, secret } = authConfig + if ( + isAccessTokenExpiring(access_token) || + isRefreshTokenExpiring(refresh_token) + ) { + logger.info('Refreshing access and refresh tokens.') + ;({ access_token, refresh_token } = await this.refreshTokens( + client, + secret, + refresh_token + )) + } + return { access_token, refresh_token, client, secret } + } } From eb0e7247a621c3c5bc278b1e04d4bcef42d3e4ed Mon Sep 17 00:00:00 2001 From: Krishna Acondy Date: Wed, 30 Jun 2021 18:05:52 +0100 Subject: [PATCH 3/3] fix(scripts): change git hook script to prepare --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fdfe60..21517d5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "postpublish": "git clean -fd", "semantic-release": "semantic-release", "typedoc": "typedoc", - "postinstall": "[ -d .git ] && git config core.hooksPath ./.git-hooks || true" + "prepare": "[ -d .git ] && git config core.hooksPath ./.git-hooks || true" }, "publishConfig": { "access": "public"