mirror of
https://github.com/sasjs/adapter.git
synced 2025-12-11 01:14:36 +00:00
fix: new axios requires form data content type, es6 strict issues with iterations
This commit is contained in:
15
sasjs-tests/craco.config.js
Normal file
15
sasjs-tests/craco.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// craco.config.js
|
||||||
|
// We use craco instead of react-scripts so we can override webpack config, to include source maps
|
||||||
|
// so we can debug @sasjs/adapter easier when tests fail
|
||||||
|
module.exports = {
|
||||||
|
webpack: {
|
||||||
|
configure: (webpackConfig, { env }) => {
|
||||||
|
// Disable optimizations in both development and production
|
||||||
|
webpackConfig.optimization.minimize = false;
|
||||||
|
webpackConfig.optimization.minimizer = [];
|
||||||
|
webpackConfig.optimization.concatenateModules = false;
|
||||||
|
webpackConfig.optimization.splitChunks = { cacheGroups: { default: false } };
|
||||||
|
return webpackConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
752
sasjs-tests/package-lock.json
generated
752
sasjs-tests/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
|
"start": "NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "NODE_OPTIONS=--openssl-legacy-provider craco build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"update:adapter": "cd .. && npm run package:lib && cd sasjs-tests && npm i ../build/sasjs-adapter-5.0.0.tgz",
|
"update:adapter": "cd .. && npm run package:lib && cd sasjs-tests && npm i ../build/sasjs-adapter-5.0.0.tgz",
|
||||||
@@ -43,6 +43,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"node-sass": "9.0.0"
|
"@craco/craco": "6.4.3",
|
||||||
|
"node-sass": "9.0.0",
|
||||||
|
"source-map-loader": "0.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,8 +134,19 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
|||||||
return adapter.request('common/sendArr', moreSpecialCharData)
|
return adapter.request('common/sendArr', moreSpecialCharData)
|
||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
// If sas session is latin9 we can't process the special characters
|
// If sas session is `latin9` or `wlatin1` we can't process the special characters,
|
||||||
if (res.SYSENCODING === 'latin9') return true
|
// But it can happen that response is broken JSON, so we first need to check if
|
||||||
|
// it's object and then check accordingly
|
||||||
|
|
||||||
|
if (typeof res === 'object') {
|
||||||
|
// Valid JSON response
|
||||||
|
if (res.SYSENCODING === 'latin9' || res.SYSENCODING === 'wlatin1')
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
// Since we got string response (broken JSON), we need to check with regex
|
||||||
|
const regex = /"SYSENCODING"\s*:\s*"(?:wlatin1|latin9)"/
|
||||||
|
if (regex.test(res)) return true
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
res.table1[0][0] === moreSpecialCharData.table1[0].speech0 &&
|
res.table1[0][0] === moreSpecialCharData.table1[0].speech0 &&
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
"esnext"
|
"esnext"
|
||||||
],
|
],
|
||||||
|
"sourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
|||||||
@@ -73,8 +73,10 @@ export class SasjsJobExecutor extends BaseJobExecutor {
|
|||||||
/* The NodeFormData object does not set the request header - so, set it */
|
/* The NodeFormData object does not set the request header - so, set it */
|
||||||
const contentType =
|
const contentType =
|
||||||
formData instanceof NodeFormData && typeof FormData === 'undefined'
|
formData instanceof NodeFormData && typeof FormData === 'undefined'
|
||||||
? `multipart/form-data; boundary=${formData.getBoundary()}`
|
? `multipart/form-data; boundary=${
|
||||||
: undefined
|
formData.getHeaders()['content-type']
|
||||||
|
}`
|
||||||
|
: 'multipart/form-data'
|
||||||
|
|
||||||
const requestPromise = new Promise((resolve, reject) => {
|
const requestPromise = new Promise((resolve, reject) => {
|
||||||
this.requestClient!.post(
|
this.requestClient!.post(
|
||||||
|
|||||||
@@ -150,8 +150,10 @@ export class WebJobExecutor extends BaseJobExecutor {
|
|||||||
/* The NodeFormData object does not set the request header - so, set it */
|
/* The NodeFormData object does not set the request header - so, set it */
|
||||||
const contentType =
|
const contentType =
|
||||||
formData instanceof NodeFormData && typeof FormData === 'undefined'
|
formData instanceof NodeFormData && typeof FormData === 'undefined'
|
||||||
? `multipart/form-data; boundary=${formData.getBoundary()}`
|
? `multipart/form-data; boundary=${
|
||||||
: undefined
|
formData.getHeaders()['content-type']
|
||||||
|
}`
|
||||||
|
: 'multipart/form-data'
|
||||||
|
|
||||||
const requestPromise = new Promise((resolve, reject) => {
|
const requestPromise = new Promise((resolve, reject) => {
|
||||||
this.requestClient!.post(
|
this.requestClient!.post(
|
||||||
|
|||||||
@@ -191,6 +191,13 @@ export class RequestClient implements HttpClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param contentType Newer version of Axios is more strict so if you don't
|
||||||
|
* set the contentType to `form data` while sending a FormData object
|
||||||
|
* application/json will be used by default, axios won’t treat it as FormData.
|
||||||
|
* Instead, it serializes data as JSON—resulting in a payload like
|
||||||
|
* {"sometable":{}} and we lose the multipart/form-data formatting.
|
||||||
|
*/
|
||||||
public async post<T>(
|
public async post<T>(
|
||||||
url: string,
|
url: string,
|
||||||
data: any,
|
data: any,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { isNode } from './'
|
import { isNode } from './'
|
||||||
import NodeFormData from 'form-data'
|
import NodeFormData from 'form-data'
|
||||||
|
|
||||||
export const getFormData = () =>
|
export const getFormData = (): NodeFormData | FormData =>
|
||||||
isNode() ? new NodeFormData() : new FormData()
|
isNode() ? new NodeFormData() : new FormData()
|
||||||
|
|||||||
@@ -52,19 +52,22 @@ export const validateInput = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const item of data[key]) {
|
// ES6 is stricter so we had to include the check for the array
|
||||||
if (getType(item) !== 'object') {
|
if (Array.isArray(data[key])) {
|
||||||
return {
|
for (const item of data[key]) {
|
||||||
status: false,
|
if (getType(item) !== 'object') {
|
||||||
msg: `Table ${key} contains invalid structure. ${MORE_INFO}`
|
return {
|
||||||
}
|
status: false,
|
||||||
} else {
|
msg: `Table ${key} contains invalid structure. ${MORE_INFO}`
|
||||||
const attributes = Object.keys(item)
|
}
|
||||||
for (const attribute of attributes) {
|
} else {
|
||||||
if (item[attribute] === undefined) {
|
const attributes = Object.keys(item)
|
||||||
return {
|
for (const attribute of attributes) {
|
||||||
status: false,
|
if (item[attribute] === undefined) {
|
||||||
msg: `A row in table ${key} contains invalid value. Can't assign undefined to ${attribute}.`
|
return {
|
||||||
|
status: false,
|
||||||
|
msg: `A row in table ${key} contains invalid value. Can't assign undefined to ${attribute}.`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
"typeRoots": ["./node_modules/@types", "./src/types/system"]
|
"typeRoots": ["./node_modules/@types", "./src/types/system"]
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ const defaultPlugins = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const optimization = {
|
const optimization = {
|
||||||
minimize: true,
|
minimize: false,
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new terserPlugin({
|
// new terserPlugin({
|
||||||
parallel: true,
|
// parallel: true,
|
||||||
terserOptions: {}
|
// terserOptions: {}
|
||||||
})
|
// })
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ const browserConfig = {
|
|||||||
},
|
},
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
optimization: optimization,
|
optimization: optimization,
|
||||||
|
devtool: 'inline-source-map',
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user