@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/package.json CHANGED
@@ -84,5 +84,5 @@
84
84
  "https": "1.0.0",
85
85
  "tough-cookie": "4.0.0"
86
86
  },
87
- "version": "4.3.7"
87
+ "version": "4.4.0"
88
88
  }
@@ -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(err, 'Error while getting access token. ')
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
- * Exchanges the auth code for an access token for the given client.
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 data = new URLSearchParams({
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(err, 'Error while getting access token. ')
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
@@ -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(err, 'Error while refreshing tokens: ')
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(err, 'Error while refreshing tokens: ')
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 if (this.serverType !== ServerType.Sasjs) {
113
+ } else {
105
114
  jsonResponse =
106
115
  typeof res.result === 'string'
107
116
  ? getValidJson(res.result)