@sasjs/adapter 4.3.7 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth/getAccessTokenForViya.d.ts +2 -2
- package/auth/getTokenRequestErrorPrefix.d.ts +44 -0
- package/auth/spec/getTokenRequestErrorPrefix.spec.d.ts +1 -0
- package/index-dev.js +2 -2
- package/index.js +1 -1
- package/minified_sas9.js +1 -1
- package/node/auth/getAccessTokenForViya.d.ts +2 -2
- package/node/auth/getTokenRequestErrorPrefix.d.ts +44 -0
- package/node/index.js +1 -1
- package/package.json +1 -1
- package/src/auth/getAccessTokenForSasjs.ts +13 -1
- package/src/auth/getAccessTokenForViya.ts +22 -6
- package/src/auth/getTokenRequestErrorPrefix.ts +88 -0
- package/src/auth/getTokens.ts +3 -0
- package/src/auth/refreshTokensForSasjs.ts +11 -1
- package/src/auth/refreshTokensForViya.ts +15 -2
- package/src/job-execution/FileUploader.ts +10 -1
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { prefixMessage } from '@sasjs/utils/error'
|
|
2
2
|
import { RequestClient } from '../request/RequestClient'
|
|
3
|
+
import { getTokenRequestErrorPrefix } from './getTokenRequestErrorPrefix'
|
|
4
|
+
import { ServerType } from '@sasjs/utils/types'
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Exchanges the auth code for an access token for the given client.
|
|
@@ -31,6 +33,16 @@ export async function getAccessTokenForSasjs(
|
|
|
31
33
|
}
|
|
32
34
|
})
|
|
33
35
|
.catch((err) => {
|
|
34
|
-
throw prefixMessage(
|
|
36
|
+
throw prefixMessage(
|
|
37
|
+
err,
|
|
38
|
+
getTokenRequestErrorPrefix(
|
|
39
|
+
'fetching access token',
|
|
40
|
+
'getAccessTokenForSasjs',
|
|
41
|
+
ServerType.Sasjs,
|
|
42
|
+
url,
|
|
43
|
+
data,
|
|
44
|
+
clientId
|
|
45
|
+
)
|
|
46
|
+
)
|
|
35
47
|
})
|
|
36
48
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { SasAuthResponse } from '@sasjs/utils/types'
|
|
1
|
+
import { SasAuthResponse, ServerType } from '@sasjs/utils/types'
|
|
2
2
|
import { prefixMessage } from '@sasjs/utils/error'
|
|
3
3
|
import { RequestClient } from '../request/RequestClient'
|
|
4
4
|
import { CertificateError } from '../types/errors'
|
|
5
|
+
import { getTokenRequestErrorPrefix } from './getTokenRequestErrorPrefix'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param requestClient - the pre-configured HTTP request client
|
|
8
|
+
* Exchange the auth code for access / refresh tokens for the given client / secret pair.
|
|
9
|
+
* @param requestClient - the pre-configured HTTP request client.
|
|
9
10
|
* @param clientId - the client ID to authenticate with.
|
|
10
11
|
* @param clientSecret - the client secret to authenticate with.
|
|
11
12
|
* @param authCode - the auth code received from the server.
|
|
@@ -16,29 +17,44 @@ export async function getAccessTokenForViya(
|
|
|
16
17
|
clientSecret: string,
|
|
17
18
|
authCode: string
|
|
18
19
|
): Promise<SasAuthResponse> {
|
|
19
|
-
const url = '/SASLogon/oauth/token'
|
|
20
20
|
let token
|
|
21
|
+
|
|
21
22
|
if (typeof Buffer === 'undefined') {
|
|
22
23
|
token = btoa(clientId + ':' + clientSecret)
|
|
23
24
|
} else {
|
|
24
25
|
token = Buffer.from(clientId + ':' + clientSecret).toString('base64')
|
|
25
26
|
}
|
|
27
|
+
|
|
28
|
+
const url = '/SASLogon/oauth/token'
|
|
26
29
|
const headers = {
|
|
27
30
|
Authorization: 'Basic ' + token,
|
|
28
31
|
Accept: 'application/json'
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
const
|
|
34
|
+
const dataJson = new URLSearchParams({
|
|
32
35
|
grant_type: 'authorization_code',
|
|
33
36
|
code: authCode
|
|
34
37
|
})
|
|
38
|
+
const data = new URLSearchParams(dataJson)
|
|
35
39
|
|
|
36
40
|
const authResponse = await requestClient
|
|
37
41
|
.post(url, data, undefined, 'application/x-www-form-urlencoded', headers)
|
|
38
42
|
.then((res) => res.result as SasAuthResponse)
|
|
39
43
|
.catch((err) => {
|
|
40
44
|
if (err instanceof CertificateError) throw err
|
|
41
|
-
throw prefixMessage(
|
|
45
|
+
throw prefixMessage(
|
|
46
|
+
err,
|
|
47
|
+
getTokenRequestErrorPrefix(
|
|
48
|
+
'fetching access token',
|
|
49
|
+
'getAccessTokenForViya',
|
|
50
|
+
ServerType.SasViya,
|
|
51
|
+
url,
|
|
52
|
+
dataJson,
|
|
53
|
+
headers,
|
|
54
|
+
clientId,
|
|
55
|
+
clientSecret
|
|
56
|
+
)
|
|
57
|
+
)
|
|
42
58
|
})
|
|
43
59
|
|
|
44
60
|
return authResponse
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ServerType } from '@sasjs/utils/types'
|
|
2
|
+
|
|
3
|
+
type Server = ServerType.SasViya | ServerType.Sasjs
|
|
4
|
+
type Operation = 'fetching access token' | 'refreshing tokens'
|
|
5
|
+
|
|
6
|
+
const getServerName = (server: Server) =>
|
|
7
|
+
server === ServerType.SasViya ? 'Viya' : 'Sasjs'
|
|
8
|
+
|
|
9
|
+
const getResponseTitle = (server: Server) =>
|
|
10
|
+
`Response from ${getServerName(server)} is below.`
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Forms error prefix for requests related to token operations.
|
|
14
|
+
* @param operation - string describing operation ('fetching access token' or 'refreshing tokens').
|
|
15
|
+
* @param funcName - name of the function sent the request.
|
|
16
|
+
* @param server - server type (SASVIYA or SASJS).
|
|
17
|
+
* @param url - endpoint used to send the request.
|
|
18
|
+
* @param data - request payload.
|
|
19
|
+
* @param headers - request headers.
|
|
20
|
+
* @param clientId - client ID to authenticate with.
|
|
21
|
+
* @param clientSecret - client secret to authenticate with.
|
|
22
|
+
* @returns - string containing request information. Example:
|
|
23
|
+
* Error while fetching access token from /SASLogon/oauth/token
|
|
24
|
+
* Thrown by the @sasjs/adapter getAccessTokenForViya function.
|
|
25
|
+
* Payload:
|
|
26
|
+
* {
|
|
27
|
+
* "grant_type": "authorization_code",
|
|
28
|
+
* "code": "example_code"
|
|
29
|
+
* }
|
|
30
|
+
* Headers:
|
|
31
|
+
* {
|
|
32
|
+
* "Authorization": "Basic NEdMQXBwOjRHTEFwcDE=",
|
|
33
|
+
* "Accept": "application/json"
|
|
34
|
+
* }
|
|
35
|
+
* ClientId: exampleClientId
|
|
36
|
+
* ClientSecret: exampleClientSecret
|
|
37
|
+
*
|
|
38
|
+
* Response from Viya is below.
|
|
39
|
+
* Auth error: {
|
|
40
|
+
* "error": "invalid_token",
|
|
41
|
+
* "error_description": "No scopes were granted"
|
|
42
|
+
* }
|
|
43
|
+
*/
|
|
44
|
+
export const getTokenRequestErrorPrefix = (
|
|
45
|
+
operation: Operation,
|
|
46
|
+
funcName: string,
|
|
47
|
+
server: Server,
|
|
48
|
+
url: string,
|
|
49
|
+
data?: {},
|
|
50
|
+
headers?: {},
|
|
51
|
+
clientId?: string,
|
|
52
|
+
clientSecret?: string
|
|
53
|
+
) => {
|
|
54
|
+
const stringify = (obj: {}) => JSON.stringify(obj, null, 2)
|
|
55
|
+
|
|
56
|
+
const lines = [
|
|
57
|
+
`Error while ${operation} from ${url}`,
|
|
58
|
+
`Thrown by the @sasjs/adapter ${funcName} function.`
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
if (data) {
|
|
62
|
+
lines.push('Payload:')
|
|
63
|
+
lines.push(stringify(data))
|
|
64
|
+
}
|
|
65
|
+
if (headers) {
|
|
66
|
+
lines.push('Headers:')
|
|
67
|
+
lines.push(stringify(headers))
|
|
68
|
+
}
|
|
69
|
+
if (clientId) lines.push(`ClientId: ${clientId}`)
|
|
70
|
+
if (clientSecret) lines.push(`ClientSecret: ${clientSecret}`)
|
|
71
|
+
|
|
72
|
+
lines.push('')
|
|
73
|
+
lines.push(`${getResponseTitle(server)}`)
|
|
74
|
+
lines.push('')
|
|
75
|
+
|
|
76
|
+
return lines.join(`\n`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Parse error prefix to get response payload.
|
|
81
|
+
* @param prefix - error prefix generated by getTokenRequestErrorPrefix function.
|
|
82
|
+
* @param server - server type (SASVIYA or SASJS).
|
|
83
|
+
* @returns - response payload.
|
|
84
|
+
*/
|
|
85
|
+
export const getTokenRequestErrorPrefixResponse = (
|
|
86
|
+
prefix: string,
|
|
87
|
+
server: ServerType.SasViya | ServerType.Sasjs
|
|
88
|
+
) => prefix.split(`${getResponseTitle(server)}\n`).pop() as string
|
package/src/auth/getTokens.ts
CHANGED
|
@@ -22,6 +22,7 @@ export async function getTokens(
|
|
|
22
22
|
): Promise<AuthConfig> {
|
|
23
23
|
const logger = process.logger || console
|
|
24
24
|
let { access_token, refresh_token, client, secret } = authConfig
|
|
25
|
+
|
|
25
26
|
if (
|
|
26
27
|
isAccessTokenExpiring(access_token) ||
|
|
27
28
|
isRefreshTokenExpiring(refresh_token)
|
|
@@ -29,6 +30,7 @@ export async function getTokens(
|
|
|
29
30
|
if (hasTokenExpired(refresh_token)) {
|
|
30
31
|
const error =
|
|
31
32
|
'Unable to obtain new access token. Your refresh token has expired.'
|
|
33
|
+
|
|
32
34
|
logger.error(error)
|
|
33
35
|
|
|
34
36
|
throw new Error(error)
|
|
@@ -47,5 +49,6 @@ export async function getTokens(
|
|
|
47
49
|
: await refreshTokensForSasjs(requestClient, refresh_token)
|
|
48
50
|
;({ access_token, refresh_token } = tokens)
|
|
49
51
|
}
|
|
52
|
+
|
|
50
53
|
return { access_token, refresh_token, client, secret }
|
|
51
54
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { prefixMessage } from '@sasjs/utils/error'
|
|
2
2
|
import { RequestClient } from '../request/RequestClient'
|
|
3
|
+
import { getTokenRequestErrorPrefix } from './getTokenRequestErrorPrefix'
|
|
4
|
+
import { ServerType } from '@sasjs/utils/types'
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Exchanges the refresh token for an access token for the given client.
|
|
@@ -28,7 +30,15 @@ export async function refreshTokensForSasjs(
|
|
|
28
30
|
}
|
|
29
31
|
})
|
|
30
32
|
.catch((err) => {
|
|
31
|
-
throw prefixMessage(
|
|
33
|
+
throw prefixMessage(
|
|
34
|
+
err,
|
|
35
|
+
getTokenRequestErrorPrefix(
|
|
36
|
+
'refreshing tokens',
|
|
37
|
+
'refreshTokensForSasjs',
|
|
38
|
+
ServerType.Sasjs,
|
|
39
|
+
url
|
|
40
|
+
)
|
|
41
|
+
)
|
|
32
42
|
})
|
|
33
43
|
|
|
34
44
|
return authResponse
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { SasAuthResponse } from '@sasjs/utils/types'
|
|
1
|
+
import { SasAuthResponse, ServerType } from '@sasjs/utils/types'
|
|
2
2
|
import { prefixMessage } from '@sasjs/utils/error'
|
|
3
3
|
import * as NodeFormData from 'form-data'
|
|
4
4
|
import { RequestClient } from '../request/RequestClient'
|
|
5
5
|
import { isNode } from '../utils'
|
|
6
|
+
import { getTokenRequestErrorPrefix } from './getTokenRequestErrorPrefix'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Exchanges the refresh token for an access token for the given client.
|
|
@@ -46,7 +47,19 @@ export async function refreshTokensForViya(
|
|
|
46
47
|
)
|
|
47
48
|
.then((res) => res.result)
|
|
48
49
|
.catch((err) => {
|
|
49
|
-
throw prefixMessage(
|
|
50
|
+
throw prefixMessage(
|
|
51
|
+
err,
|
|
52
|
+
getTokenRequestErrorPrefix(
|
|
53
|
+
'refreshing tokens',
|
|
54
|
+
'refreshTokensForViya',
|
|
55
|
+
ServerType.SasViya,
|
|
56
|
+
url,
|
|
57
|
+
formData,
|
|
58
|
+
headers,
|
|
59
|
+
clientId,
|
|
60
|
+
clientSecret
|
|
61
|
+
)
|
|
62
|
+
)
|
|
50
63
|
})
|
|
51
64
|
|
|
52
65
|
return authResponse
|
|
@@ -93,15 +93,24 @@ export class FileUploader extends BaseJobExecutor {
|
|
|
93
93
|
this.requestClient,
|
|
94
94
|
config.serverUrl
|
|
95
95
|
)
|
|
96
|
+
|
|
96
97
|
break
|
|
97
98
|
case ServerType.Sas9:
|
|
98
99
|
jsonResponse =
|
|
99
100
|
typeof res.result === 'string'
|
|
100
101
|
? parseWeboutResponse(res.result, uploadUrl)
|
|
101
102
|
: res.result
|
|
103
|
+
|
|
104
|
+
break
|
|
105
|
+
case ServerType.Sasjs:
|
|
106
|
+
jsonResponse =
|
|
107
|
+
typeof res.result === 'string'
|
|
108
|
+
? getValidJson(res.result)
|
|
109
|
+
: res.result
|
|
110
|
+
|
|
102
111
|
break
|
|
103
112
|
}
|
|
104
|
-
} else
|
|
113
|
+
} else {
|
|
105
114
|
jsonResponse =
|
|
106
115
|
typeof res.result === 'string'
|
|
107
116
|
? getValidJson(res.result)
|