mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-05 11:40:06 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
504777603c | ||
| 706cbe5513 | |||
| 88eadd27aa | |||
| 4ed9f87434 |
72
src/SASjs.ts
72
src/SASjs.ts
@@ -1,4 +1,4 @@
|
|||||||
import { compareTimestamps, asyncForEach } from './utils'
|
import { compareTimestamps, asyncForEach, validateInput } from './utils'
|
||||||
import {
|
import {
|
||||||
SASjsConfig,
|
SASjsConfig,
|
||||||
UploadFile,
|
UploadFile,
|
||||||
@@ -686,7 +686,7 @@ export default class SASjs {
|
|||||||
...config
|
...config
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationResult = this.validateInput(data)
|
const validationResult = validateInput(data)
|
||||||
|
|
||||||
// status is true if the data passes validation checks above
|
// status is true if the data passes validation checks above
|
||||||
if (validationResult.status) {
|
if (validationResult.status) {
|
||||||
@@ -748,74 +748,6 @@ export default class SASjs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This function validates the input data structure and table naming convention
|
|
||||||
*
|
|
||||||
* @param data A json object that contains one or more tables, it can also be null
|
|
||||||
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
||||||
*/
|
|
||||||
private validateInput(data: { [key: string]: any } | null): {
|
|
||||||
status: boolean
|
|
||||||
msg: string
|
|
||||||
} {
|
|
||||||
if (data === null) return { status: true, msg: '' }
|
|
||||||
|
|
||||||
const isSasFormatsTable = (key: string) =>
|
|
||||||
key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
|
|
||||||
|
|
||||||
for (const key in data) {
|
|
||||||
if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
msg: 'First letter of table should be alphabet or underscore.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
|
|
||||||
return { status: false, msg: 'Table name should be alphanumeric.' }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.length > 32) {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
msg: 'Maximum length for table name could be 32 characters.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
msg: 'Parameter data contains invalid table structure.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < data[key].length; i++) {
|
|
||||||
if (this.getType(data[key][i]) !== 'object') {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
msg: `Table ${key} contains invalid structure.`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { status: true, msg: '' }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this function returns the type of variable
|
|
||||||
*
|
|
||||||
* @param data it could be anything, like string, array, object etc.
|
|
||||||
* @returns a string which tells the type of input parameter
|
|
||||||
*/
|
|
||||||
private getType(data: any): string {
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
return 'Array'
|
|
||||||
} else {
|
|
||||||
return typeof data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
|
* Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
|
||||||
* @param serviceJson - the JSON specifying the folders and services to be created.
|
* @param serviceJson - the JSON specifying the folders and services to be created.
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
|
export * from './appendExtraResponseAttributes'
|
||||||
export * from './asyncForEach'
|
export * from './asyncForEach'
|
||||||
export * from './compareTimestamps'
|
export * from './compareTimestamps'
|
||||||
export * from './convertToCsv'
|
export * from './convertToCsv'
|
||||||
export * from './createAxiosInstance'
|
export * from './createAxiosInstance'
|
||||||
export * from './delay'
|
export * from './delay'
|
||||||
|
export * from './fetchLogByChunks'
|
||||||
|
export * from './getValidJson'
|
||||||
export * from './isNode'
|
export * from './isNode'
|
||||||
export * from './isRelativePath'
|
export * from './isRelativePath'
|
||||||
export * from './isUri'
|
export * from './isUri'
|
||||||
export * from './isUrl'
|
export * from './isUrl'
|
||||||
export * from './needsRetry'
|
export * from './needsRetry'
|
||||||
export * from './parseGeneratedCode'
|
export * from './parseGeneratedCode'
|
||||||
export * from './parseSourceCode'
|
|
||||||
export * from './parseSasViyaLog'
|
export * from './parseSasViyaLog'
|
||||||
|
export * from './parseSourceCode'
|
||||||
|
export * from './parseViyaDebugResponse'
|
||||||
|
export * from './parseWeboutResponse'
|
||||||
export * from './serialize'
|
export * from './serialize'
|
||||||
export * from './splitChunks'
|
export * from './splitChunks'
|
||||||
export * from './parseWeboutResponse'
|
export * from './validateInput'
|
||||||
export * from './fetchLogByChunks'
|
|
||||||
export * from './getValidJson'
|
|
||||||
export * from './parseViyaDebugResponse'
|
|
||||||
export * from './appendExtraResponseAttributes'
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { convertToCSV, isFormatsTable } from './convertToCsv'
|
import { convertToCSV, isFormatsTable } from '../convertToCsv'
|
||||||
|
|
||||||
describe('convertToCsv', () => {
|
describe('convertToCsv', () => {
|
||||||
const tableName = 'testTable'
|
const tableName = 'testTable'
|
||||||
84
src/utils/spec/validateInput.spec.ts
Normal file
84
src/utils/spec/validateInput.spec.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import {
|
||||||
|
validateInput,
|
||||||
|
INVALID_TABLE_STRUCTURE,
|
||||||
|
MORE_INFO
|
||||||
|
} from '../validateInput'
|
||||||
|
|
||||||
|
const tableArray = [{ col1: 'first col value' }]
|
||||||
|
const stringData: any = { table1: tableArray }
|
||||||
|
|
||||||
|
describe('validateInput', () => {
|
||||||
|
it('should not return an error message if input data valid', () => {
|
||||||
|
const validationResult = validateInput(stringData)
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: true,
|
||||||
|
msg: ''
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not return an error message if input data is null', () => {
|
||||||
|
const validationResult = validateInput(null)
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: true,
|
||||||
|
msg: ''
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an error message if input data is an array', () => {
|
||||||
|
const validationResult = validateInput(tableArray)
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: false,
|
||||||
|
msg: INVALID_TABLE_STRUCTURE
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an error message if first letter of table is neither alphabet nor underscore', () => {
|
||||||
|
const validationResult = validateInput({ '1stTable': tableArray })
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: false,
|
||||||
|
msg: 'First letter of table should be alphabet or underscore.'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an error message if table name contains a character other than alphanumeric or underscore', () => {
|
||||||
|
const validationResult = validateInput({ 'table!': tableArray })
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: false,
|
||||||
|
msg: 'Table name should be alphanumeric.'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an error message if length of table name contains exceeds 32', () => {
|
||||||
|
const validationResult = validateInput({
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: tableArray
|
||||||
|
})
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: false,
|
||||||
|
msg: 'Maximum length for table name could be 32 characters.'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an error message if table does not have array of objects', () => {
|
||||||
|
const validationResult = validateInput({ table: stringData })
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: false,
|
||||||
|
msg: INVALID_TABLE_STRUCTURE
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an error message if a table array has an item other than object', () => {
|
||||||
|
const validationResult = validateInput({ table1: ['invalid'] })
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: false,
|
||||||
|
msg: `Table table1 contains invalid structure. ${MORE_INFO}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an error message if a row in a table contains an column with undefined value', () => {
|
||||||
|
const validationResult = validateInput({ table1: [{ column: undefined }] })
|
||||||
|
expect(validationResult).toEqual({
|
||||||
|
status: false,
|
||||||
|
msg: `A row in table table1 contains invalid value. Can't assign undefined to column.`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
90
src/utils/validateInput.ts
Normal file
90
src/utils/validateInput.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
export const MORE_INFO =
|
||||||
|
'For more info see https://sasjs.io/sasjs-adapter/#request-response'
|
||||||
|
export const INVALID_TABLE_STRUCTURE = `Parameter data contains invalid table structure. ${MORE_INFO}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function validates the input data structure and table naming convention
|
||||||
|
*
|
||||||
|
* @param data A json object that contains one or more tables, it can also be null
|
||||||
|
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
||||||
|
*/
|
||||||
|
export const validateInput = (
|
||||||
|
data: { [key: string]: any } | null
|
||||||
|
): {
|
||||||
|
status: boolean
|
||||||
|
msg: string
|
||||||
|
} => {
|
||||||
|
if (data === null) return { status: true, msg: '' }
|
||||||
|
|
||||||
|
if (getType(data) !== 'object') {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
msg: INVALID_TABLE_STRUCTURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSasFormatsTable = (key: string) =>
|
||||||
|
key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
msg: 'First letter of table should be alphabet or underscore.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
|
||||||
|
return { status: false, msg: 'Table name should be alphanumeric.' }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.length > 32) {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
msg: 'Maximum length for table name could be 32 characters.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
msg: INVALID_TABLE_STRUCTURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of data[key]) {
|
||||||
|
if (getType(item) !== 'object') {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
msg: `Table ${key} contains invalid structure. ${MORE_INFO}`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const attributes = Object.keys(item)
|
||||||
|
for (const attribute of attributes) {
|
||||||
|
if (item[attribute] === undefined) {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
msg: `A row in table ${key} contains invalid value. Can't assign undefined to ${attribute}.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: true, msg: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this function returns the type of variable
|
||||||
|
*
|
||||||
|
* @param data it could be anything, like string, array, object etc.
|
||||||
|
* @returns a string which tells the type of input parameter
|
||||||
|
*/
|
||||||
|
const getType = (data: any): string => {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return 'Array'
|
||||||
|
} else {
|
||||||
|
return typeof data
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user