mirror of
https://github.com/sasjs/adapter.git
synced 2026-07-24 07:02:14 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c5967fa10 | ||
|
|
a641a2fbdf | ||
|
|
5f1d2fa1c9 | ||
|
|
b7b56ecde1 | ||
|
|
39e5386346 | ||
|
|
33e28483c5 | ||
|
|
fa67b5b447 | ||
|
|
6f42f40f07 | ||
|
|
c0289583d5 | ||
|
|
b3921f7d05 |
@@ -11,21 +11,41 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: write # required for npm provenance (trusted publisher)
|
||||||
|
issues: write # optional: lets @semantic-release/github comment on issues
|
||||||
|
pull-requests: write # optional: lets @semantic-release/github comment on PRs
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [22]
|
node-version: [22]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
# 1. Mint a short-lived GitHub App token (bypass-capable)
|
||||||
|
- name: Generate token
|
||||||
|
id: app-token
|
||||||
|
uses: actions/create-github-app-token@v1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.APP_ID }}
|
||||||
|
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
# 2. Checkout using the app token so the release commit can be pushed
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
persist-credentials: true
|
||||||
|
fetch-depth: 0 # semantic-release needs full history + tags
|
||||||
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
registry-url: https://registry.npmjs.org
|
||||||
|
|
||||||
# 2. Restore npm cache manually
|
# 3. Restore npm cache manually
|
||||||
- name: Restore npm cache
|
- name: Restore npm cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: npm-cache
|
id: npm-cache
|
||||||
with:
|
with:
|
||||||
path: ~/.npm
|
path: ~/.npm
|
||||||
@@ -46,10 +66,15 @@ jobs:
|
|||||||
run: npm run publishInit
|
run: npm run publishInit
|
||||||
|
|
||||||
- name: Semantic Release
|
- name: Semantic Release
|
||||||
uses: cycjimmy/semantic-release-action@v3
|
run: npx semantic-release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} # <-- app token, not secrets.GITHUB_TOKEN
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
|
- name: Publish to npm with trusted publisher
|
||||||
|
if: success()
|
||||||
|
env:
|
||||||
|
NPM_CONFIG_PROVENANCE: true
|
||||||
|
run: npm publish --access public --provenance
|
||||||
|
|
||||||
- name: Send Matrix message
|
- name: Send Matrix message
|
||||||
run: curl -XPOST -d "{\"msgtype\":\"m.text\", \"body\":\"New version of @sasjs/adapter has been released! \n Please deploy and run 'dctests' with new adapter to make sure everything is still in place.\"}" https://matrix.4gl.io/_matrix/client/r0/rooms/!jRebyiGmHZlpfDwYXN:4gl.io/send/m.room.message?access_token=${{ secrets.MATRIX_TOKEN }}
|
run: curl -XPOST -d "{\"msgtype\":\"m.text\", \"body\":\"New version of @sasjs/adapter has been released! \n Please deploy and run 'dctests' with new adapter to make sure everything is still in place.\"}" https://matrix.4gl.io/_matrix/client/r0/rooms/!jRebyiGmHZlpfDwYXN:4gl.io/send/m.room.message?access_token=${{ secrets.MATRIX_TOKEN }}
|
||||||
|
|||||||
Generated
+153
-25
@@ -10,12 +10,14 @@
|
|||||||
"@sasjs/utils": "^3.5.6",
|
"@sasjs/utils": "^3.5.6",
|
||||||
"axios": "1.16.0",
|
"axios": "1.16.0",
|
||||||
"axios-cookiejar-support": "5.0.5",
|
"axios-cookiejar-support": "5.0.5",
|
||||||
"form-data": "4.0.4",
|
"form-data": "4.0.6",
|
||||||
"https": "1.0.0",
|
"https": "1.0.0",
|
||||||
"tough-cookie": "4.1.3"
|
"tough-cookie": "4.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/webpack-preprocessor": "5.9.1",
|
"@cypress/webpack-preprocessor": "5.9.1",
|
||||||
|
"@semantic-release/exec": "6.0.3",
|
||||||
|
"@semantic-release/git": "10.0.1",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "4.17.13",
|
"@types/express": "4.17.13",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
@@ -47,7 +49,8 @@
|
|||||||
"typescript": "4.9.5",
|
"typescript": "4.9.5",
|
||||||
"webpack": "5.76.2",
|
"webpack": "5.76.2",
|
||||||
"webpack-cli": "4.9.2"
|
"webpack-cli": "4.9.2"
|
||||||
}
|
},
|
||||||
|
"version": "4.17.1"
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
@@ -2471,6 +2474,144 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@semantic-release/exec": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@semantic-release/error": "^3.0.0",
|
||||||
|
"aggregate-error": "^3.0.0",
|
||||||
|
"debug": "^4.0.0",
|
||||||
|
"execa": "^5.0.0",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"parse-json": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"semantic-release": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@semantic-release/exec/node_modules/execa": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
|
"get-stream": "^6.0.0",
|
||||||
|
"human-signals": "^2.1.0",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"npm-run-path": "^4.0.1",
|
||||||
|
"onetime": "^5.1.2",
|
||||||
|
"signal-exit": "^3.0.3",
|
||||||
|
"strip-final-newline": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@semantic-release/exec/node_modules/get-stream": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@semantic-release/exec/node_modules/human-signals": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@semantic-release/git": {
|
||||||
|
"version": "10.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz",
|
||||||
|
"integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@semantic-release/error": "^3.0.0",
|
||||||
|
"aggregate-error": "^3.0.0",
|
||||||
|
"debug": "^4.0.0",
|
||||||
|
"dir-glob": "^3.0.0",
|
||||||
|
"execa": "^5.0.0",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"micromatch": "^4.0.0",
|
||||||
|
"p-reduce": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"semantic-release": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@semantic-release/git/node_modules/execa": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
|
"get-stream": "^6.0.0",
|
||||||
|
"human-signals": "^2.1.0",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"npm-run-path": "^4.0.1",
|
||||||
|
"onetime": "^5.1.2",
|
||||||
|
"signal-exit": "^3.0.3",
|
||||||
|
"strip-final-newline": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@semantic-release/git/node_modules/get-stream": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@semantic-release/git/node_modules/human-signals": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@semantic-release/github": {
|
"node_modules/@semantic-release/github": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -3556,22 +3697,6 @@
|
|||||||
"tough-cookie": ">=4.0.0"
|
"tough-cookie": ">=4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios/node_modules/form-data": {
|
|
||||||
"version": "4.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
|
||||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.8",
|
|
||||||
"es-set-tostringtag": "^2.1.0",
|
|
||||||
"hasown": "^2.0.2",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/babel-jest": {
|
"node_modules/babel-jest": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||||
@@ -6179,16 +6304,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
|
||||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
"es-set-tostringtag": "^2.1.0",
|
"es-set-tostringtag": "^2.1.0",
|
||||||
"hasown": "^2.0.2",
|
"hasown": "^2.0.4",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.35"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@@ -6616,7 +6741,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@@ -15242,5 +15369,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"version": "4.17.1"
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-6
@@ -23,10 +23,24 @@
|
|||||||
},
|
},
|
||||||
"release": {
|
"release": {
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@semantic-release/npm",
|
"@semantic-release/commit-analyzer",
|
||||||
{
|
"@semantic-release/release-notes-generator",
|
||||||
"pkgRoot": "/build"
|
[
|
||||||
}
|
"@semantic-release/exec",
|
||||||
|
{
|
||||||
|
"prepareCmd": "npm version ${nextRelease.version} --no-git-tag-version"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/git",
|
||||||
|
{
|
||||||
|
"assets": [
|
||||||
|
"package.json",
|
||||||
|
"package-lock.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@semantic-release/github"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -42,6 +56,8 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/webpack-preprocessor": "5.9.1",
|
"@cypress/webpack-preprocessor": "5.9.1",
|
||||||
|
"@semantic-release/exec": "6.0.3",
|
||||||
|
"@semantic-release/git": "10.0.1",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "4.17.13",
|
"@types/express": "4.17.13",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
@@ -79,8 +95,9 @@
|
|||||||
"@sasjs/utils": "^3.5.6",
|
"@sasjs/utils": "^3.5.6",
|
||||||
"axios": "1.16.0",
|
"axios": "1.16.0",
|
||||||
"axios-cookiejar-support": "5.0.5",
|
"axios-cookiejar-support": "5.0.5",
|
||||||
"form-data": "4.0.4",
|
"form-data": "4.0.6",
|
||||||
"https": "1.0.0",
|
"https": "1.0.0",
|
||||||
"tough-cookie": "4.1.3"
|
"tough-cookie": "4.1.3"
|
||||||
}
|
},
|
||||||
|
"version": "4.17.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ export class AuthManager {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public async logOut() {
|
public async logOut() {
|
||||||
this.requestClient.clearCsrfTokens()
|
this.requestClient.resetInMemoryAuthState()
|
||||||
|
|
||||||
return this.requestClient.get(this.logoutUrl, undefined).then(() => true)
|
return this.requestClient.get(this.logoutUrl, undefined).then(() => true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ import {
|
|||||||
import { InvalidSASjsCsrfError } from '../types/errors/InvalidSASjsCsrfError'
|
import { InvalidSASjsCsrfError } from '../types/errors/InvalidSASjsCsrfError'
|
||||||
import { inspect } from 'util'
|
import { inspect } from 'util'
|
||||||
|
|
||||||
|
const getLogger = () =>
|
||||||
|
(typeof process !== 'undefined' && process.logger) || console
|
||||||
|
|
||||||
export class RequestClient implements HttpClient {
|
export class RequestClient implements HttpClient {
|
||||||
private requests: SASjsRequest[] = []
|
private requests: SASjsRequest[] = []
|
||||||
private requestsLimit: number = 10
|
private requestsLimit: number = 10
|
||||||
@@ -37,6 +40,7 @@ export class RequestClient implements HttpClient {
|
|||||||
protected csrfToken: CsrfToken = { headerName: '', value: '' }
|
protected csrfToken: CsrfToken = { headerName: '', value: '' }
|
||||||
protected fileUploadCsrfToken: CsrfToken | undefined
|
protected fileUploadCsrfToken: CsrfToken | undefined
|
||||||
protected httpClient!: AxiosInstance
|
protected httpClient!: AxiosInstance
|
||||||
|
private isRecoveringFromNetworkError = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected baseUrl: string,
|
protected baseUrl: string,
|
||||||
@@ -77,6 +81,25 @@ export class RequestClient implements HttpClient {
|
|||||||
localStorage.setItem('refreshToken', '')
|
localStorage.setItem('refreshToken', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resetInMemoryAuthState() {
|
||||||
|
this.clearCsrfTokens()
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
this.clearLocalStorageTokens()
|
||||||
|
}
|
||||||
|
if (typeof document !== 'undefined') {
|
||||||
|
this.clearAllCookies()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearAllCookies() {
|
||||||
|
const cookies = document.cookie.split(';')
|
||||||
|
for (const cookie of cookies) {
|
||||||
|
const name = cookie.split('=')[0].trim()
|
||||||
|
if (!name) continue
|
||||||
|
document.cookie = `${name}=; Max-Age=0; Path=/;`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getBaseUrl() {
|
public getBaseUrl() {
|
||||||
return this.httpClient.defaults.baseURL || ''
|
return this.httpClient.defaults.baseURL || ''
|
||||||
}
|
}
|
||||||
@@ -354,6 +377,7 @@ export class RequestClient implements HttpClient {
|
|||||||
const csrfTokenKey = Object.keys(params).find((k) =>
|
const csrfTokenKey = Object.keys(params).find((k) =>
|
||||||
k?.toLowerCase().includes('csrf')
|
k?.toLowerCase().includes('csrf')
|
||||||
)
|
)
|
||||||
|
|
||||||
if (csrfTokenKey) {
|
if (csrfTokenKey) {
|
||||||
this.csrfToken.value = params[csrfTokenKey]
|
this.csrfToken.value = params[csrfTokenKey]
|
||||||
this.csrfToken.headerName = this.csrfToken.headerName || 'x-csrf-token'
|
this.csrfToken.headerName = this.csrfToken.headerName || 'x-csrf-token'
|
||||||
@@ -378,7 +402,7 @@ export class RequestClient implements HttpClient {
|
|||||||
})
|
})
|
||||||
.then((res) => res.data)
|
.then((res) => res.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const logger = process.logger || console
|
const logger = getLogger()
|
||||||
logger.error(error)
|
logger.error(error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -687,6 +711,45 @@ ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
|
|||||||
throw new CertificateError(e.message)
|
throw new CertificateError(e.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
e.isAxiosError &&
|
||||||
|
!response &&
|
||||||
|
e.code === 'ERR_NETWORK' &&
|
||||||
|
!this.isRecoveringFromNetworkError
|
||||||
|
) {
|
||||||
|
// Opaque ERR_NETWORK usually means the server rejected stale credentials.
|
||||||
|
// Wipe in-memory auth state, re-establish session via GET /,
|
||||||
|
// then retry the original request.
|
||||||
|
this.resetInMemoryAuthState()
|
||||||
|
this.isRecoveringFromNetworkError = true
|
||||||
|
try {
|
||||||
|
// Re-establish session and CSRF cookie
|
||||||
|
const rootResponse = await this.httpClient
|
||||||
|
.get('/', { withXSRFToken: true })
|
||||||
|
.catch((err) => err.response)
|
||||||
|
|
||||||
|
if (rootResponse?.data) {
|
||||||
|
const cookie =
|
||||||
|
/<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/.exec(
|
||||||
|
rootResponse.data
|
||||||
|
)?.[1]
|
||||||
|
|
||||||
|
if (cookie && typeof document !== 'undefined') {
|
||||||
|
document.cookie = cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parseAndSetCsrfToken(rootResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await callback()
|
||||||
|
} catch {
|
||||||
|
// Session could not be recovered — surface LoginRequiredError
|
||||||
|
throw new LoginRequiredError()
|
||||||
|
} finally {
|
||||||
|
this.isRecoveringFromNetworkError = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (e.message) throw e
|
if (e.message) throw e
|
||||||
else throw prefixMessage(e, 'Error while handling error. ')
|
else throw prefixMessage(e, 'Error while handling error. ')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ export class Sas9RequestClient extends RequestClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resetInMemoryAuthState() {
|
||||||
|
super.resetInMemoryAuthState()
|
||||||
|
if (this.httpClient.defaults.jar) {
|
||||||
|
;(this.httpClient.defaults.jar as tough.CookieJar).removeAllCookiesSync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async login(username: string, password: string, jobsPath: string) {
|
public async login(username: string, password: string, jobsPath: string) {
|
||||||
const codeInjectorPath = `/User Folders/${username}/My Folder/sasjs/runner`
|
const codeInjectorPath = `/User Folders/${username}/My Folder/sasjs/runner`
|
||||||
if (this.httpClient.defaults.jar) {
|
if (this.httpClient.defaults.jar) {
|
||||||
|
|||||||
@@ -589,6 +589,42 @@ ${resHeaders[0]}: ${resHeaders[1]}${
|
|||||||
requestClient['handleError'](error, () => {}, false)
|
requestClient['handleError'](error, () => {}, false)
|
||||||
).resolves.toEqual(undefined)
|
).resolves.toEqual(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should clear CSRF and retry once on opaque ERR_NETWORK', async () => {
|
||||||
|
const networkError = {
|
||||||
|
isAxiosError: true,
|
||||||
|
code: 'ERR_NETWORK',
|
||||||
|
message: 'Network Error'
|
||||||
|
}
|
||||||
|
requestClient['csrfToken'] = { headerName: 'h', value: 'v' }
|
||||||
|
const callback = jest.fn().mockResolvedValue('ok')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
requestClient['handleError'](networkError, callback)
|
||||||
|
).resolves.toEqual('ok')
|
||||||
|
|
||||||
|
expect(callback).toHaveBeenCalledTimes(1)
|
||||||
|
expect(requestClient['csrfToken']).toEqual({ headerName: '', value: '' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw LoginRequiredError if retry also fails with ERR_NETWORK', async () => {
|
||||||
|
const networkError = {
|
||||||
|
isAxiosError: true,
|
||||||
|
code: 'ERR_NETWORK',
|
||||||
|
message: 'Network Error'
|
||||||
|
}
|
||||||
|
const innerHandle = jest.fn(() =>
|
||||||
|
requestClient['handleError'](networkError, () =>
|
||||||
|
Promise.reject(networkError)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
requestClient['handleError'](networkError, innerHandle)
|
||||||
|
).rejects.toThrow(LoginRequiredError)
|
||||||
|
|
||||||
|
expect(innerHandle).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user