diff --git a/src/SASjs.ts b/src/SASjs.ts index 01c00b5..8fd5cde 100644 --- a/src/SASjs.ts +++ b/src/SASjs.ts @@ -1,4 +1,4 @@ -import { compareTimestamps, asyncForEach } from './utils' +import { compareTimestamps, asyncForEach, validateInput } from './utils' import { SASjsConfig, UploadFile, @@ -686,7 +686,7 @@ export default class SASjs { ...config } - const validationResult = this.validateInput(data) + const validationResult = validateInput(data) // status is true if the data passes validation checks above 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`. * @param serviceJson - the JSON specifying the folders and services to be created. diff --git a/src/utils/index.ts b/src/utils/index.ts index 065dd87..62ceb97 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,20 +1,21 @@ +export * from './appendExtraResponseAttributes' export * from './asyncForEach' export * from './compareTimestamps' export * from './convertToCsv' export * from './createAxiosInstance' export * from './delay' +export * from './fetchLogByChunks' +export * from './getValidJson' export * from './isNode' export * from './isRelativePath' export * from './isUri' export * from './isUrl' export * from './needsRetry' export * from './parseGeneratedCode' -export * from './parseSourceCode' export * from './parseSasViyaLog' +export * from './parseSourceCode' +export * from './parseViyaDebugResponse' +export * from './parseWeboutResponse' export * from './serialize' export * from './splitChunks' -export * from './parseWeboutResponse' -export * from './fetchLogByChunks' -export * from './getValidJson' -export * from './parseViyaDebugResponse' -export * from './appendExtraResponseAttributes' +export * from './validateInput' diff --git a/src/utils/validateInput.ts b/src/utils/validateInput.ts new file mode 100644 index 0000000..6f1774d --- /dev/null +++ b/src/utils/validateInput.ts @@ -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 + } +}