1
0
mirror of https://github.com/sasjs/adapter.git synced 2025-12-28 08:00:05 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Allan Bowe
98c492e85e Merge pull request #729 from sasjs/update-AuthManager
fix: update logout url
2022-06-21 22:10:26 +02:00
d1fcc2ca0a fix: update logout url 2022-06-22 00:53:49 +05:00
Yury Shkoda
122f302bae Merge pull request #728 from sasjs/deps-fix
fix(workflow): added actions/setup-node@v2
2022-06-20 20:44:57 +03:00
Yury Shkoda
c3a0ad1f41 fix(workflow): added actions/setup-node@v2 2022-06-20 20:43:11 +03:00
Yury Shkoda
a28b48f815 Merge pull request #726 from sasjs/deps-fix
fix(workflows): fixed npmpublish workflow
2022-06-20 20:36:13 +03:00
Yury Shkoda
9b6a42e412 fix(workflows): fixed npmpublish workflow 2022-06-20 20:33:44 +03:00
Allan Bowe
db60962c1e Merge pull request #725 from sasjs/allanbowe-patch-1
fix: bumping with README updates
2022-06-20 19:29:15 +02:00
Allan Bowe
1eae59ad3b fix: bumping with README updates 2022-06-20 18:28:47 +01:00
Allan Bowe
d485023d65 Update dependabot.yml 2022-06-20 18:09:07 +01:00
Allan Bowe
c2f21babb4 Merge pull request #723 from sasjs/deps-fix
Regenerated package-lock and fixed linting issues
2022-06-20 19:03:17 +02:00
Yury Shkoda
dd788ae423 chore(lint): fixed linting 2022-06-20 19:48:42 +03:00
Yury Shkoda
a113c95441 chore(deps): added prettier dev dependency 2022-06-20 19:36:12 +03:00
Yury Shkoda
489947bcae chore(lint): fixed linting issues 2022-06-20 19:26:29 +03:00
Yury Shkoda
1596173dda fix(deps): regenerated package-lock 2022-06-20 19:24:09 +03:00
Allan Bowe
bb1b2ddcb2 Merge pull request #719 from sasjs/issue-718
fix: parse the logs before appending the request to request array whe…
2022-06-20 17:53:44 +02:00
a3cc274ef1 chore: no need to appendRequest from then block when there is jobExecutionError 2022-06-10 15:38:03 +05:00
451d0906fa chore: update error message 2022-06-10 15:31:09 +05:00
dd6b89b0d0 fix: parse the logs before appending the request to request array when server type is sasjs 2022-06-09 23:11:47 +05:00
29 changed files with 5656 additions and 2533 deletions

View File

@@ -1,7 +1,7 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: monthly
interval: "monthly"
open-pull-requests-limit: 2

View File

@@ -11,10 +11,19 @@ on:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [lts/fermium]
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install Dependencies
run: npm ci
@@ -23,7 +32,7 @@ jobs:
- name: Build Project
run: npm run build
- name: Semantic Release
uses: cycjimmy/semantic-release-action@v2
env:

View File

@@ -2,7 +2,6 @@
[![npm package][npm-image]][npm-url]
[![Github Workflow][githubworkflow-image]][githubworkflow-url]
[![Dependency Status][dependency-image]][dependency-url]
[![npm](https://img.shields.io/npm/dt/@sasjs/adapter)]()
![Snyk Vulnerabilities for npm package](https://img.shields.io/snyk/vulnerabilities/npm/@sasjs/adapter)
[![License](https://img.shields.io/apm/l/atomic-design-ui.svg)](/LICENSE)
@@ -16,7 +15,6 @@
[githubworkflow-image]:https://github.com/sasjs/adapter/actions/workflows/build.yml/badge.svg
[githubworkflow-url]:https://github.com/sasjs/adapter/blob/main/.github/workflows/build.yml
[dependency-image]:https://david-dm.org/sasjs/adapter.svg
[dependency-url]:https://github.com/sasjs/adapter/blob/main/package.json
SASjs is a open-source framework for building Web Apps on SAS® platforms. You can use as much or as little of it as you like. This repository contains the JS adapter, the part that handles the to/from SAS communication on the client side. There are 3 ways to install it:
@@ -32,7 +30,7 @@ For more information on building web apps with SAS, check out [sasjs.io](https:/
## None of this makes sense. How do I build an app with it?
Ok ok. Deploy this [example.html](https://raw.githubusercontent.com/sasjs/adapter/master/example.html) file to your web server, and update `servertype` to `SAS9` or `SASVIYA` depending on your backend.
Ok ok. Deploy this [example.html](https://raw.githubusercontent.com/sasjs/adapter/master/example.html) file to your web server, and update `servertype` to `SAS9`, `SASVIYA`, or `SASJS` depending on your backend.
The backend part can be deployed as follows:
@@ -52,7 +50,7 @@ parmcards4;
%webout(OBJ,areas)
%webout(CLOSE)
;;;;
%mp_createwebservice(path=&appLoc/common,name=getdata)
%mx_createwebservice(path=&appLoc/common,name=getdata)
```
You now have a simple web app with a backend service!
@@ -221,7 +219,7 @@ The SAS side is handled by a number of macros in the [macro core](https://github
The following snippet shows the process of SAS tables arriving / leaving:
```sas
/* fetch all input tables sent from frontend - they arrive as work tables */
/* convert frontend input tables from into SASWORK datasets */
%webout(FETCH)
/* some sas code */
@@ -314,7 +312,7 @@ For more information and examples specific to this adapter you can check out the
For more information on building web apps in general, check out these [resources](https://sasjs.io/training/resources/) or contact the [author](https://www.linkedin.com/in/allanbowe/) directly.
If you are a SAS 9 or SAS Viya customer you can also request a copy of [Data Controller](https://datacontroller.io) - free for up to 5 users, this tool makes use of all parts of the SASjs framework.
As a SAS customer you can also request a copy of [Data Controller](https://datacontroller.io) - free for up to 5 users, this tool makes use of all parts of the SASjs framework.
## Star Gazing

8004
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -44,18 +44,16 @@
"license": "ISC",
"devDependencies": {
"@cypress/webpack-preprocessor": "5.9.1",
"@types/form-data": "2.5.0",
"cypress": "7.7.0",
"typedoc-neo-theme": "1.1.1",
"typedoc-plugin-external-module-name": "4.0.6",
"@types/axios": "0.14.0",
"@types/express": "4.17.13",
"@types/form-data": "2.5.0",
"@types/jest": "27.4.0",
"@types/mime": "2.0.3",
"@types/pem": "1.9.6",
"@types/tough-cookie": "4.0.1",
"copyfiles": "2.4.1",
"cp": "0.2.0",
"cypress": "7.7.0",
"dotenv": "16.0.0",
"express": "4.17.3",
"jest": "27.4.7",
@@ -63,6 +61,7 @@
"node-polyfill-webpack-plugin": "1.1.4",
"path": "0.12.7",
"pem": "1.14.6",
"prettier": "2.7.1",
"process": "0.11.10",
"rimraf": "3.0.2",
"semantic-release": "18.0.0",
@@ -72,6 +71,8 @@
"tslint": "6.1.3",
"tslint-config-prettier": "1.18.0",
"typedoc": "0.22.11",
"typedoc-neo-theme": "1.1.1",
"typedoc-plugin-external-module-name": "4.0.6",
"typedoc-plugin-rename-defaults": "0.4.0",
"typescript": "4.5.5",
"webpack": "5.69.0",

View File

@@ -9,7 +9,7 @@ const Login = (): ReactElement<{}> => {
const appContext = useContext(AppContext)
const handleSubmit = useCallback(
(e) => {
(e: any) => {
e.preventDefault()
appContext.adapter.logIn(username, password).then((res) => {
appContext.setIsLoggedIn(res.isLoggedIn)
@@ -28,7 +28,7 @@ const Login = (): ReactElement<{}> => {
placeholder="User Name"
value={username}
required
onChange={(e) => setUsername(e.target.value)}
onChange={(e: any) => setUsername(e.target.value)}
/>
</div>
<div className="row">
@@ -38,7 +38,7 @@ const Login = (): ReactElement<{}> => {
type="password"
value={password}
required
onChange={(e) => setPassword(e.target.value)}
onChange={(e: any) => setPassword(e.target.value)}
/>
</div>
<button type="submit" className="submit-button">

View File

@@ -86,7 +86,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
'Should error out with long string values over 32765 characters',
test: () => {
const data = getLongStringData(32767)
return adapter.request('common/sendArr', data).catch((e) => e)
return adapter.request('common/sendArr', data).catch((e: any) => e)
},
assertion: (error: any) => {
return !!error && !!error.error && !!error.error.message
@@ -182,7 +182,9 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
const invalidData: any = {
'1InvalidTable': [{ col1: 42 }]
}
return adapter.request('common/sendObj', invalidData).catch((e) => e)
return adapter
.request('common/sendObj', invalidData)
.catch((e: any) => e)
},
assertion: (error: any) =>
!!error && !!error.error && !!error.error.message
@@ -194,7 +196,9 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
const invalidData: any = {
'an invalidTable': [{ col1: 42 }]
}
return adapter.request('common/sendObj', invalidData).catch((e) => e)
return adapter
.request('common/sendObj', invalidData)
.catch((e: any) => e)
},
assertion: (error: any) =>
!!error && !!error.error && !!error.error.message
@@ -206,7 +210,9 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
const invalidData: any = {
'anInvalidTable#': [{ col1: 42 }]
}
return adapter.request('common/sendObj', invalidData).catch((e) => e)
return adapter
.request('common/sendObj', invalidData)
.catch((e: any) => e)
},
assertion: (error: any) =>
!!error && !!error.error && !!error.error.message
@@ -219,7 +225,9 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: [{ col1: 42 }]
}
return adapter.request('common/sendObj', invalidData).catch((e) => e)
return adapter
.request('common/sendObj', invalidData)
.catch((e: any) => e)
},
assertion: (error: any) =>
!!error && !!error.error && !!error.error.message
@@ -231,7 +239,9 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
const invalidData: any = {
inData: [[{ data: 'value' }]]
}
return adapter.request('common/sendObj', invalidData).catch((e) => e)
return adapter
.request('common/sendObj', invalidData)
.catch((e: any) => e)
},
assertion: (error: any) =>
!!error && !!error.error && !!error.error.message
@@ -265,7 +275,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
test: () => {
return adapter
.request('common/sendObj', getLongStringData(32767))
.catch((e) => e)
.catch((e: any) => e)
},
assertion: (error: any) => {
return !!error && !!error.error && !!error.error.message

View File

@@ -9,7 +9,7 @@ export const assert = (
} else {
result = expression()
}
} catch (e) {
} catch (e: any) {
console.error(message)
throw new Error(message)
}

View File

@@ -31,7 +31,7 @@ describe('SASViyaApiClient', () => {
.mockImplementation(() => Promise.reject('Not Found'))
const error = await sasViyaApiClient
.createFolder('test', '/foo')
.catch((e) => e)
.catch((e: any) => e)
expect(error).toBeInstanceOf(RootFolderNotFoundError)
})
})

View File

@@ -240,7 +240,7 @@ export async function executeScript(
jobResult = await requestClient
.get<any>(resultLink, access_token, 'text/plain')
.catch(async (e) => {
.catch(async (e: any) => {
if (e instanceof NotFoundError) {
if (logLink) {
const logUrl = `${logLink.href}/content`
@@ -271,7 +271,7 @@ export async function executeScript(
})
return { result: jobResult?.result, log }
} catch (e) {
} catch (e: any) {
interface HttpError {
status: number
}

View File

@@ -80,7 +80,7 @@ describe('executeScript', () => {
'test',
['%put hello'],
'test context'
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while getting session.')
})
@@ -128,7 +128,7 @@ describe('executeScript', () => {
false,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while getting session variable.')
})
@@ -297,7 +297,7 @@ describe('executeScript', () => {
false,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while posting job')
})
@@ -367,7 +367,7 @@ describe('executeScript', () => {
true,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while polling job status.')
})
@@ -393,7 +393,7 @@ describe('executeScript', () => {
true,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(fetchLogsModule.fetchLogByChunks).toHaveBeenCalledWith(
requestClient,
@@ -468,7 +468,7 @@ describe('executeScript', () => {
true,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(fetchLogsModule.fetchLogByChunks).toHaveBeenCalledWith(
requestClient,
@@ -501,7 +501,7 @@ describe('executeScript', () => {
true,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(fetchLogsModule.fetchLogByChunks).toHaveBeenCalledWith(
requestClient,
@@ -561,7 +561,7 @@ describe('executeScript', () => {
true,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(requestClient.get).toHaveBeenCalledWith(
`/compute/sessions/${mockSession.id}/filerefs/_webout/content`,
@@ -622,7 +622,7 @@ describe('executeScript', () => {
true,
defaultPollOptions,
true
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while clearing session.')
})

View File

@@ -59,7 +59,7 @@ describe('pollJobState', () => {
false,
undefined,
defaultPollOptions
).catch((e) => e)
).catch((e: any) => e)
expect((error as Error).message).toContain('Job state link was not found.')
})
@@ -238,7 +238,7 @@ describe('pollJobState', () => {
false,
undefined,
defaultPollOptions
).catch((e) => e)
).catch((e: any) => e)
expect(error.message).toEqual(
'Error while polling job state for job j0b: Status Error'

View File

@@ -17,7 +17,7 @@ describe('saveLog', () => {
it('should throw an error when a valid access token is not provided', async () => {
const error = await saveLog(mockJob, requestClient, 0, 100, stream).catch(
(e) => e
(e: any) => e
)
expect(error.message).toContain(
@@ -33,7 +33,7 @@ describe('saveLog', () => {
100,
stream,
't0k3n'
).catch((e) => e)
).catch((e: any) => e)
expect(error.message).toContain(
`Log URL for job ${mockJob.id} was not found.`

View File

@@ -30,7 +30,7 @@ describe('uploadTables', () => {
.mockImplementation(() => 'ERROR: LARGE STRING LENGTH')
const error = await uploadTables(requestClient, data, 't0k3n').catch(
(e) => e
(e: any) => e
)
expect(requestClient.uploadFile).not.toHaveBeenCalled()
@@ -46,7 +46,7 @@ describe('uploadTables', () => {
.mockImplementation(() => Promise.reject('Upload Error'))
const error = await uploadTables(requestClient, data, 't0k3n').catch(
(e) => e
(e: any) => e
)
expect(error).toContain('Error while uploading file.')

View File

@@ -28,7 +28,7 @@ describe('writeStream', () => {
jest
.spyOn(stream, 'write')
.mockImplementation((_, callback) => callback(new Error('Test Error')))
const error = await writeStream(stream, content).catch((e) => e)
const error = await writeStream(stream, content).catch((e: any) => e)
expect(error.message).toEqual('Test Error')
})

View File

@@ -4,7 +4,7 @@ export const writeStream = async (
stream: WriteStream,
content: string
): Promise<void> =>
stream.write(content + '\n', (e) => {
stream.write(content + '\n', (e: any) => {
if (e) return Promise.reject(e)
return Promise.resolve()

View File

@@ -23,7 +23,7 @@ export class AuthManager {
? '/SASLogon/logout?'
: this.serverType === ServerType.SasViya
? '/SASLogon/logout.do?'
: '/SASjsApi/auth/logout'
: '/SASLogon/logout'
}
/**
@@ -334,19 +334,9 @@ export class AuthManager {
/**
* Logs out of the configured SAS server.
* @param accessToken - an optional access token is required for SASjs server type.
*
*/
public async logOut() {
if (this.serverType === ServerType.Sasjs) {
return this.requestClient
.delete(this.logoutUrl)
.catch(() => true)
.finally(() => {
this.requestClient.clearLocalStorageTokens()
return true
})
}
this.requestClient.clearCsrfTokens()
return this.requestClient.get(this.logoutUrl, undefined).then(() => true)

View File

@@ -53,7 +53,7 @@ describe('getAccessTokenForSasjs', () => {
requestClient,
authConfig.client,
authConfig.refresh_token
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while getting access token')
})

View File

@@ -64,7 +64,7 @@ describe('getAccessTokenForViya', () => {
authConfig.client,
authConfig.secret,
authConfig.refresh_token
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while getting access token')
})

View File

@@ -62,7 +62,9 @@ describe('getTokens', () => {
const expectedError =
'Unable to obtain new access token. Your refresh token has expired.'
const error = await getTokens(requestClient, authConfig).catch((e) => e)
const error = await getTokens(requestClient, authConfig).catch(
(e: any) => e
)
expect(error.message).toEqual(expectedError)
})

View File

@@ -35,7 +35,7 @@ describe('refreshTokensForSasjs', () => {
const error = await refreshTokensForSasjs(
requestClient,
refresh_token
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while refreshing tokens')
})

View File

@@ -63,7 +63,7 @@ describe('refreshTokensForViya', () => {
authConfig.client,
authConfig.secret,
authConfig.refresh_token
).catch((e) => e)
).catch((e: any) => e)
expect(error).toContain('Error while refreshing tokens')
})

View File

@@ -7,7 +7,6 @@ describe('generateFileUploadForm', () => {
}
const BlobMock = jest.fn()
;(global as any).FormData = FormDataMock
;(global as any).Blob = BlobMock
})

View File

@@ -169,15 +169,23 @@ export class WebJobExecutor extends BaseJobExecutor {
? res.result.log.map((logLine: any) => logLine.line).join('\n')
: res.result.log
const resObj = res
const resObj =
this.serverType === ServerType.Sasjs
? {
result: res.result._webout,
log: parsedSasjsServerLog
}
: res
if (this.serverType === ServerType.Sasjs) {
if (res.result._webout.length < 1)
throw new JobExecutionError(
0,
'Job execution failed',
parsedSasjsServerLog
)
if (
this.serverType === ServerType.Sasjs &&
res.result._webout.length < 1
) {
throw new JobExecutionError(
0,
`No webout was returned by job ${program}. Server type is SASJS and the calling function is WebJobExecutor. Please check the SAS log for more info.`,
parsedSasjsServerLog
)
}
this.requestClient!.appendRequest(resObj, sasJob, config.debug)

View File

@@ -207,7 +207,7 @@ export class RequestClient implements HttpClient {
return this.parseResponse<T>(response)
})
.catch(async (e) => {
.catch(async (e: any) => {
return await this.handleError(
e,
() =>
@@ -248,7 +248,7 @@ export class RequestClient implements HttpClient {
return this.parseResponse<T>(response)
})
.catch(async (e) => {
.catch(async (e: any) => {
return await this.handleError(e, () =>
this.post<T>(url, data, accessToken, contentType, overrideHeaders)
)
@@ -272,7 +272,7 @@ export class RequestClient implements HttpClient {
throwIfError(response)
return this.parseResponse<T>(response)
})
.catch(async (e) => {
.catch(async (e: any) => {
return await this.handleError(e, () =>
this.put<T>(url, data, accessToken, overrideHeaders)
)
@@ -291,7 +291,7 @@ export class RequestClient implements HttpClient {
throwIfError(response)
return this.parseResponse<T>(response)
})
.catch(async (e) => {
.catch(async (e: any) => {
return await this.handleError(e, () => this.delete<T>(url, accessToken))
})
}
@@ -309,7 +309,7 @@ export class RequestClient implements HttpClient {
throwIfError(response)
return this.parseResponse<T>(response)
})
.catch(async (e) => {
.catch(async (e: any) => {
return await this.handleError(e, () =>
this.patch<T>(url, data, accessToken)
)

View File

@@ -68,7 +68,7 @@ export class Sas9RequestClient extends RequestClient {
throwIfError(response)
return this.parseResponse<T>(response)
})
.catch(async (e) => {
.catch(async (e: any) => {
return await this.handleError(
e,
() =>
@@ -113,7 +113,7 @@ export class Sas9RequestClient extends RequestClient {
throwIfError(response)
return this.parseResponse<T>(response)
})
.catch(async (e) => {
.catch(async (e: any) => {
return await this.handleError(e, () =>
this.post<T>(url, data, accessToken, contentType, overrideHeaders)
)

View File

@@ -15,7 +15,7 @@ export const getValidJson = (str: string | object): object => {
if (str === '') return {}
return JSON.parse(str)
} catch (e) {
} catch (e: any) {
if (e instanceof JsonParseArrayError) throw e
throw new InvalidJsonError()
}

View File

@@ -4,7 +4,7 @@ export const parseSasViyaLog = (logResponse: { items: any[] }) => {
log = logResponse.items
? logResponse.items.map((i) => i.line).join('\n')
: JSON.stringify(logResponse)
} catch (e) {
} catch (e: any) {
console.error('An error has occurred while parsing the log response', e)
log = logResponse
}

View File

@@ -8,7 +8,7 @@ export const parseWeboutResponse = (response: string, url?: string): string => {
sasResponse = response
.split('>>weboutBEGIN<<')[1]
.split('>>weboutEND<<')[0]
} catch (e) {
} catch (e: any) {
if (url) throw new WeboutResponseError(url)
sasResponse = ''