diff --git a/.gitignore b/.gitignore index 70981b7..eb11248 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,4 @@ client/.npmrc *~ .lighthouseci audit.log - +tmp/ diff --git a/sas/mocks/sasjs/services/admin/makedata.js b/sas/mocks/sasjs/services/admin/makedata.js index 769e7cc..62c04a8 100644 --- a/sas/mocks/sasjs/services/admin/makedata.js +++ b/sas/mocks/sasjs/services/admin/makedata.js @@ -33,24 +33,24 @@ const tables = { MPE_ALERTS: [], MPE_AUDIT: [], MPE_COLUMN_LEVEL_SECURITY: [ - { tx_from: 0, tx_to: '31DEC9999:23:59:59', CLS_SCOPE: 'EDIT', CLS_GROUP: 'AllUsers', CLS_LIBREF: dcLibref, CLS_TABLE: 'MPE_LOCKANYTABLE', CLS_VARIABLE_NM: 'LOCK_STATUS_CD', CLS_ACTIVE: 1, CLS_HIDE: 0 } + { tx_from: 0, tx_to: 253717919999, CLS_SCOPE: 'EDIT', CLS_GROUP: 'AllUsers', CLS_LIBREF: dcLibref, CLS_TABLE: 'MPE_LOCKANYTABLE', CLS_VARIABLE_NM: 'LOCK_STATUS_CD', CLS_ACTIVE: 1, CLS_HIDE: 0 } ], MPE_CONFIG: [ - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_EMAIL_ALERTS', var_value: 'NO', var_active: 1, var_desc: 'YES or NO to enable email alerts.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_VIEWLIB_CHECK', var_value: 'NO', var_active: 1, var_desc: 'Set to YES to enable library validity checking in viewLibs service.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_MACROS', var_value: dataDir + '/dc_macros', var_active: 1, var_desc: 'Location of underlying macros - EUC feature.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_MAXOBS_WEBEDIT', var_value: '100', var_active: 1, var_desc: 'Maximum observations for editing in the EDIT screen.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_REQUEST_LOGS', var_value: 'YES', var_active: 1, var_desc: 'Setting to NO will prevent each request being logged to MPE_REQUESTS.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_RESTRICT_VIEWER', var_value: 'NO', var_active: 1, var_desc: 'YES will restrict the viewer to tables in MPE_SECURITY.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_RESTRICT_EDITRECORD', var_value: 'NO', var_active: 1, var_desc: 'YES will prevent the EDIT RECORD dialog.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC_CATALOG', var_name: 'DC_IGNORELIBS', var_value: '|MAPSSAS|MAPS|', var_active: 1, var_desc: 'Pipe separated list of librefs to ignore in Data Catalog.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_LOCALE', var_value: 'SYSTEM', var_active: 1, var_desc: 'Set to a locale to override the system value.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC_REVIEW', var_name: 'HISTORY_ROWS', var_value: '100', var_active: 1, var_desc: 'Number of rows to return in the HISTORY page.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_LICENCE_KEY', var_value: ' ', var_active: 1, var_desc: 'Licence Key' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC', var_name: 'DC_ACTIVATION_KEY', var_value: ' ', var_active: 1, var_desc: 'Activation Key' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC_EMAIL', var_name: 'SUBMITTED_TEMPLATE', var_value: 'Dear user, a change has been submitted.', var_active: 1, var_desc: 'Template email sent after submitting a change.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC_EMAIL', var_name: 'APPROVED_TEMPLATE', var_value: 'Dear user, a change has been approved.', var_active: 1, var_desc: 'Template email sent after approving a change.' }, - { tx_from: 0, tx_to: '31DEC9999:23:59:59', var_scope: 'DC_EMAIL', var_name: 'REJECTED_TEMPLATE', var_value: 'Dear user, a change has been rejected.', var_active: 1, var_desc: 'Template email sent after rejecting a change.' } + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_EMAIL_ALERTS', var_value: 'NO', var_active: 1, var_desc: 'YES or NO to enable email alerts.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_VIEWLIB_CHECK', var_value: 'NO', var_active: 1, var_desc: 'Set to YES to enable library validity checking in viewLibs service.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_MACROS', var_value: dataDir + '/dc_macros', var_active: 1, var_desc: 'Location of underlying macros - EUC feature.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_MAXOBS_WEBEDIT', var_value: '100', var_active: 1, var_desc: 'Maximum observations for editing in the EDIT screen.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_REQUEST_LOGS', var_value: 'YES', var_active: 1, var_desc: 'Setting to NO will prevent each request being logged to MPE_REQUESTS.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_RESTRICT_VIEWER', var_value: 'NO', var_active: 1, var_desc: 'YES will restrict the viewer to tables in MPE_SECURITY.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_RESTRICT_EDITRECORD', var_value: 'NO', var_active: 1, var_desc: 'YES will prevent the EDIT RECORD dialog.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC_CATALOG', var_name: 'DC_IGNORELIBS', var_value: '|MAPSSAS|MAPS|', var_active: 1, var_desc: 'Pipe separated list of librefs to ignore in Data Catalog.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_LOCALE', var_value: 'SYSTEM', var_active: 1, var_desc: 'Set to a locale to override the system value.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC_REVIEW', var_name: 'HISTORY_ROWS', var_value: '100', var_active: 1, var_desc: 'Number of rows to return in the HISTORY page.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_LICENCE_KEY', var_value: ' ', var_active: 1, var_desc: 'Licence Key' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC', var_name: 'DC_ACTIVATION_KEY', var_value: ' ', var_active: 1, var_desc: 'Activation Key' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC_EMAIL', var_name: 'SUBMITTED_TEMPLATE', var_value: 'Dear user, a change has been submitted.', var_active: 1, var_desc: 'Template email sent after submitting a change.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC_EMAIL', var_name: 'APPROVED_TEMPLATE', var_value: 'Dear user, a change has been approved.', var_active: 1, var_desc: 'Template email sent after approving a change.' }, + { tx_from: 0, tx_to: 253717919999, var_scope: 'DC_EMAIL', var_name: 'REJECTED_TEMPLATE', var_value: 'Dear user, a change has been rejected.', var_active: 1, var_desc: 'Template email sent after rejecting a change.' } ], MPE_DATACATALOG_CATS: [], MPE_DATACATALOG_LIBS: [], @@ -62,12 +62,12 @@ const tables = { MPE_DATASTATUS_OBJS: [], MPE_DATASTATUS_TABS: [], MPE_DATADICTIONARY: [ - { tx_from: 0, tx_to: '31DEC5999:23:59:59', DD_TYPE: 'LIBRARY', DD_SOURCE: dcLibref, DD_SHORTDESC: 'Data Controller Control Tables', DD_LONGDESC: '# The Data Controller Library', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', DD_TYPE: 'TABLE', DD_SOURCE: dcLibref + '.MPE_TABLES', DD_SHORTDESC: 'Configuration of new tables for Data Controller', DD_LONGDESC: '# MPE_TABLES', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', DD_TYPE: 'COLUMN', DD_SOURCE: dcLibref + '.MPE_TABLES.DSN', DD_SHORTDESC: 'Dataset Name to be edited', DD_LONGDESC: '_DSN_ - must be UPCASE', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', DD_TYPE: 'DIRECTORY', DD_SOURCE: '/some/directory', DD_SHORTDESC: 'Directory for some purpose', DD_LONGDESC: 'This directory is great.', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', DD_TYPE: 'TABLE', DD_SOURCE: dcLibref, DD_SHORTDESC: 'Transaction table for capturing Data Controller users', DD_LONGDESC: 'After a user accepts the EULA they are registered here.', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', DD_TYPE: 'COLUMN', DD_SOURCE: dcLibref + '.MPE_CONFIG.VAR_ACTIVE', DD_SHORTDESC: 'Set to 1 to make an option active', DD_LONGDESC: 'Used as a filter when querying for option settings.', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' } + { tx_from: 0, tx_to: 127490111999, DD_TYPE: 'LIBRARY', DD_SOURCE: dcLibref, DD_SHORTDESC: 'Data Controller Control Tables', DD_LONGDESC: '# The Data Controller Library', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, + { tx_from: 0, tx_to: 127490111999, DD_TYPE: 'TABLE', DD_SOURCE: dcLibref + '.MPE_TABLES', DD_SHORTDESC: 'Configuration of new tables for Data Controller', DD_LONGDESC: '# MPE_TABLES', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, + { tx_from: 0, tx_to: 127490111999, DD_TYPE: 'COLUMN', DD_SOURCE: dcLibref + '.MPE_TABLES.DSN', DD_SHORTDESC: 'Dataset Name to be edited', DD_LONGDESC: '_DSN_ - must be UPCASE', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, + { tx_from: 0, tx_to: 127490111999, DD_TYPE: 'DIRECTORY', DD_SOURCE: '/some/directory', DD_SHORTDESC: 'Directory for some purpose', DD_LONGDESC: 'This directory is great.', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, + { tx_from: 0, tx_to: 127490111999, DD_TYPE: 'TABLE', DD_SOURCE: dcLibref, DD_SHORTDESC: 'Transaction table for capturing Data Controller users', DD_LONGDESC: 'After a user accepts the EULA they are registered here.', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' }, + { tx_from: 0, tx_to: 127490111999, DD_TYPE: 'COLUMN', DD_SOURCE: dcLibref + '.MPE_CONFIG.VAR_ACTIVE', DD_SHORTDESC: 'Set to 1 to make an option active', DD_LONGDESC: 'Used as a filter when querying for option settings.', DD_OWNER: 'sasdemo', DD_RESPONSIBLE: 'sasdemo', DD_SENSITIVITY: 'Low' } ], MPE_DATALOADS: [], MPE_EMAILS: [], @@ -75,9 +75,9 @@ const tables = { MPE_FILTERANYTABLE: [], MPE_FILTERSOURCE: [], MPE_GROUPS: [ - { tx_from: 0, tx_to: '31DEC5999:23:59:59', group_name: 'dc-admin', group_desc: 'Custom Group for Data Controller Purposes', user_name: 'allbow' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', group_name: 'dc-admin', group_desc: 'Custom Group for Data Controller Purposes', user_name: 'dctestuser1' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', group_name: 'dc-admin', group_desc: 'Custom Group for Data Controller Purposes', user_name: 'mihmed' } + { tx_from: 0, tx_to: 127490111999, group_name: 'dc-admin', group_desc: 'Custom Group for Data Controller Purposes', user_name: 'allbow' }, + { tx_from: 0, tx_to: 127490111999, group_name: 'dc-admin', group_desc: 'Custom Group for Data Controller Purposes', user_name: 'dctestuser1' }, + { tx_from: 0, tx_to: 127490111999, group_name: 'dc-admin', group_desc: 'Custom Group for Data Controller Purposes', user_name: 'mihmed' } ], MPE_LINEAGE_COLS: [], MPE_LINEAGE_TABS: [], @@ -86,194 +86,194 @@ const tables = { MPE_REVIEW: [], MPE_REQUESTS: [], MPE_ROW_LEVEL_SECURITY: [ - { tx_from: 0, tx_to: '31DEC5999:23:59:59', RLS_RK: 1, RLS_SCOPE: 'ALL', RLS_GROUP: 'dc-admin', RLS_LIBREF: dcLibref, RLS_TABLE: 'MPE_GROUPS', RLS_GROUP_LOGIC: 'AND', RLS_SUBGROUP_LOGIC: 'OR', RLS_SUBGROUP_ID: 0, RLS_VARIABLE_NM: 'GROUP_NAME', RLS_OPERATOR_NM: 'NE', RLS_RAW_VALUE: "'-1'", RLS_ACTIVE: 1 }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', RLS_RK: 2, RLS_SCOPE: 'ALL', RLS_GROUP: 'dc-admin', RLS_LIBREF: dcLibref, RLS_TABLE: 'MPE_ROW_LEVEL_SECURITY', RLS_GROUP_LOGIC: 'AND', RLS_SUBGROUP_LOGIC: 'OR', RLS_SUBGROUP_ID: 0, RLS_VARIABLE_NM: 'RLS_RK', RLS_OPERATOR_NM: '>', RLS_RAW_VALUE: '0', RLS_ACTIVE: 1 }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', RLS_RK: 3, RLS_SCOPE: 'ALL', RLS_GROUP: 'DC Demo Group', RLS_LIBREF: dcLibref, RLS_TABLE: 'MPE_SECURITY', RLS_GROUP_LOGIC: 'AND', RLS_SUBGROUP_LOGIC: 'OR', RLS_SUBGROUP_ID: 0, RLS_VARIABLE_NM: 'ACCESS_LEVEL', RLS_OPERATOR_NM: 'NE', RLS_RAW_VALUE: "'N/A'", RLS_ACTIVE: 1 } + { tx_from: 0, tx_to: 127490111999, RLS_RK: 1, RLS_SCOPE: 'ALL', RLS_GROUP: 'dc-admin', RLS_LIBREF: dcLibref, RLS_TABLE: 'MPE_GROUPS', RLS_GROUP_LOGIC: 'AND', RLS_SUBGROUP_LOGIC: 'OR', RLS_SUBGROUP_ID: 0, RLS_VARIABLE_NM: 'GROUP_NAME', RLS_OPERATOR_NM: 'NE', RLS_RAW_VALUE: "'-1'", RLS_ACTIVE: 1 }, + { tx_from: 0, tx_to: 127490111999, RLS_RK: 2, RLS_SCOPE: 'ALL', RLS_GROUP: 'dc-admin', RLS_LIBREF: dcLibref, RLS_TABLE: 'MPE_ROW_LEVEL_SECURITY', RLS_GROUP_LOGIC: 'AND', RLS_SUBGROUP_LOGIC: 'OR', RLS_SUBGROUP_ID: 0, RLS_VARIABLE_NM: 'RLS_RK', RLS_OPERATOR_NM: '>', RLS_RAW_VALUE: '0', RLS_ACTIVE: 1 }, + { tx_from: 0, tx_to: 127490111999, RLS_RK: 3, RLS_SCOPE: 'ALL', RLS_GROUP: 'DC Demo Group', RLS_LIBREF: dcLibref, RLS_TABLE: 'MPE_SECURITY', RLS_GROUP_LOGIC: 'AND', RLS_SUBGROUP_LOGIC: 'OR', RLS_SUBGROUP_ID: 0, RLS_VARIABLE_NM: 'ACCESS_LEVEL', RLS_OPERATOR_NM: 'NE', RLS_RAW_VALUE: "'N/A'", RLS_ACTIVE: 1 } ], MPE_SECURITY: [ - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: '*ALL*', dsn: '*ALL*', access_level: 'APPROVE', sas_group: 'dc-admin' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: '*ALL*', dsn: '*ALL*', access_level: 'EDIT', sas_group: 'dc-admin' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: '*ALL*', dsn: '*ALL*', access_level: 'VIEW', sas_group: 'AllUsers' } + { tx_from: 0, tx_to: 127490111999, libref: '*ALL*', dsn: '*ALL*', access_level: 'APPROVE', sas_group: 'dc-admin' }, + { tx_from: 0, tx_to: 127490111999, libref: '*ALL*', dsn: '*ALL*', access_level: 'EDIT', sas_group: 'dc-admin' }, + { tx_from: 0, tx_to: 127490111999, libref: '*ALL*', dsn: '*ALL*', access_level: 'VIEW', sas_group: 'AllUsers' } ], MPE_SELECTBOX: [ - { selectbox_rk: 1, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_LOCKANYTABLE', base_column: 'LOCK_STATUS_CD', selectbox_value: 'LOCKED', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 2, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_LOCKANYTABLE', base_column: 'LOCK_STATUS_CD', selectbox_value: 'UNLOCKED', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 3, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'EDIT', selectbox_order: 0, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 4, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'APPROVE', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 5, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'VIEW', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 6, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'SIGNOFF', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 7, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'AUDIT', selectbox_order: 4, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 8, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'UPDATE', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 9, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'REPLACE', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 10, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'TXTEMPORAL', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 11, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'BITEMPORAL', selectbox_order: 4, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 12, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'FORMAT_CAT', selectbox_order: 5, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 13, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: '*ALL*', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 14, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: 'SUBMITTED', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 15, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: 'APPROVED', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 16, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: 'REJECTED', selectbox_order: 4, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 17, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'Option 1', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 18, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'Option 2', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 19, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'Option 3', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 20, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'This is a long option. This option is very long. It is optional, though.', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 21, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'CASE', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 22, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDSELECT', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 23, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDSELECT_HOOK', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 24, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HIDDEN', selectbox_order: 4, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 25, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'MAXVAL', selectbox_order: 5, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 26, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'MINVAL', selectbox_order: 6, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 27, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'NOTNULL', selectbox_order: 7, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 28, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'NUMBER_FORMAT', selectbox_order: 8, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 29, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'READONLY', selectbox_order: 9, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 30, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'ROUND', selectbox_order: 10, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 31, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTSELECT', selectbox_order: 11, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 32, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTSELECT_HOOK', selectbox_order: 12, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 33, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDREGEX', selectbox_order: 13, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 34, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTREGEX', selectbox_order: 14, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 35, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDFORMULA', selectbox_order: 15, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 36, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTFORMULA', selectbox_order: 16, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 37, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_ACTIVE', selectbox_value: '1', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 38, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_ACTIVE', selectbox_value: '0', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 39, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'DSN', selectbox_value: '*ALL*', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 40, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'LIBREF', selectbox_value: '*ALL*', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 41, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'COLUMN', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 42, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'TABLE', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 43, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'LIBRARY', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 44, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'CATALOG', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 45, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'FORMAT', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 46, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SCOPE', selectbox_value: 'ALL', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 47, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SCOPE', selectbox_value: 'EDIT', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 48, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SCOPE', selectbox_value: 'VIEW', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 49, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_GROUP_LOGIC', selectbox_value: 'AND', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 50, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_GROUP_LOGIC', selectbox_value: 'OR', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 51, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SUBGROUP_LOGIC', selectbox_value: 'AND', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 52, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SUBGROUP_LOGIC', selectbox_value: 'OR', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 53, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '=', selectbox_order: 0, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 54, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '>', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 55, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '<', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 56, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '<=', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 57, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '>=', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 58, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'BETWEEN', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 59, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'IN', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 60, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'NOT IN', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 61, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'NE', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 62, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'CONTAINS', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 63, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_ACTIVE', selectbox_value: '1', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 64, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_ACTIVE', selectbox_value: '0', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 65, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_ACTIVE', selectbox_value: '1', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 66, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_ACTIVE', selectbox_value: '0', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 67, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_SCOPE', selectbox_value: 'EDIT', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 68, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_SCOPE', selectbox_value: 'VIEW', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 69, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_SCOPE', selectbox_value: 'ALL', selectbox_order: 3, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 70, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_HIDE', selectbox_value: '0', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 71, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_HIDE', selectbox_value: '1', selectbox_order: 2, ver_to_dttm: '31DEC5999:23:59:59' }, - { selectbox_rk: 72, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_EXCEL_CONFIG', base_column: 'XL_RULE', selectbox_value: 'FORMULA', selectbox_order: 1, ver_to_dttm: '31DEC5999:23:59:59' } + { selectbox_rk: 1, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_LOCKANYTABLE', base_column: 'LOCK_STATUS_CD', selectbox_value: 'LOCKED', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 2, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_LOCKANYTABLE', base_column: 'LOCK_STATUS_CD', selectbox_value: 'UNLOCKED', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 3, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'EDIT', selectbox_order: 0, ver_to_dttm: 127490111999 }, + { selectbox_rk: 4, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'APPROVE', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 5, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'VIEW', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 6, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'SIGNOFF', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 7, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'ACCESS_LEVEL', selectbox_value: 'AUDIT', selectbox_order: 4, ver_to_dttm: 127490111999 }, + { selectbox_rk: 8, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'UPDATE', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 9, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'REPLACE', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 10, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'TXTEMPORAL', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 11, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'BITEMPORAL', selectbox_order: 4, ver_to_dttm: 127490111999 }, + { selectbox_rk: 12, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_TABLES', base_column: 'LOADTYPE', selectbox_value: 'FORMAT_CAT', selectbox_order: 5, ver_to_dttm: 127490111999 }, + { selectbox_rk: 13, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: '*ALL*', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 14, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: 'SUBMITTED', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 15, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: 'APPROVED', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 16, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ALERTS', base_column: 'ALERT_EVENT', selectbox_value: 'REJECTED', selectbox_order: 4, ver_to_dttm: 127490111999 }, + { selectbox_rk: 17, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'Option 1', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 18, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'Option 2', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 19, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'Option 3', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 20, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_X_TEST', base_column: 'SOME_DROPDOWN', selectbox_value: 'This is a long option. This option is very long. It is optional, though.', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 21, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'CASE', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 22, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDSELECT', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 23, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDSELECT_HOOK', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 24, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HIDDEN', selectbox_order: 4, ver_to_dttm: 127490111999 }, + { selectbox_rk: 25, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'MAXVAL', selectbox_order: 5, ver_to_dttm: 127490111999 }, + { selectbox_rk: 26, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'MINVAL', selectbox_order: 6, ver_to_dttm: 127490111999 }, + { selectbox_rk: 27, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'NOTNULL', selectbox_order: 7, ver_to_dttm: 127490111999 }, + { selectbox_rk: 28, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'NUMBER_FORMAT', selectbox_order: 8, ver_to_dttm: 127490111999 }, + { selectbox_rk: 29, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'READONLY', selectbox_order: 9, ver_to_dttm: 127490111999 }, + { selectbox_rk: 30, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'ROUND', selectbox_order: 10, ver_to_dttm: 127490111999 }, + { selectbox_rk: 31, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTSELECT', selectbox_order: 11, ver_to_dttm: 127490111999 }, + { selectbox_rk: 32, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTSELECT_HOOK', selectbox_order: 12, ver_to_dttm: 127490111999 }, + { selectbox_rk: 33, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDREGEX', selectbox_order: 13, ver_to_dttm: 127490111999 }, + { selectbox_rk: 34, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTREGEX', selectbox_order: 14, ver_to_dttm: 127490111999 }, + { selectbox_rk: 35, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'HARDFORMULA', selectbox_order: 15, ver_to_dttm: 127490111999 }, + { selectbox_rk: 36, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_TYPE', selectbox_value: 'SOFTFORMULA', selectbox_order: 16, ver_to_dttm: 127490111999 }, + { selectbox_rk: 37, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_ACTIVE', selectbox_value: '1', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 38, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_VALIDATIONS', base_column: 'RULE_ACTIVE', selectbox_value: '0', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 39, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'DSN', selectbox_value: '*ALL*', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 40, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_SECURITY', base_column: 'LIBREF', selectbox_value: '*ALL*', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 41, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'COLUMN', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 42, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'TABLE', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 43, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'LIBRARY', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 44, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'CATALOG', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 45, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_DATADICTIONARY', base_column: 'DD_TYPE', selectbox_value: 'FORMAT', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 46, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SCOPE', selectbox_value: 'ALL', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 47, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SCOPE', selectbox_value: 'EDIT', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 48, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SCOPE', selectbox_value: 'VIEW', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 49, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_GROUP_LOGIC', selectbox_value: 'AND', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 50, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_GROUP_LOGIC', selectbox_value: 'OR', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 51, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SUBGROUP_LOGIC', selectbox_value: 'AND', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 52, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_SUBGROUP_LOGIC', selectbox_value: 'OR', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 53, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '=', selectbox_order: 0, ver_to_dttm: 127490111999 }, + { selectbox_rk: 54, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '>', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 55, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '<', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 56, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '<=', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 57, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: '>=', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 58, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'BETWEEN', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 59, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'IN', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 60, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'NOT IN', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 61, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'NE', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 62, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_OPERATOR_NM', selectbox_value: 'CONTAINS', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 63, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_ACTIVE', selectbox_value: '1', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 64, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_ROW_LEVEL_SECURITY', base_column: 'RLS_ACTIVE', selectbox_value: '0', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 65, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_ACTIVE', selectbox_value: '1', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 66, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_ACTIVE', selectbox_value: '0', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 67, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_SCOPE', selectbox_value: 'EDIT', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 68, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_SCOPE', selectbox_value: 'VIEW', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 69, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_SCOPE', selectbox_value: 'ALL', selectbox_order: 3, ver_to_dttm: 127490111999 }, + { selectbox_rk: 70, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_HIDE', selectbox_value: '0', selectbox_order: 1, ver_to_dttm: 127490111999 }, + { selectbox_rk: 71, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_column: 'CLS_HIDE', selectbox_value: '1', selectbox_order: 2, ver_to_dttm: 127490111999 }, + { selectbox_rk: 72, ver_from_dttm: 0, select_lib: dcLibref, select_ds: 'MPE_EXCEL_CONFIG', base_column: 'XL_RULE', selectbox_value: 'FORMULA', selectbox_order: 1, ver_to_dttm: 127490111999 } ], MPE_SUBMIT: [], MPE_TABLES: [ - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_COLUMN_LEVEL_SECURITY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', buskey: 'CLS_SCOPE CLS_GROUP CLS_LIBREF CLS_TABLE CLS_VARIABLE_NM', notes: 'Column Level Security config', post_edit_hook: 'services/hooks/mpe_column_level_security_postedit' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_XLMAP_INFO', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', buskey: 'XLMAP_ID', notes: 'Excel Map Info', post_edit_hook: 'services/hooks/mpe_xlmap_info_postedit' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_XLMAP_RULES', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', buskey: 'XLMAP_ID XLMAP_RANGE_ID', notes: 'Excel Map Rules', post_edit_hook: 'services/hooks/mpe_xlmap_rules_postedit' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_XLMAP_DATA', num_of_approvals_required: 1, loadtype: 'UPDATE', buskey: 'LOAD_REF XLMAP_ID XLMAP_RANGE_ID ROW_NO COL_NO', notes: 'Excel Map Data' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_LOCKANYTABLE', num_of_approvals_required: 1, loadtype: 'UPDATE', buskey: 'LOCK_LIB LOCK_DS', notes: 'Table lock management' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_TABLES', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'LIBREF DSN', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', notes: 'MPE Editor self-edit', post_edit_hook: 'services/hooks/mpe_tables_postedit' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_SECURITY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'LIBREF DSN ACCESS_LEVEL SAS_GROUP', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', notes: 'Group access control', post_edit_hook: 'services/hooks/mpe_security_postedit' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_SELECTBOX', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'SELECTBOX_RK', var_txfrom: 'VER_FROM_DTTM', var_txto: 'VER_TO_DTTM', notes: 'Dropdown configuration', rk_underlying: 'SELECT_LIB SELECT_DS BASE_COLUMN SELECTBOX_VALUE' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_X_TEST', num_of_approvals_required: 1, loadtype: 'UPDATE', buskey: 'PRIMARY_KEY_FIELD', notes: 'Test table for controller' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_EMAILS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'USER_NAME', notes: 'Primary Emails Table', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_CONFIG', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'VAR_SCOPE VAR_NAME', notes: 'Configuration variables', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_ALERTS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'ALERT_EVENT ALERT_LIB ALERT_DS ALERT_USER', notes: 'Alert email events', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_GROUPS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'GROUP_NAME USER_NAME', notes: 'Additional DC groups', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_VALIDATIONS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'BASE_LIB BASE_DS BASE_COL RULE_TYPE', notes: 'Data quality rules', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', post_edit_hook: 'services/hooks/mpe_validations_postedit' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_DATADICTIONARY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'DD_TYPE DD_SOURCE', notes: 'Data dictionary', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_EXCEL_CONFIG', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'XL_LIBREF XL_TABLE XL_COLUMN', notes: 'Excel import rules', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_ROW_LEVEL_SECURITY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'RLS_RK', notes: 'Row Level Security', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', rk_underlying: 'RLS_SCOPE RLS_GROUP RLS_LIBREF RLS_TABLE RLS_GROUP_LOGIC RLS_SUBGROUP_LOGIC RLS_SUBGROUP_ID RLS_VARIABLE_NM RLS_OPERATOR_NM RLS_RAW_VALUE', post_edit_hook: 'services/hooks/mpe_row_level_security_postedit' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', libref: dcLibref, dsn: 'MPE_X_CATALOG-FC', num_of_approvals_required: 1, loadtype: 'FORMAT_CAT', buskey: 'TYPE FMTNAME FMTROW', notes: 'Sample Format Catalog' } + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_COLUMN_LEVEL_SECURITY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', buskey: 'CLS_SCOPE CLS_GROUP CLS_LIBREF CLS_TABLE CLS_VARIABLE_NM', notes: 'Column Level Security config', post_edit_hook: 'services/hooks/mpe_column_level_security_postedit' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_XLMAP_INFO', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', buskey: 'XLMAP_ID', notes: 'Excel Map Info', post_edit_hook: 'services/hooks/mpe_xlmap_info_postedit' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_XLMAP_RULES', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', buskey: 'XLMAP_ID XLMAP_RANGE_ID', notes: 'Excel Map Rules', post_edit_hook: 'services/hooks/mpe_xlmap_rules_postedit' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_XLMAP_DATA', num_of_approvals_required: 1, loadtype: 'UPDATE', buskey: 'LOAD_REF XLMAP_ID XLMAP_RANGE_ID ROW_NO COL_NO', notes: 'Excel Map Data' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_LOCKANYTABLE', num_of_approvals_required: 1, loadtype: 'UPDATE', buskey: 'LOCK_LIB LOCK_DS', notes: 'Table lock management' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_TABLES', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'LIBREF DSN', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', notes: 'MPE Editor self-edit', post_edit_hook: 'services/hooks/mpe_tables_postedit' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_SECURITY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'LIBREF DSN ACCESS_LEVEL SAS_GROUP', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', notes: 'Group access control', post_edit_hook: 'services/hooks/mpe_security_postedit' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_SELECTBOX', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'SELECTBOX_RK', var_txfrom: 'VER_FROM_DTTM', var_txto: 'VER_TO_DTTM', notes: 'Dropdown configuration', rk_underlying: 'SELECT_LIB SELECT_DS BASE_COLUMN SELECTBOX_VALUE' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_X_TEST', num_of_approvals_required: 1, loadtype: 'UPDATE', buskey: 'PRIMARY_KEY_FIELD', notes: 'Test table for controller' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_EMAILS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'USER_NAME', notes: 'Primary Emails Table', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_CONFIG', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'VAR_SCOPE VAR_NAME', notes: 'Configuration variables', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_ALERTS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'ALERT_EVENT ALERT_LIB ALERT_DS ALERT_USER', notes: 'Alert email events', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_GROUPS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'GROUP_NAME USER_NAME', notes: 'Additional DC groups', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_VALIDATIONS', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'BASE_LIB BASE_DS BASE_COL RULE_TYPE', notes: 'Data quality rules', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', post_edit_hook: 'services/hooks/mpe_validations_postedit' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_DATADICTIONARY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'DD_TYPE DD_SOURCE', notes: 'Data dictionary', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_EXCEL_CONFIG', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'XL_LIBREF XL_TABLE XL_COLUMN', notes: 'Excel import rules', var_txfrom: 'TX_FROM', var_txto: 'TX_TO' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_ROW_LEVEL_SECURITY', num_of_approvals_required: 1, loadtype: 'TXTEMPORAL', buskey: 'RLS_RK', notes: 'Row Level Security', var_txfrom: 'TX_FROM', var_txto: 'TX_TO', rk_underlying: 'RLS_SCOPE RLS_GROUP RLS_LIBREF RLS_TABLE RLS_GROUP_LOGIC RLS_SUBGROUP_LOGIC RLS_SUBGROUP_ID RLS_VARIABLE_NM RLS_OPERATOR_NM RLS_RAW_VALUE', post_edit_hook: 'services/hooks/mpe_row_level_security_postedit' }, + { tx_from: 0, tx_to: 127490111999, libref: dcLibref, dsn: 'MPE_X_CATALOG-FC', num_of_approvals_required: 1, loadtype: 'FORMAT_CAT', buskey: 'TYPE FMTNAME FMTROW', notes: 'Sample Format Catalog' } ], MPE_VALIDATIONS: [ - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_SCOPE', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_LIBREF', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_all', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_TABLE', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_TABLE', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_all', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_VARIABLE_NM', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_VARIABLE_NM', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_ACTIVE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_HIDE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_GROUP', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/sas_groups', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'LIBREF', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'DSN', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'LIBREF', rule_type: 'NOTNULL', rule_value: ' ', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'DSN', rule_type: 'NOTNULL', rule_value: ' ', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'NUM_OF_APPROVALS_REQUIRED', rule_type: 'MINVAL', rule_value: '1', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'BUSKEY', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'BUSKEY', rule_type: 'NOTNULL', rule_value: ' ', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_TXFROM', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_TXTO', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_BUSFROM', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_BUSTO', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_PROCESSED', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_all', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'DSN', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/mpe_tables.dsn', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'LIBREF', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'LIBREF', rule_type: 'HARDSELECT', rule_value: dcLibref + '.MPE_TABLES.LIBREF', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'DSN', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'DSN', rule_type: 'SOFTSELECT', rule_value: dcLibref + '.MPE_TABLES.DSN', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'SAS_GROUP', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/sas_groups', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'BASE_LIB', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_editable', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'BASE_DS', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_editable', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'BASE_COL', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'RULE_ACTIVE', rule_type: 'MINVAL', rule_value: '0', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'RULE_ACTIVE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_editable', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_TABLE', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_editable', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_COLUMN', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_ACTIVE', rule_type: 'MINVAL', rule_value: '0', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_ACTIVE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_XLMAP_INFO', base_col: 'XLMAP_ID', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_XLMAP_INFO', base_col: 'XLMAP_ID', rule_type: 'SOFTSELECT', rule_value: dcLibref + '.MPE_XLMAP_RULES.XLMAP_ID', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_XLMAP_RULES', base_col: 'XLMAP_ID', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SELECTBOX', base_col: 'SELECT_LIB', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_editable', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SELECTBOX', base_col: 'SELECT_DS', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_editable', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SELECTBOX', base_col: 'BASE_COLUMN', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_GROUP', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/sas_groups', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_all', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_TABLE', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_all', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_SUBGROUP_ID', rule_type: 'MINVAL', rule_value: '0', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_SUBGROUP_ID', rule_type: 'NOTNULL', rule_value: '0', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_VARIABLE_NM', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ALERTS', base_col: 'ALERT_LIB', rule_type: 'HARDSELECT_HOOK', rule_value: 'services/validations/mpe_alerts.alert_lib', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_BESTNUM', rule_type: 'SOFTSELECT', rule_value: dcLibref + '.MPE_X_TEST.SOME_BESTNUM', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_CHAR', rule_type: 'HARDREGEX', rule_value: '/the|data/i', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_CHAR', rule_type: 'SOFTREGEX', rule_value: '/t/', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'PRIMARY_KEY_FIELD', rule_type: 'SOFTREGEX', rule_value: '/^\\d+$/', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_SHORTNUM', rule_type: 'HARDREGEX', rule_value: '/^(?![1-5](\\.\\d+)?$).*/', rule_active: 1, tx_to: '31DEC5999:23:59:59' }, - { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_NUM', rule_type: 'HARDSELECT_HOOK', rule_value: 'services/validations/mpe_x_test.some_num', rule_active: 1, tx_to: '31DEC5999:23:59:59' } + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_SCOPE', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_LIBREF', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_all', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_TABLE', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_TABLE', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_all', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_VARIABLE_NM', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_VARIABLE_NM', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_ACTIVE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_HIDE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_COLUMN_LEVEL_SECURITY', base_col: 'CLS_GROUP', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/sas_groups', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'LIBREF', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'DSN', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'LIBREF', rule_type: 'NOTNULL', rule_value: ' ', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'DSN', rule_type: 'NOTNULL', rule_value: ' ', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'NUM_OF_APPROVALS_REQUIRED', rule_type: 'MINVAL', rule_value: '1', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'BUSKEY', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'BUSKEY', rule_type: 'NOTNULL', rule_value: ' ', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_TXFROM', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_TXTO', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_BUSFROM', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_BUSTO', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'VAR_PROCESSED', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_all', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_TABLES', base_col: 'DSN', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/mpe_tables.dsn', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'LIBREF', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'LIBREF', rule_type: 'HARDSELECT', rule_value: dcLibref + '.MPE_TABLES.LIBREF', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'DSN', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'DSN', rule_type: 'SOFTSELECT', rule_value: dcLibref + '.MPE_TABLES.DSN', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SECURITY', base_col: 'SAS_GROUP', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/sas_groups', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'BASE_LIB', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_editable', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'BASE_DS', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_editable', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'BASE_COL', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'RULE_ACTIVE', rule_type: 'MINVAL', rule_value: '0', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_VALIDATIONS', base_col: 'RULE_ACTIVE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_editable', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_TABLE', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_editable', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_COLUMN', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_ACTIVE', rule_type: 'MINVAL', rule_value: '0', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_EXCEL_CONFIG', base_col: 'XL_ACTIVE', rule_type: 'MAXVAL', rule_value: '1', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_XLMAP_INFO', base_col: 'XLMAP_ID', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_XLMAP_INFO', base_col: 'XLMAP_ID', rule_type: 'SOFTSELECT', rule_value: dcLibref + '.MPE_XLMAP_RULES.XLMAP_ID', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_XLMAP_RULES', base_col: 'XLMAP_ID', rule_type: 'CASE', rule_value: 'UPCASE', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SELECTBOX', base_col: 'SELECT_LIB', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_editable', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SELECTBOX', base_col: 'SELECT_DS', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_editable', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_SELECTBOX', base_col: 'BASE_COLUMN', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_GROUP', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/sas_groups', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_LIBREF', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/libraries_all', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_TABLE', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/tables_all', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_SUBGROUP_ID', rule_type: 'MINVAL', rule_value: '0', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_SUBGROUP_ID', rule_type: 'NOTNULL', rule_value: '0', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ROW_LEVEL_SECURITY', base_col: 'RLS_VARIABLE_NM', rule_type: 'SOFTSELECT_HOOK', rule_value: 'services/validations/columns_in_libds', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_ALERTS', base_col: 'ALERT_LIB', rule_type: 'HARDSELECT_HOOK', rule_value: 'services/validations/mpe_alerts.alert_lib', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_BESTNUM', rule_type: 'SOFTSELECT', rule_value: dcLibref + '.MPE_X_TEST.SOME_BESTNUM', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_CHAR', rule_type: 'HARDREGEX', rule_value: '/the|data/i', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_CHAR', rule_type: 'SOFTREGEX', rule_value: '/t/', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'PRIMARY_KEY_FIELD', rule_type: 'SOFTREGEX', rule_value: '/^\\d+$/', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_SHORTNUM', rule_type: 'HARDREGEX', rule_value: '/^(?![1-5](\\.\\d+)?$).*/', rule_active: 1, tx_to: 127490111999 }, + { tx_from: 0, base_lib: dcLibref, base_ds: 'MPE_X_TEST', base_col: 'SOME_NUM', rule_type: 'HARDSELECT_HOOK', rule_value: 'services/validations/mpe_x_test.some_num', rule_active: 1, tx_to: 127490111999 } ], MPE_X_TEST: [ - { PRIMARY_KEY_FIELD: 0, SOME_CHAR: 'this is dummy data', SOME_DROPDOWN: 'Option 1', SOME_NUM: 42, SOME_DATE: '12FEB1960', SOME_DATETIME: '01JAN1960:00:00:42', SOME_TIME: '0:00:42', SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, - { PRIMARY_KEY_FIELD: 1, SOME_CHAR: 'more dummy data', SOME_DROPDOWN: 'Option 2', SOME_NUM: 42, SOME_DATE: '12FEB1960', SOME_DATETIME: '01JAN1960:00:00:42', SOME_TIME: '0:07:02', SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, - { PRIMARY_KEY_FIELD: 2, SOME_CHAR: 'even more dummy data', SOME_DROPDOWN: 'Option 3', SOME_NUM: 42, SOME_DATE: '12FEB1960', SOME_DATETIME: '01JAN1960:00:00:42', SOME_TIME: '0:02:22', SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, - { PRIMARY_KEY_FIELD: 3, SOME_CHAR: 'It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:', SOME_DROPDOWN: 'Option 2', SOME_NUM: 1613.001, SOME_DATE: '27FEB1961', SOME_DATETIME: '01JAN1960:00:07:03', SOME_TIME: '0:00:44', SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, - { PRIMARY_KEY_FIELD: 4, SOME_CHAR: 'if you can fill the unforgiving minute', SOME_DROPDOWN: 'Option 1', SOME_NUM: 1613.001123456, SOME_DATE: '02AUG1971', SOME_DATETIME: '29MAY1973:06:12:03', SOME_TIME: '0:06:52', SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, - { PRIMARY_KEY_FIELD: 5, SOME_CHAR: '= testing the formula situation', SOME_DROPDOWN: 'Option 1', SOME_NUM: 42, SOME_DATE: '12FEB1960', SOME_DATETIME: '01JAN1960:00:00:42', SOME_TIME: '0:00:42', SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, - { PRIMARY_KEY_FIELD: 6, SOME_CHAR: "'=the formula with leading apostrophe", SOME_DROPDOWN: 'Option 1', SOME_NUM: 42, SOME_DATE: '12FEB1960', SOME_DATETIME: '01JAN1960:00:00:42', SOME_TIME: '0:00:42', SOME_SHORTNUM: 8, SOME_BESTNUM: 44 } + { PRIMARY_KEY_FIELD: 0, SOME_CHAR: 'this is dummy data', SOME_DROPDOWN: 'Option 1', SOME_NUM: 42, SOME_DATE: 42, SOME_DATETIME: 42, SOME_TIME: 42, SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, + { PRIMARY_KEY_FIELD: 1, SOME_CHAR: 'more dummy data', SOME_DROPDOWN: 'Option 2', SOME_NUM: 42, SOME_DATE: 42, SOME_DATETIME: 42, SOME_TIME: 422, SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, + { PRIMARY_KEY_FIELD: 2, SOME_CHAR: 'even more dummy data', SOME_DROPDOWN: 'Option 3', SOME_NUM: 42, SOME_DATE: 42, SOME_DATETIME: 42, SOME_TIME: 142, SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, + { PRIMARY_KEY_FIELD: 3, SOME_CHAR: 'It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:', SOME_DROPDOWN: 'Option 2', SOME_NUM: 1613.001, SOME_DATE: 423, SOME_DATETIME: 423, SOME_TIME: 44, SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, + { PRIMARY_KEY_FIELD: 4, SOME_CHAR: 'if you can fill the unforgiving minute', SOME_DROPDOWN: 'Option 1', SOME_NUM: 1613.001123456, SOME_DATE: 4231, SOME_DATETIME: 423123123, SOME_TIME: 412, SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, + { PRIMARY_KEY_FIELD: 5, SOME_CHAR: '= testing the formula situation', SOME_DROPDOWN: 'Option 1', SOME_NUM: 42, SOME_DATE: 42, SOME_DATETIME: 42, SOME_TIME: 42, SOME_SHORTNUM: 8, SOME_BESTNUM: 44 }, + { PRIMARY_KEY_FIELD: 6, SOME_CHAR: "'=the formula with leading apostrophe", SOME_DROPDOWN: 'Option 1', SOME_NUM: 42, SOME_DATE: 42, SOME_DATETIME: 42, SOME_TIME: 42, SOME_SHORTNUM: 8, SOME_BESTNUM: 44 } ], MPE_XLMAP_DATA: [], MPE_XLMAP_INFO: [ - { tx_from: 0, tx_to: '31DEC5999:23:59:59', XLMAP_ID: 'BASEL-KM1', XLMAP_DESCRIPTION: 'Basel 3 Key Metrics report', XLMAP_TARGETLIBDS: dcLibref + '.MPE_XLMAP_DATA' } + { tx_from: 0, tx_to: 127490111999, XLMAP_ID: 'BASEL-KM1', XLMAP_DESCRIPTION: 'Basel 3 Key Metrics report', XLMAP_TARGETLIBDS: dcLibref + '.MPE_XLMAP_DATA' } ], MPE_XLMAP_RULES: [ - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:a', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:a' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:b', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:b' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:c', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:c' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:d', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:d' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:e', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:e' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:f', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:f' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-CR2', xlmap_range_id: 'CR2-sec1', xlmap_sheet: 'CR2', xlmap_start: 'ABSOLUTE D8', xlmap_finish: 'BLANKROW' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'BASEL-CR2', xlmap_range_id: 'CR2-sec2', xlmap_sheet: 'CR2', xlmap_start: 'ABSOLUTE D18', xlmap_finish: 'LASTDOWN' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'SAMPLE', xlmap_range_id: 'header', xlmap_sheet: '/1', xlmap_start: 'ABSOLUTE B3', xlmap_finish: 'ABSOLUTE B8' }, - { tx_from: 0, tx_to: '31DEC5999:23:59:59', xlmap_id: 'SAMPLE', xlmap_range_id: 'data', xlmap_sheet: '/1', xlmap_start: 'ABSOLUTE B13', xlmap_finish: 'ABSOLUTE E16' } + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:a', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:a' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:b', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:b' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:c', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:c' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:d', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:d' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:e', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:e' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-KM1', xlmap_range_id: 'KM1:f', xlmap_sheet: 'KM1', xlmap_start: 'MATCH 4 R[2]C[0]:f' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-CR2', xlmap_range_id: 'CR2-sec1', xlmap_sheet: 'CR2', xlmap_start: 'ABSOLUTE D8', xlmap_finish: 'BLANKROW' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'BASEL-CR2', xlmap_range_id: 'CR2-sec2', xlmap_sheet: 'CR2', xlmap_start: 'ABSOLUTE D18', xlmap_finish: 'LASTDOWN' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'SAMPLE', xlmap_range_id: 'header', xlmap_sheet: '/1', xlmap_start: 'ABSOLUTE B3', xlmap_finish: 'ABSOLUTE B8' }, + { tx_from: 0, tx_to: 127490111999, xlmap_id: 'SAMPLE', xlmap_range_id: 'data', xlmap_sheet: '/1', xlmap_start: 'ABSOLUTE B13', xlmap_finish: 'ABSOLUTE E16' } ], MPE_X_CATALOG: [], MPE_USERS: [] diff --git a/sas/mocks/sasjs/services/admin/registerkey.js b/sas/mocks/sasjs/services/admin/registerkey.js index ae979df..cd36f97 100644 --- a/sas/mocks/sasjs/services/admin/registerkey.js +++ b/sas/mocks/sasjs/services/admin/registerkey.js @@ -6,8 +6,8 @@ let activationKey = '' let writeError = 0 -if (_WEBIN_FILEREF1) { - const fileText = _WEBIN_FILEREF1.toString() +if (_webin_fileref1) { + const fileText = _webin_fileref1.toString() const split = fileText.split('\n')[1].split(',') activationKey = split[0] licenceKey = split[1] @@ -27,7 +27,7 @@ const json = { } try { - fs.writeFileSync(licenceStore, JSON.stringify(json)) + fs.writeFileSync(licenceStore, JSON.stringify(json, null, 2)) } catch (err) { writeError = 1 } diff --git a/sas/mocks/sasjs/services/approvers/getapprovals.js b/sas/mocks/sasjs/services/approvers/getapprovals.js index 44a7091..c3ac9a8 100644 --- a/sas/mocks/sasjs/services/approvers/getapprovals.js +++ b/sas/mocks/sasjs/services/approvers/getapprovals.js @@ -19,7 +19,7 @@ try { REVIEW_STATUS_ID: r.SUBMIT_STATUS_CD, SUBMITTED_BY_NM: r.SUBMITTED_BY_NM, BASE_TABLE: r.BASE_LIB + '.' + r.BASE_DS, - SUBMITTED_ON_DTTM: new Date(r.SUBMITTED_ON_DTTM * 1000).toISOString().replace('T',' ').replace(/\.\d+Z$/, ''), + SUBMITTED_ON_DTTM: ' ' + new Date(r.SUBMITTED_ON_DTTM * 1000).toISOString().replace('T',' ').replace(/\.\d+Z$/, ''), SUBMITTED_ON_DTTM2: r.SUBMITTED_ON_DTTM, SUBMITTED_REASON_TXT: r.SUBMITTED_REASON_TXT || '', NUM_OF_APPROVALS_REQUIRED: r.NUM_OF_APPROVALS_REQUIRED, diff --git a/sas/mocks/sasjs/services/auditors/getstagetable.js b/sas/mocks/sasjs/services/auditors/getstagetable.js index ae4031c..2680e93 100644 --- a/sas/mocks/sasjs/services/auditors/getstagetable.js +++ b/sas/mocks/sasjs/services/auditors/getstagetable.js @@ -7,45 +7,39 @@ const dcLibref = 'DC_JSLIB' const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) const stagingDir = nodePath.resolve(dataDir, 'staging') -// Parse the iwant input to get table_id -let tableId = '' -for (let i = 1; i <= 10; i++) { - const nameVar = '_WEBIN_NAME' + i - try { - const nameVal = eval(nameVar) - if (nameVal && nameVal.toLowerCase() === 'iwant') { - const refVar = '_WEBIN_FILEREF' + i - const raw = eval(refVar).toString() - const lines = raw.split(/\r?\n/) - if (lines.length >= 2) { - const headers = lines[0].trim().split(/\s+/).map(h => h.split(':')[0].trim().toLowerCase()) - const values = lines[1].trim().split(',') - const idx = headers.indexOf('table_id') - if (idx >= 0) tableId = values[idx] - } - break - } - } catch(e) { break } -} +eval(fs.readFileSync(nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js'), 'utf8')) +const loadTableData = makeTableLoader(dataDir) + +// ─── Parse input ────────────────────────────────────────────────────────────── + +const _iwantRow = fetchTable('iwant')[0] || {} +const tableId = _iwantRow.TABLE_ID || _iwantRow.table_id || '' -// Load the staged data from the staging directory let stagetable = [] -let fmtStagetable = [] +let fmt_stagetable = [] if (tableId) { try { const stageFile = nodePath.resolve(stagingDir, tableId + '.json') - const raw = fs.readFileSync(stageFile, {encoding:'utf8'}).toString() - const stageData = JSON.parse(raw) + const stageData = JSON.parse(fs.readFileSync(stageFile, {encoding:'utf8'}).toString()) - // Return the rows as stagetable (raw values) stagetable = stageData.rows - // Build fmt_stagetable (stringified values for display) - fmtStagetable = stageData.rows.map(r => { + // Build fmt_stagetable by applying SAS formats to numeric temporal columns + const dsn = stageData.dsn || '' + const schema = loadTableSchema(dataDir, dsn) + const colDdtypes = {} + for (const col of schema) colDdtypes[col.name.toUpperCase()] = getDdType(col) + + fmt_stagetable = stageData.rows.map(r => { const fmt = {} for (const key of Object.keys(r)) { - fmt[key] = String(r[key]) + const ddtype = colDdtypes[key.toUpperCase()] + if (ddtype === 'DATE' || ddtype === 'DATETIME' || ddtype === 'TIME') { + fmt[key] = formatSasValue(r[key], ddtype) + } else { + fmt[key] = String(r[key]) + } } return fmt }) @@ -55,29 +49,14 @@ if (tableId) { } _webout = JSON.stringify({ - SYSDATE: '26SEP22', - SYSTIME: '08:43', - stagetable: stagetable, - fmt_stagetable: fmtStagetable, - _DEBUG: '', - _METAUSER: 'sasdemo@SAS', - _METAPERSON: 'sasdemo', - _PROGRAM: _program, - AUTOEXEC: '', - MF_GETUSER: 'sasdemo', - SYSCC: '0', - SYSENCODING: 'utf-8', - SYSERRORTEXT: '', - SYSHOSTNAME: 'sasjs-server', - SYSPROCESSID: '0', - SYSPROCESSMODE: 'Stored Program', - SYSPROCESSNAME: '', - SYSJOBID: '0', - SYSSCPL: 'LINUX', - SYSSITE: '123', - SYSUSERID: 'sasdemo', - SYSVLONG: '9.04.01M7P080520', - SYSWARNINGTEXT: '', - END_DTTM: new Date().toISOString(), - MEMSIZE: '0KB' + SYSDATE: '26SEP22', SYSTIME: '08:43', + stagetable, fmt_stagetable, + _DEBUG: '', _METAUSER: 'sasdemo@SAS', _METAPERSON: 'sasdemo', + _PROGRAM: _program, AUTOEXEC: '', MF_GETUSER: 'sasdemo', + SYSCC: '0', SYSENCODING: 'utf-8', SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', SYSPROCESSNAME: '', + SYSJOBID: '0', SYSSCPL: 'LINUX', SYSSITE: '123', + SYSUSERID: 'sasdemo', SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', END_DTTM: new Date().toISOString(), MEMSIZE: '0KB' }) diff --git a/sas/mocks/sasjs/services/dcMockUtils.js b/sas/mocks/sasjs/services/dcMockUtils.js new file mode 100644 index 0000000..87735f6 --- /dev/null +++ b/sas/mocks/sasjs/services/dcMockUtils.js @@ -0,0 +1,373 @@ +/** + * @file dcMockUtils.js + * @brief Shared utilities for Data Controller JS mock services on SASjs Server. + * + * Provides: + * - fetchTable / fetchRaw / parseCsv : emulate SAS %webout(FETCH) + * - webOutOpen / webOutObj / webOutClose : emulate SAS %webout(OPEN/OBJ/CLOSE) + * - formatSasValue : apply a SAS format to a numeric value + * - parseFormattedToSas : convert an ISO string back to a SAS numeric + * - getDdType : derive DATE/DATETIME/TIME/N/C from a column format + * - loadTableData / loadTableSchema : read a DC data JSON file from the Drive + * - sasVarsRow : build a $sasdata/$viewdata vars entry for a column + * - sasVarsRow : build a $sasdata/$viewdata vars entry for a column + * + * Usage (from a mock service that is eval'd by the SASjs Server): + * const _utilPath = nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js') + * eval(fs.readFileSync(_utilPath, 'utf8')) + * const rows = fetchTable('SASControlTable') + * const libds = rows[0]?.LIBDS + */ + +// ─── SAS epoch ──────────────────────────────────────────────────────────────── + +const SAS_EPOCH_MS = Date.UTC(1960, 0, 1) +const MONTHS_ABBR = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'] +const MONTHS_NUM = {JAN:'01',FEB:'02',MAR:'03',APR:'04',MAY:'05',JUN:'06',JUL:'07',AUG:'08',SEP:'09',OCT:'10',NOV:'11',DEC:'12'} + +// ─── %webout(FETCH) emulation ──────────────────────────────────────────────── + +/** + * Fetches the raw CSV text of a named input table. + * Uses eval() to access variables in the CALLER's scope. + */ +function fetchRaw(tableName) { + // 1. Direct variable (SASjs Server may set form fields as top-level JS vars) + try { + const val = eval(tableName) + if (val !== undefined && val !== null && val !== '') return val.toString() + } catch(e) { /* not defined */ } + + // 2. _WEBIN_NAME / _WEBIN_FILEREF loop (SASjs Server format) + // Variable names are UPPERCASE: _WEBIN_NAME1, _WEBIN_FILEREF1, etc. + // _WEBIN_FILEREF may be a Buffer (from fs.readFileSync) or a string. + for (let i = 1; i <= 10; i++) { + try { + const nameVal = eval('_WEBIN_NAME' + i) + if (nameVal && nameVal.toLowerCase() === tableName.toLowerCase()) { + const refVal = eval('_WEBIN_FILEREF' + i) + if (refVal) return refVal.toString() + } + } catch(e) {} + // Also try lowercase variant + try { + const nameVal = eval('_webin_name' + i) + if (nameVal && nameVal.toLowerCase() === tableName.toLowerCase()) { + const refVal = eval('_webin_fileref' + i) + if (refVal) return refVal.toString() + } + } catch(e) {} + } + + // 3. _WEBIN_FILENAME / _WEBIN_FILEREF fallback + for (let i = 1; i <= 10; i++) { + try { + const nameVal = eval('_WEBIN_FILENAME' + i) + if (nameVal && nameVal.toLowerCase().includes(tableName.toLowerCase())) { + const refVal = eval('_WEBIN_FILEREF' + i) + if (refVal) return refVal.toString() + } + } catch(e) {} + try { + const nameVal = eval('_webin_filename' + i) + if (nameVal && nameVal.toLowerCase().includes(tableName.toLowerCase())) { + const refVal = eval('_webin_fileref' + i) + if (refVal) return refVal.toString() + } + } catch(e) {} + } + + return null +} + +/** + * Fetches a named input table as parsed row objects. + */ +function fetchTable(tableName) { + const raw = fetchRaw(tableName) + if (!raw) return [] + return parseCsv(raw) +} + +/** + * Parses a SASjs adapter CSV string into an array of row objects. + * Header line: "COL1:$char10. COL2:$char8." (space-separated, name before colon) + * Data lines: comma-separated with double-quote escaping. + */ +function parseCsv(csvText) { + if (!csvText) return [] + const lines = csvText.split(/\r?\n/) + if (lines.length < 2) return [] + + const headers = lines[0].trim().split(/\s+/).map(h => h.split(':')[0]) + if (headers.length === 0) return [] + + const rows = [] + for (let i = 1; i < lines.length; i++) { + const line = lines[i].trim() + if (!line) continue + const values = _parseCsvLine(line) + const row = {} + for (let j = 0; j < headers.length && j < values.length; j++) { + row[headers[j]] = values[j] + } + rows.push(row) + } + return rows +} + +function _parseCsvLine(line) { + const values = [] + let cur = '' + let inQuote = false + for (let j = 0; j < line.length; j++) { + const ch = line[j] + if (ch === '"') { + if (inQuote && line[j + 1] === '"') { cur += '"'; j++ } + else inQuote = !inQuote + } else if (ch === ',' && !inQuote) { + values.push(cur); cur = '' + } else { cur += ch } + } + values.push(cur) + return values +} + +// ─── SAS format <-> ISO conversions ────────────────────────────────────────── + +/** + * Derives a DDTYPE (DATE, DATETIME, TIME, N, C) from a SAS column format string. + */ +function getDdType(col) { + const fmt = (col.format || '').toLowerCase() + if (fmt.startsWith('datetime') || fmt.startsWith('e8601dt')) return 'DATETIME' + if (fmt.startsWith('date') || fmt.startsWith('yymmdd') || fmt.startsWith('ddmmyy') || fmt.startsWith('mmddyy')) return 'DATE' + if (fmt.startsWith('time')) return 'TIME' + return col.type === 'N' ? 'N' : 'C' +} + +/** + * Applies a SAS format to a raw numeric value, producing the display string. + * DATE: 42 -> "12FEB1960" + * DATETIME: 42 -> "01JAN1960:00:00:42" + * TIME: 42 -> "0:00:42" + */ +function formatSasValue(value, ddtype) { + if (value === null || value === undefined || value === '') return String(value) + if (typeof value !== 'number') return String(value) + + if (ddtype === 'DATE') { + const d = new Date(SAS_EPOCH_MS + value * 86400000) + return `${d.getUTCDate()}${MONTHS_ABBR[d.getUTCMonth()]}${d.getUTCFullYear()}` + } + if (ddtype === 'DATETIME') { + const d = new Date(SAS_EPOCH_MS + value * 1000) + return `${d.getUTCDate()}${MONTHS_ABBR[d.getUTCMonth()]}${d.getUTCFullYear()}:${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}:${String(d.getUTCSeconds()).padStart(2,'0')}` + } + if (ddtype === 'TIME') { + const h = Math.floor(value / 3600) + const m = Math.floor((value % 3600) / 60) + const s = value % 60 + return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}` + } + return String(value) +} + +/** + * Converts a numeric SAS value to the ISO string Handsontable expects. + * DATE: 42 -> "1960-02-12" + * DATETIME: 42 -> "1960-01-01 00:00:42" + * TIME: 42 -> "00:00:42" + */ +function sasToIso(value, ddtype) { + if (value === null || value === undefined || value === '') return value + if (typeof value !== 'number') return value + + if (ddtype === 'DATE') { + const d = new Date(SAS_EPOCH_MS + value * 86400000) + return `${d.getUTCFullYear()}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')}` + } + if (ddtype === 'DATETIME') { + const d = new Date(SAS_EPOCH_MS + value * 1000) + return `${d.getUTCFullYear()}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')} ${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}:${String(d.getUTCSeconds()).padStart(2,'0')}` + } + if (ddtype === 'TIME') { + const h = Math.floor(value / 3600) + const m = Math.floor((value % 3600) / 60) + const s = value % 60 + return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}` + } + return value +} + +/** + * Converts an ISO-formatted string (as sent by the editor) back to a SAS numeric. + * DATE: "1960-02-12" -> 42 + * DATETIME: "1960-01-01 00:00:42" -> 42 + * TIME: "00:00:42" -> 42 + * Non-temporal values pass through unchanged. + */ +function parseFormattedToSas(value, ddtype) { + if (value === null || value === undefined || value === '') return value + if (ddtype !== 'DATE' && ddtype !== 'DATETIME' && ddtype !== 'TIME') return value + if (typeof value === 'number') return value + const str = String(value) + + if (ddtype === 'DATE') { + const m = str.match(/^(\d{4})-(\d{2})-(\d{2})$/) + if (m) return Math.round((Date.UTC(+m[1], +m[2]-1, +m[3]) - SAS_EPOCH_MS) / 86400000) + return value + } + if (ddtype === 'DATETIME') { + const m = str.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})/) + if (m) return Math.round((Date.UTC(+m[1], +m[2]-1, +m[3], +m[4], +m[5], +m[6]) - SAS_EPOCH_MS) / 1000) + return value + } + if (ddtype === 'TIME') { + const parts = str.split(':') + if (parts.length >= 3) return (+parts[0]) * 3600 + (+parts[1]) * 60 + (+parts[2]) + return value + } + return value +} + +/** + * Handles either numeric or string temporal values, converting to ISO for + * Handsontable. Numeric values use sasToIso; string values use the legacy + * regex parsing (for backwards compatibility with old data files). + */ +function formatCellValue(value, ddtype) { + if (value === null || value === undefined || value === '') return value + if (typeof value === 'number') return sasToIso(value, ddtype) + + const str = String(value) + if (ddtype === 'DATE') { + const m = str.match(/^(\d{1,2})([A-Z]{3})(\d{4})$/) + if (m) return `${m[3]}-${MONTHS_NUM[m[2]]}-${m[1].padStart(2,'0')}` + return str + } + if (ddtype === 'DATETIME') { + const m = str.match(/^(\d{1,2})([A-Z]{3})(\d{4}):(\d{1,2}):(\d{2}):(\d{2})$/) + if (m) return `${m[3]}-${MONTHS_NUM[m[2]]}-${m[1].padStart(2,'0')} ${m[4].padStart(2,'0')}:${m[5]}:${m[6]}` + return str + } + if (ddtype === 'TIME') { + const parts = str.split(':') + if (parts.length >= 3) return `${parts[0].padStart(2,'0')}:${parts[1].padStart(2,'0')}:${parts[2].padStart(2,'0')}` + return str + } + return value +} + +// ─── DC data file helpers ──────────────────────────────────────────────────── + +/** + * Creates a loadTableData function bound to a specific data directory. + * const loadTableData = makeTableLoader(dataDir) + * const mpeXTest = loadTableData('MPE_X_TEST') + */ +function makeTableLoader(dataDir) { + return function loadTableData(tableName) { + try { + const file = nodePath.resolve(dataDir, tableName.toLowerCase() + '.json') + return JSON.parse(fs.readFileSync(file, {encoding:'utf8'}).toString()) + } catch(err) { return null } + } +} + +/** + * Loads just the column schema (array of column metadata) for a table. + */ +function loadTableSchema(dataDir, tableName) { + const data = makeTableLoader(dataDir)(tableName) + return (data && data.columns) ? data.columns : [] +} + +/** + * Builds a $sasdata/$viewdata vars entry for a column. + * Temporal columns are sent as char ($200.) by the real SAS backend. + */ +function sasVarsEntry(col) { + const ddtype = getDdType(col) + if (ddtype === 'DATE' || ddtype === 'DATETIME' || ddtype === 'TIME') { + return { format: '$200.', label: col.label || col.name, length: '200', type: 'char' } + } + const fmt = col.type === 'N' ? (col.format || 'best.') : ('$' + col.length + '.') + return { format: fmt, label: col.label || col.name, length: String(col.length), type: col.type === 'N' ? 'num' : 'char' } +} + +// ─── %webout(OPEN/OBJ/CLOSE) emulation ─────────────────────────────────────── + +/** + * Internal buffer for the webOut stream. + * webOutOpen() starts it, webOutObj() appends tables, webOutClose() + * finalises and assigns to _webout. + */ +let _webOutBuf = '' +let _webOutFirst = true + +/** + * Opens the JSON stream, writing the opening brace and PROCESSED_DTTM. + * Mirrors %webout(OPEN). + * + * Usage: + * webOutOpen() + * webOutObj(rows, 'sasdata', { vars }) + * webOutObj(params, 'sasparams') + * webOutClose() + */ +function webOutOpen() { + const now = new Date() + const pad = n => String(n).padStart(2, '0') + const processedDttm = now.getFullYear() + '-' + pad(now.getMonth()+1) + '-' + pad(now.getDate()) + + 'T' + pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds()) + + '.' + String(now.getMilliseconds()).padStart(3,'0') + _webOutBuf = '{"PROCESSED_DTTM":"' + processedDttm + '"' + _webOutFirst = true +} + +/** + * Appends a table to the JSON stream. + * Mirrors %webout(OBJ,ds,dslabel=,showmeta=). + * + * @param {Array} rows - Array of row objects to send as a JSON array. + * @param {string} label - The key name in the JSON (e.g. 'sasdata', 'sasparams'). + * @param {Object} [meta] - Optional metadata object. If provided, it is sent + * as `$label` (e.g. `$sasdata`), matching the showmeta=YES output from SAS. + * Typically { vars: { COL1: {format,label,length,type}, ... } }. + */ +function webOutObj(rows, label, meta) { + if (_webOutFirst) { _webOutBuf += ','; _webOutFirst = false } + else _webOutBuf += ',' + + _webOutBuf += '"' + label + '":' + JSON.stringify(rows) + + if (meta) { + _webOutBuf += ',"$' + label + '":' + JSON.stringify(meta) + } +} + +/** + * Closes the JSON stream, appends the standard SAS runtime variables + * (SYSDATE, SYSCC, _PROGRAM, etc.), and assigns the final JSON string to + * the `_webout` variable that the SASjs Server reads. + * Mirrors %webout(CLOSE). + */ +function webOutClose() { + _webOutBuf += '}' + _webout = _webOutBuf +} + +/** + * Convenience: builds a complete response in one call. + * Opens, adds each table, closes. + * + * @param {Object} tables - { label1: { rows: [...], meta: {...} }, label2: { rows: [...] } } + */ +function webOutSend(tables) { + webOutOpen() + for (const [label, entry] of Object.entries(tables)) { + webOutObj(entry.rows, label, entry.meta) + } + webOutClose() +} diff --git a/sas/mocks/sasjs/services/editors/getdata.js b/sas/mocks/sasjs/services/editors/getdata.js index 6f051c7..1e21709 100644 --- a/sas/mocks/sasjs/services/editors/getdata.js +++ b/sas/mocks/sasjs/services/editors/getdata.js @@ -6,248 +6,111 @@ const driveRoot = nodePath.resolve(sasjsRoot, 'drive') const dcLibref = 'DC_JSLIB' const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) -let FILTER_TEXT = '' +// Load shared DC mock utilities +eval(fs.readFileSync(nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js'), 'utf8')) -// Read SASControlTable from the adapter input -let sasControlTableFileText = '' -if (typeof _WEBIN_NAME1 !== 'undefined' && _WEBIN_NAME1 && _WEBIN_NAME1.includes('SASControlTable')) { - sasControlTableFileText = _WEBIN_FILEREF1.toString() -} else if (typeof _WEBIN_FILENAME1 !== 'undefined' && _WEBIN_FILENAME1 && _WEBIN_FILENAME1.includes('SASControlTable')) { - sasControlTableFileText = _WEBIN_FILEREF1.toString() -} +const loadTableData = makeTableLoader(dataDir) -// Extract the requested table name from SASControlTable -// The frontend sends LIBDS (e.g. "DC_JSLIB.MPE_X_TEST"), not separate LIBREF/DSN -// Adapter CSV format: "LIBDS:$100.\r\nDC_JSLIB.MPE_X_TEST\r\n" +// ─── Parse input ────────────────────────────────────────────────────────────── + +const _sctRow = fetchTable('SASControlTable')[0] || {} let requestedLibref = dcLibref let requestedTable = '' -try { - const lines = sasControlTableFileText.split(/\r?\n/) - if (lines.length >= 2) { - const headers = lines[0].trim().split(/\s+/).map(h => h.split(':')[0].trim().toLowerCase()) - const values = lines[1].trim().split(',') - const libdsIndex = headers.indexOf('libds') - if (libdsIndex >= 0 && values[libdsIndex]) { - const libds = values[libdsIndex].trim() - const parts = libds.split('.') - if (parts.length >= 2) { - requestedLibref = parts[0] - requestedTable = parts[1] - } - } - // Fallback: check for separate LIBREF/DSN columns - if (!requestedTable) { - const dsIndex = headers.indexOf('dsn') - const libIndex = headers.indexOf('libref') - if (dsIndex >= 0 && values[dsIndex]) requestedTable = values[dsIndex].trim() - if (libIndex >= 0 && values[libIndex]) requestedLibref = values[libIndex].trim() - } - } -} catch(err) {} - -// Also check for filter_pk in the SASControlTable -try { - const lines = sasControlTableFileText.split(/\r?\n/) - if (lines.length >= 2) { - const headers = lines[0].trim().split(/\s+/).map(h => h.split(':')[0].trim().toLowerCase()) - const values = lines[1].trim().split(',') - const filterIndex = headers.indexOf('filter_rk') - if (filterIndex >= 0 && values[filterIndex] === '1') { - const filterStore = nodePath.resolve(driveRoot, 'files', appLoc, 'mock-storage', 'filter.txt') - try { FILTER_TEXT = fs.readFileSync(filterStore, {encoding:'utf8'}).toString() } catch(e) {} - } - } -} catch(err) {} - -// Helper: load a table JSON file from the makedata data directory -function loadTableData(tableName) { - try { - const file = nodePath.resolve(dataDir, tableName.toLowerCase() + '.json') - const raw = fs.readFileSync(file, {encoding:'utf8'}).toString() - return JSON.parse(raw) - } catch(err) { - return null - } +if (_sctRow.LIBDS) { + const parts = _sctRow.LIBDS.split('.') + if (parts.length >= 2) { requestedLibref = parts[0]; requestedTable = parts[1] } +} +if (!requestedTable) { + if (_sctRow.DSN) requestedTable = _sctRow.DSN.trim() + if (_sctRow.LIBREF) requestedLibref = _sctRow.LIBREF.trim() } -// Helper: convert a schema column to the getdata "cols" format -function schemaToCols(columns) { - return columns.map(col => { - const fmt = col.format || '' - let ddtype = col.type === 'N' ? 'N' : 'C' - if (fmt.startsWith('datetime') || fmt.startsWith('E8601DT')) ddtype = 'DATETIME' - else if (fmt.startsWith('date')) ddtype = 'DATE' - else if (fmt.startsWith('time')) ddtype = 'TIME' - const fmtname = fmt - ? fmt.replace(/[\d.]+$/, '').replace('datetime', 'DATETIME').replace('date', 'DATE').replace('time', 'TIME').replace('best', 'BEST').replace('E8601DT', 'DATETIME') - : '' - // COLTYPE type maps to DDTYPE: date, datetime, time, numeric, or omitted (character) - let coltype - if (ddtype === 'DATE') { - coltype = `{"data":"${col.name}","type":"date"}` - } else if (ddtype === 'DATETIME') { - coltype = `{"data":"${col.name}","type":"datetime"}` - } else if (ddtype === 'TIME') { - coltype = `{"data":"${col.name}","type":"time"}` - } else if (ddtype === 'N') { - coltype = `{"data":"${col.name}","type":"numeric","format":"0"}` - } else { - coltype = `{"data":"${col.name}"}` - } - return { - NAME: col.name, - LABEL: col.label || col.name, - FMTNAME: fmtname || ' ', - DDTYPE: ddtype, - CLS_RULE: 'READ', - MEMLABEL: ' ', - DESC: ' ', - LONGDESC: ' ', - COLTYPE: coltype - } - }) -} +// ─── Load table data and schema ────────────────────────────────────────────── -// Helper: compute maxvarlengths from actual row data -// Row keys may be any case, so do case-insensitive lookup -function computeMaxVarLengths(columns, rows) { - return columns.map(col => { - let maxLen = 0 - const lcName = col.name.toLowerCase() - for (const row of rows) { - // Try exact match first, then case-insensitive - let val = row[col.name] - if (val === undefined) val = row[lcName] - if (val === undefined) { - const key = Object.keys(row).find(k => k.toLowerCase() === lcName) - if (key) val = row[key] - } - if (val !== undefined && val !== null) { - const len = String(val).length - if (len > maxLen) maxLen = len - } - } - return { NAME: col.name.toLowerCase(), MAXLEN: maxLen } - }) -} - -// Helper: convert a SAS date/datetime/time value to the ISO format expected by Handsontable -// SAS dates: "12FEB1960" -> "1960-02-12" -// SAS datetimes: "01JAN1960:00:00:42" -> "1960-01-01 00:00:42" -// SAS times: "0:00:42" or "0:07:02" -> "00:00:42" or "00:07:02" -function formatCellValue(value, ddtype) { - if (value === null || value === undefined || value === '') return value - const str = String(value) - - if (ddtype === 'DATE') { - // SAS date: DDMMMYYYY -> YYYY-MM-DD - const months = {JAN:'01',FEB:'02',MAR:'03',APR:'04',MAY:'05',JUN:'06',JUL:'07',AUG:'08',SEP:'09',OCT:'10',NOV:'11',DEC:'12'} - const m = str.match(/^(\d{1,2})([A-Z]{3})(\d{4})$/) - if (m) return `${m[3]}-${months[m[2]]}-${m[1].padStart(2,'0')}` - return str - } - - if (ddtype === 'DATETIME') { - // SAS datetime: DDMMMYYYY:HH:MM:SS -> YYYY-MM-DD HH:mm:ss - const months = {JAN:'01',FEB:'02',MAR:'03',APR:'04',MAY:'05',JUN:'06',JUL:'07',AUG:'08',SEP:'09',OCT:'10',NOV:'11',DEC:'12'} - const m = str.match(/^(\d{1,2})([A-Z]{3})(\d{4}):(\d{1,2}):(\d{2}):(\d{2})$/) - if (m) return `${m[3]}-${months[m[2]]}-${m[1].padStart(2,'0')} ${m[4].padStart(2,'0')}:${m[5]}:${m[6]}` - return str - } - - if (ddtype === 'TIME') { - // SAS time: H:MM:SS or H:MM:SS -> HH:mm:ss - const parts = str.split(':') - if (parts.length >= 3) { - return `${parts[0].padStart(2,'0')}:${parts[1].padStart(2,'0')}:${parts[2].padStart(2,'0')}` - } - return str - } - - return value -} - -// Helper: prepend _____DELETE__THIS__RECORD_____ to rows and build $sasdata vars -// Also normalises row keys to match column names (uppercase) -// Also formats date/datetime/time values to ISO format for Handsontable -function addDeleteFlag(columns, rows) { - // Build a lookup from lowercase column name to { name, ddtype } - const colLookup = {} - for (const col of columns) { - const fmt = (col.format || '').toLowerCase() - let ddtype = col.type === 'N' ? 'N' : 'C' - if (fmt.startsWith('datetime') || fmt.startsWith('e8601dt')) ddtype = 'DATETIME' - else if (fmt.startsWith('date')) ddtype = 'DATE' - else if (fmt.startsWith('time')) ddtype = 'TIME' - colLookup[col.name.toLowerCase()] = { name: col.name, ddtype } - } - const augmentedRows = rows.map(r => { - const normalised = {} - for (const key of Object.keys(r)) { - const info = colLookup[key.toLowerCase()] - const targetKey = info ? info.name : key - normalised[targetKey] = info ? formatCellValue(r[key], info.ddtype) : r[key] - } - return { - _____DELETE__THIS__RECORD_____: 'No', - ...normalised - } - }) - // $sasdata vars: date/datetime/time are converted to char strings by SAS - const vars = { _____DELETE__THIS__RECORD_____: { format: '$3.', label: '_____DELETE__THIS__RECORD_____', length: '3', type: 'char' } } - for (const col of columns) { - const info = colLookup[col.name.toLowerCase()] - const isTemporal = info && (info.ddtype === 'DATE' || info.ddtype === 'DATETIME' || info.ddtype === 'TIME') - if (isTemporal) { - vars[col.name] = { format: '$200.', label: col.label || col.name, length: '200', type: 'char' } - } else { - const fmt = col.type === 'N' ? (col.format || 'best.') : ('$' + col.length + '.') - vars[col.name] = { format: fmt, label: col.label || col.name, length: String(col.length), type: col.type === 'N' ? 'num' : 'char' } - } - } - return { augmentedRows, vars } -} - -// Load the requested table's data const tableData = loadTableData(requestedTable) -// Load DQ rules for this table from MPE_VALIDATIONS -const validationsData = loadTableData('MPE_VALIDATIONS') -let dqrules = [] -let dqdata = [] -if (validationsData && validationsData.rows) { - dqrules = validationsData.rows - .filter(r => r.base_lib === requestedLibref && r.base_ds === requestedTable) - .map(r => ({ BASE_COL: r.base_col, RULE_TYPE: r.rule_type, RULE_VALUE: r.rule_value, X: 0 })) -} - -// Load DQ dropdown data for this table from MPE_SELECTBOX -const selectboxData = loadTableData('MPE_SELECTBOX') -if (selectboxData && selectboxData.rows) { - dqdata = selectboxData.rows - .filter(r => r.select_lib === requestedLibref && r.select_ds === requestedTable) - .map(r => ({ BASE_COL: r.base_column, RULE_VALUE: r.base_column, RULE_DATA: r.selectbox_value, SELECTBOX_ORDER: r.selectbox_order })) -} - -// Load table registration from MPE_TABLES for sasparams +// Load table registration from MPE_TABLES (needed for PK detection, temporal +// column exclusion, and sasparams) const mpeTablesData = loadTableData('MPE_TABLES') let tableReg = null if (mpeTablesData && mpeTablesData.rows) { tableReg = mpeTablesData.rows.find(r => r.libref === requestedLibref && r.dsn === requestedTable) } -// Build the response +// ─── DQ rules (from MPE_VALIDATIONS) ───────────────────────────────────────── + +const validationsData = loadTableData('MPE_VALIDATIONS') +let dqrules = [] +let dqdata = [] +if (validationsData && validationsData.rows) { + const pkCols = new Set() + if (tableReg && tableReg.buskey) { + tableReg.buskey.trim().split(/\s+/).forEach(c => pkCols.add(c.toUpperCase())) + } + dqrules = validationsData.rows + .filter(r => r.base_lib === requestedLibref && r.base_ds === requestedTable) + .map(r => ({ + BASE_COL: r.base_col, + RULE_TYPE: r.rule_type, + RULE_VALUE: r.rule_value, + X: pkCols.has(r.base_col.toUpperCase()) ? 0 : 1 + })) +} + +// ─── DQ dropdown data (from MPE_SELECTBOX + SOFTSELECT from table data) ────── + +const selectboxData = loadTableData('MPE_SELECTBOX') +if (selectboxData && selectboxData.rows) { + dqdata = selectboxData.rows + .filter(r => r.select_lib === requestedLibref && r.select_ds === requestedTable) + .map(r => ({ + BASE_COL: r.base_column, + RULE_VALUE: r.base_column, + RULE_DATA: r.selectbox_value, + SELECTBOX_ORDER: r.selectbox_order + })) +} + +// For SOFTSELECT/HARDSELECT rules that reference the table's own data, +// generate dropdown values from the actual table rows +if (tableData && tableData.rows && tableData.columns) { + const selectRules = dqrules.filter(r => + (r.RULE_TYPE === 'SOFTSELECT' || r.RULE_TYPE === 'HARDSELECT') && r.RULE_VALUE.includes('.')) + for (const rule of selectRules) { + const parts = rule.RULE_VALUE.split('.') + const colName = parts[parts.length - 1] + const lcName = colName.toLowerCase() + const seen = new Set() + let order = 1 + for (const row of tableData.rows) { + let val = row[colName] + if (val === undefined) val = row[lcName] + if (val === undefined) { + const key = Object.keys(row).find(k => k.toLowerCase() === lcName) + if (key) val = row[key] + } + if (val !== undefined && val !== null) { + const strVal = String(val) + if (!seen.has(strVal)) { + seen.add(strVal) + dqdata.push({ BASE_COL: rule.BASE_COL, RULE_VALUE: rule.RULE_VALUE, RULE_DATA: strVal, SELECTBOX_ORDER: order++ }) + } + } + } + } +} + +// ─── Build response ────────────────────────────────────────────────────────── + let response if (tableData && tableData.columns && tableData.rows) { - // For TXTEMPORAL and BITEMPORAL tables, the VAR_TXFROM / VAR_TXTO columns - // are technical versioning columns that should be excluded from the editor + // Exclude temporal columns (TX_FROM/TX_TO etc.) from the editor view let excludeCols = new Set() if (tableReg && (tableReg.loadtype === 'TXTEMPORAL' || tableReg.loadtype === 'BITEMPORAL')) { if (tableReg.var_txfrom) excludeCols.add(tableReg.var_txfrom.toUpperCase()) if (tableReg.var_txto) excludeCols.add(tableReg.var_txto.toUpperCase()) } - // For BITEMPORAL, also exclude VAR_BUSFROM / VAR_BUSTO if (tableReg && tableReg.loadtype === 'BITEMPORAL') { if (tableReg.var_busfrom) excludeCols.add(tableReg.var_busfrom.toUpperCase()) if (tableReg.var_busto) excludeCols.add(tableReg.var_busto.toUpperCase()) @@ -262,29 +125,86 @@ if (tableData && tableData.columns && tableData.rows) { return copy }) - const { augmentedRows, vars } = addDeleteFlag(visibleColumns, visibleRows) + // Augment rows: add delete flag, normalise keys, convert temporal values to ISO + const colLookup = {} + for (const col of visibleColumns) { + colLookup[col.name.toLowerCase()] = { name: col.name, ddtype: getDdType(col) } + } + const augmentedRows = visibleRows.map(r => { + const normalised = {} + for (const key of Object.keys(r)) { + const info = colLookup[key.toLowerCase()] + const targetKey = info ? info.name : key + normalised[targetKey] = info ? formatCellValue(r[key], info.ddtype) : r[key] + } + return { _____DELETE__THIS__RECORD_____: 'No', ...normalised } + }) + + // $sasdata vars: temporal columns are char ($200.), others keep their SAS type + const vars = { _____DELETE__THIS__RECORD_____: { format: '$3.', label: '_____DELETE__THIS__RECORD_____', length: '3', type: 'char' } } + for (const col of visibleColumns) { + vars[col.name] = sasVarsEntry(col) + } + const colHeaders = ['_____DELETE__THIS__RECORD_____', ...visibleColumns.map(c => c.name)] // Determine DTVARS, DTTMVARS, TMVARS from column formats - let dtVars = [] - let dttmVars = [] - let tmVars = [] + let dtVars = [], dttmVars = [], tmVars = [] for (const col of visibleColumns) { - const fmt = (col.format || '').toLowerCase() - if (fmt.startsWith('datetime') || fmt.startsWith('e8601dt')) dttmVars.push(col.name) - else if (fmt.startsWith('date')) dtVars.push(col.name) - else if (fmt.startsWith('time')) tmVars.push(col.name) + const ddtype = getDdType(col) + if (ddtype === 'DATETIME') dttmVars.push(col.name) + else if (ddtype === 'DATE') dtVars.push(col.name) + else if (ddtype === 'TIME') tmVars.push(col.name) } + // Build cols array + const cols = visibleColumns.map(col => { + const ddtype = getDdType(col) + const fmtname = col.format + ? col.format.replace(/[\d.]+$/, '').replace('datetime', 'DATETIME').replace('date', 'DATE').replace('time', 'TIME').replace('best', 'BEST').replace('E8601DT', 'DATETIME') + : ' ' + let coltype + if (ddtype === 'DATE') coltype = `{"data":"${col.name}","type":"date"}` + else if (ddtype === 'DATETIME') coltype = `{"data":"${col.name}","type":"datetime"}` + else if (ddtype === 'TIME') coltype = `{"data":"${col.name}","type":"time"}` + else if (ddtype === 'N') coltype = `{"data":"${col.name}","type":"numeric","format":"0"}` + else coltype = `{"data":"${col.name}"}` + return { + NAME: col.name, + LABEL: col.label || col.name, + FMTNAME: fmtname || ' ', + DDTYPE: ddtype, + CLS_RULE: 'READ', + MEMLABEL: ' ', + DESC: ' ', + LONGDESC: ' ', + COLTYPE: coltype + } + }).sort((a, b) => a.NAME.localeCompare(b.NAME)) + + // Build maxvarlengths + const maxvarlengths = [ + { NAME: '_____DELETE__THIS__RECORD_____', MAXLEN: 3 }, + ...visibleColumns.map(col => { + let maxLen = 0 + const lcName = col.name.toLowerCase() + for (const row of augmentedRows) { + let val = row[col.name] + if (val === undefined) val = row[lcName] + if (val !== undefined && val !== null) { + const len = String(val).length + if (len > maxLen) maxLen = len + } + } + return { NAME: col.name.toLowerCase(), MAXLEN: maxLen } + }) + ] + response = { - SYSDATE: '26SEP22', - SYSTIME: '08:30', - approvers: [ - { PERSONNAME: 'sasdemo', EMAIL: 'sasdemo', USERID: 'sasdemo' } - ], - cols: schemaToCols(visibleColumns), - dqdata: dqdata, - dqrules: dqrules, + SYSDATE: '26SEP22', SYSTIME: '08:30', + approvers: [{ PERSONNAME: 'sasdemo', EMAIL: 'sasdemo', USERID: 'sasdemo' }], + cols, + dqdata, dqrules, dsmeta: [ { ODS_TABLE: 'ATTRIBUTES', NAME: 'Data Set Name', VALUE: requestedLibref + '.' + requestedTable }, { ODS_TABLE: 'ATTRIBUTES', NAME: 'Observations', VALUE: String(tableData.rows.length) }, @@ -292,16 +212,13 @@ if (tableData && tableData.columns && tableData.rows) { { ODS_TABLE: 'ATTRIBUTES', NAME: 'Variables', VALUE: String(tableData.columns.length) }, { ODS_TABLE: 'ATTRIBUTES', NAME: 'Engine', VALUE: 'V9' } ], - maxvarlengths: [ - { NAME: '_____DELETE__THIS__RECORD_____', MAXLEN: 3 }, - ...computeMaxVarLengths(visibleColumns, augmentedRows) - ], + maxvarlengths, query: [], sasdata: augmentedRows, - $sasdata: { vars: vars }, + $sasdata: { vars }, sasparams: [{ COLHEADERS: colHeaders.join(','), - FILTER_TEXT: FILTER_TEXT || ' ', + FILTER_TEXT: ' ', PKCNT: tableReg && tableReg.buskey ? tableReg.buskey.trim().split(/\s+/).length : 0, PK: tableReg ? tableReg.buskey || '' : '', DTVARS: dtVars.length ? ' ' + dtVars.join(' ') : '', @@ -312,84 +229,38 @@ if (tableData && tableData.columns && tableData.rows) { CLS_FLAG: 0, ISMAP: 0 }], + versions: [], xl_rules: [], - _DEBUG: '', - _METAUSER: 'sasdemo@SAS', - _METAPERSON: 'sasdemo', - _PROGRAM: _program, - AUTOEXEC: '', - MF_GETUSER: 'sasdemo', - SYSCC: '0', - SYSENCODING: 'utf-8', - SYSERRORTEXT: '', - SYSHOSTNAME: 'sasjs-server', - SYSPROCESSID: '0', - SYSPROCESSMODE: 'Stored Program', - SYSPROCESSNAME: '', - SYSJOBID: '0', - SYSSCPL: 'LINUX', - SYSSITE: '123', - SYSUSERID: 'sasdemo', - SYSVLONG: '9.04.01M7P080520', - SYSWARNINGTEXT: '', - END_DTTM: new Date().toISOString(), - MEMSIZE: '0KB' + _DEBUG: '', _METAUSER: 'sasdemo@SAS', _METAPERSON: 'sasdemo', + _PROGRAM: _program, AUTOEXEC: '', MF_GETUSER: 'sasdemo', + SYSCC: '0', SYSENCODING: 'utf-8', SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', SYSPROCESSNAME: '', + SYSJOBID: '0', SYSSCPL: 'LINUX', SYSSITE: '123', + SYSUSERID: 'sasdemo', SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', END_DTTM: new Date().toISOString(), MEMSIZE: '0KB' } } else { - // Table not found - return minimal empty response response = { - SYSDATE: '26SEP22', - SYSTIME: '08:30', - approvers: [], - cols: [], - dqdata: [], - dqrules: [], + SYSDATE: '26SEP22', SYSTIME: '08:30', + approvers: [], cols: [], dqdata: [], dqrules: [], dsmeta: [ { ODS_TABLE: 'ATTRIBUTES', NAME: 'Data Set Name', VALUE: requestedLibref + '.' + requestedTable }, { ODS_TABLE: 'ATTRIBUTES', NAME: 'Observations', VALUE: '0' }, { ODS_TABLE: 'ATTRIBUTES', NAME: 'Member Type', VALUE: 'DATA' } ], - maxvarlengths: [], - query: [], - sasdata: [], - $sasdata: { vars: {} }, - sasparams: [{ - COLHEADERS: '', - FILTER_TEXT: ' ', - PKCNT: 0, - PK: '', - DTVARS: '', - DTTMVARS: '', - TMVARS: '', - LOADTYPE: 'UPDATE', - RK_FLAG: 0, - CLS_FLAG: 0, - ISMAP: 0 - }], - xl_rules: [], - _DEBUG: '', - _METAUSER: 'sasdemo@SAS', - _METAPERSON: 'sasdemo', - _PROGRAM: _program, - AUTOEXEC: '', - MF_GETUSER: 'sasdemo', - SYSCC: '0', - SYSENCODING: 'utf-8', - SYSERRORTEXT: '', - SYSHOSTNAME: 'sasjs-server', - SYSPROCESSID: '0', - SYSPROCESSMODE: 'Stored Program', - SYSPROCESSNAME: '', - SYSJOBID: '0', - SYSSCPL: 'LINUX', - SYSSITE: '123', - SYSUSERID: 'sasdemo', - SYSVLONG: '9.04.01M7P080520', - SYSWARNINGTEXT: '', - END_DTTM: new Date().toISOString(), - MEMSIZE: '0KB' + maxvarlengths: [], query: [], sasdata: [], $sasdata: { vars: {} }, + sasparams: [{ COLHEADERS: '', FILTER_TEXT: ' ', PKCNT: 0, PK: '', DTVARS: '', DTTMVARS: '', TMVARS: '', LOADTYPE: 'UPDATE', RK_FLAG: 0, CLS_FLAG: 0, ISMAP: 0 }], + versions: [], xl_rules: [], + _DEBUG: '', _METAUSER: 'sasdemo@SAS', _METAPERSON: 'sasdemo', + _PROGRAM: _program, AUTOEXEC: '', MF_GETUSER: 'sasdemo', + SYSCC: '0', SYSENCODING: 'utf-8', SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', SYSPROCESSNAME: '', + SYSJOBID: '0', SYSSCPL: 'LINUX', SYSSITE: '123', + SYSUSERID: 'sasdemo', SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', END_DTTM: new Date().toISOString(), MEMSIZE: '0KB' } - console.log('Warning: table ' + requestedTable + ' not found in ' + dataDir) } _webout = JSON.stringify(response) diff --git a/sas/mocks/sasjs/services/editors/getdynamiccolvals.js b/sas/mocks/sasjs/services/editors/getdynamiccolvals.js index d283b3e..70b3a79 100644 --- a/sas/mocks/sasjs/services/editors/getdynamiccolvals.js +++ b/sas/mocks/sasjs/services/editors/getdynamiccolvals.js @@ -6213,10 +6213,10 @@ let other = { MEMSIZE: '93GB' } -if (_WEBIN_FILENAME1.includes('SASControlTable')) - jsDataFileText = _WEBIN_FILEREF1.toString() -if (_WEBIN_FILENAME2.includes('SASControlTable')) - jsDataFileText = _WEBIN_FILEREF2.toString() +if (_webin_filename1.includes('SASControlTable')) + jsDataFileText = _webin_fileref1.toString() +if (_webin_filename2.includes('SASControlTable')) + jsDataFileText = _webin_fileref2.toString() if (jsDataFileText.length > 0) { if (jsDataFileText.includes('MPE_TABLES')) { diff --git a/sas/mocks/sasjs/services/editors/stagedata.js b/sas/mocks/sasjs/services/editors/stagedata.js index d67b469..779e90b 100644 --- a/sas/mocks/sasjs/services/editors/stagedata.js +++ b/sas/mocks/sasjs/services/editors/stagedata.js @@ -7,77 +7,30 @@ const dcLibref = 'DC_JSLIB' const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) const stagingDir = nodePath.resolve(dataDir, 'staging') -// Ensure staging directory exists -if (!fs.existsSync(stagingDir)) { - fs.mkdirSync(stagingDir, { recursive: true }) -} +if (!fs.existsSync(stagingDir)) fs.mkdirSync(stagingDir, { recursive: true }) -// Helper: load a table JSON file -function loadTableData(tableName) { - try { - const file = nodePath.resolve(dataDir, tableName.toLowerCase() + '.json') - return JSON.parse(fs.readFileSync(file, {encoding:'utf8'}).toString()) - } catch(err) { return null } -} +eval(fs.readFileSync(nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js'), 'utf8')) + +const loadTableData = makeTableLoader(dataDir) -// Helper: save a table JSON file function saveTableData(tableName, tableData) { const file = nodePath.resolve(dataDir, tableName.toLowerCase() + '.json') fs.writeFileSync(file, JSON.stringify(tableData, null, 2)) } -// Parse SASControlTable (ACTION, MESSAGE, LIBDS) -let action = 'LOAD' -let message = '' -let libds = '' -let sasControlTableFileText = '' - -for (let i = 1; i <= 10; i++) { - const nameVar = '_WEBIN_NAME' + i - try { - const nameVal = eval(nameVar) - if (nameVal && nameVal.includes('SASControlTable')) { - const refVar = '_WEBIN_FILEREF' + i - sasControlTableFileText = eval(refVar).toString() - break - } - } catch(e) { break } -} - -try { - const lines = sasControlTableFileText.split(/\r?\n/) - if (lines.length >= 2) { - const headers = lines[0].trim().split(/\s+/).map(h => h.split(':')[0].trim().toLowerCase()) - const values = lines[1].trim().split(',') - const actionIdx = headers.indexOf('action') - const msgIdx = headers.indexOf('message') - const libdsIdx = headers.indexOf('libds') - if (actionIdx >= 0) action = values[actionIdx] || 'LOAD' - if (msgIdx >= 0) message = values[msgIdx] || '' - if (libdsIdx >= 0) libds = values[libdsIdx] || '' - } -} catch(err) {} +// ─── Parse input ────────────────────────────────────────────────────────────── +const _sctRow = fetchTable('SASControlTable')[0] || {} +let action = _sctRow.ACTION || 'LOAD' +let message = _sctRow.MESSAGE || '' +let libds = _sctRow.LIBDS || '' const libref = libds.split('.')[0] || dcLibref const dsn = libds.split('.')[1] || '' // Parse jsdata (the edited rows from the grid) -let jsDataRows = [] -let jsDataRaw = '' +const jsDataRaw = fetchRaw('jsdata') let jsDataHeaders = [] - -for (let i = 1; i <= 10; i++) { - const nameVar = '_WEBIN_NAME' + i - try { - const nameVal = eval(nameVar) - if (nameVal && nameVal.toLowerCase() === 'jsdata') { - const refVar = '_WEBIN_FILEREF' + i - jsDataRaw = eval(refVar).toString() - break - } - } catch(e) { break } -} - +let jsDataRows = [] if (jsDataRaw) { try { const lines = jsDataRaw.split(/\r?\n/) @@ -87,34 +40,39 @@ if (jsDataRaw) { const line = lines[i].trim() if (!line) continue const values = [] - let cur = '' - let inQuote = false + let cur = '', inQuote = false for (let j = 0; j < line.length; j++) { const ch = line[j] - if (ch === '"') { - if (inQuote && line[j+1] === '"') { cur += '"'; j++ } - else inQuote = !inQuote - } else if (ch === ',' && !inQuote) { - values.push(cur) - cur = '' - } else { - cur += ch - } + if (ch === '"') { if (inQuote && line[j+1] === '"') { cur += '"'; j++ } else inQuote = !inQuote } + else if (ch === ',' && !inQuote) { values.push(cur); cur = '' } + else cur += ch } values.push(cur) const row = {} - for (let k = 0; k < jsDataHeaders.length && k < values.length; k++) { - row[jsDataHeaders[k]] = values[k] - } + for (let k = 0; k < jsDataHeaders.length && k < values.length; k++) row[jsDataHeaders[k]] = values[k] jsDataRows.push(row) } } - } catch(err) { - console.log('Error parsing jsdata: ' + err.message) - } + } catch(err) { console.log('Error parsing jsdata: ' + err.message) } } -// Generate a unique table_id matching SAS mperef format +// Convert formatted date/datetime/time strings back to SAS numeric values +const schema = loadTableSchema(dataDir, dsn) +if (schema.length > 0) { + const colDdtypes = {} + for (const col of schema) colDdtypes[col.name.toUpperCase()] = getDdType(col) + jsDataRows = jsDataRows.map(row => { + const converted = {} + for (const key of Object.keys(row)) { + const ddtype = colDdtypes[key.toUpperCase()] + converted[key] = ddtype ? parseFormattedToSas(row[key], ddtype) : row[key] + } + return converted + }) +} + +// ─── Generate table_id and stage the data ──────────────────────────────────── + const now = new Date() const pad = (n, w) => String(n).padStart(w, '0') const dtStr = now.getFullYear() + pad(now.getMonth()+1,2) + pad(now.getDate(),2) + '-' + @@ -124,70 +82,36 @@ const rand6 = pad(Math.floor(Math.random() * 1000000), 6) const pid4 = pad(process.pid % 10000, 4) const tableId = `DC${dtStr}_${rand6}_${pid4}` -// Store the staged rows as a separate file in the staging directory const stageFile = nodePath.resolve(stagingDir, tableId + '.json') const stageData = { - table_id: tableId, - libds: libds, - libref: libref, - dsn: dsn, - headers: jsDataHeaders, - rows: jsDataRows + table_id: tableId, libds, libref, dsn, + headers: jsDataHeaders, rows: jsDataRows } fs.writeFileSync(stageFile, JSON.stringify(stageData, null, 2)) // Insert a record into MPE_SUBMIT const submitTable = loadTableData('MPE_SUBMIT') || { name: 'MPE_SUBMIT', columns: [], rows: [] } -const submitRow = { - TABLE_ID: tableId, - SUBMIT_STATUS_CD: 'SUBMITTED', - BASE_LIB: libref, - BASE_DS: dsn, - SUBMITTED_BY_NM: 'sasdemo', - SUBMITTED_ON_DTTM: now.getTime() / 1000, +submitTable.rows.push({ + TABLE_ID: tableId, SUBMIT_STATUS_CD: 'SUBMITTED', + BASE_LIB: libref, BASE_DS: dsn, + SUBMITTED_BY_NM: 'sasdemo', SUBMITTED_ON_DTTM: now.getTime() / 1000, SUBMITTED_REASON_TXT: message, INPUT_OBS: jsDataRows.length, INPUT_VARS: jsDataHeaders.filter(h => h !== '_____DELETE__THIS__RECORD_____').length, - NUM_OF_APPROVALS_REQUIRED: 1, - NUM_OF_APPROVALS_REMAINING: 1, - REVIEWED_BY_NM: '', - REVIEWED_ON_DTTM: null -} -submitTable.rows.push(submitRow) + NUM_OF_APPROVALS_REQUIRED: 1, NUM_OF_APPROVALS_REMAINING: 1, + REVIEWED_BY_NM: '', REVIEWED_ON_DTTM: null +}) saveTableData('MPE_SUBMIT', submitTable) -console.log('Staged data written to: ' + stageFile) -console.log('table_id: ' + tableId) -console.log('libds: ' + libds) -console.log('rows: ' + jsDataRows.length) - _webout = JSON.stringify({ - SYSDATE: '26SEP22', - SYSTIME: '08:43', - sasparams: [{ - STATUS: 'SUCCESS', - DSID: tableId, - URL: '' - }], - _DEBUG: '', - _METAUSER: 'sasdemo@SAS', - _METAPERSON: 'sasdemo', - _PROGRAM: _program, - AUTOEXEC: '', - MF_GETUSER: 'sasdemo', - SYSCC: '0', - SYSENCODING: 'utf-8', - SYSERRORTEXT: '', - SYSHOSTNAME: 'sasjs-server', - SYSPROCESSID: '0', - SYSPROCESSMODE: 'Stored Program', - SYSPROCESSNAME: '', - SYSJOBID: '0', - SYSSCPL: 'LINUX', - SYSSITE: '123', - SYSUSERID: 'sasdemo', - SYSVLONG: '9.04.01M7P080520', - SYSWARNINGTEXT: '', - END_DTTM: new Date().toISOString(), - MEMSIZE: '0KB' + SYSDATE: '26SEP22', SYSTIME: '08:43', + sasparams: [{ STATUS: 'SUCCESS', DSID: tableId, URL: '' }], + _DEBUG: '', _METAUSER: 'sasdemo@SAS', _METAPERSON: 'sasdemo', + _PROGRAM: _program, AUTOEXEC: '', MF_GETUSER: 'sasdemo', + SYSCC: '0', SYSENCODING: 'utf-8', SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', SYSPROCESSNAME: '', + SYSJOBID: '0', SYSSCPL: 'LINUX', SYSSITE: '123', + SYSUSERID: 'sasdemo', SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', END_DTTM: new Date().toISOString(), MEMSIZE: '0KB' }) diff --git a/sas/mocks/sasjs/services/public/getchangeinfo.js b/sas/mocks/sasjs/services/public/getchangeinfo.js index f3e094d..4448aa4 100644 --- a/sas/mocks/sasjs/services/public/getchangeinfo.js +++ b/sas/mocks/sasjs/services/public/getchangeinfo.js @@ -1,29 +1,68 @@ -_webout = `{"SYSDATE" : "07OCT22" -,"SYSTIME" : "12:25" -, "jsparams": -[ -{"TABLE_ID":"DC20221007T122326121_612316_7259" ,"SUBMIT_STATUS_CD":"SUBMITTED" ,"BASE_LIB":"DC_JSLIB" ,"BASE_DS":"MPE_X_TEST" ,"SUBMITTED_BY_NM":"mihajlo" ,"SUBMITTED_ON":1980764606.22 ,"SUBMITTED_REASON_TXT":"" ,"INPUT_OBS":15 ,"INPUT_VARS":10 ,"NUM_OF_APPROVALS_REQUIRED":1 ,"NUM_OF_APPROVALS_REMAINING":1 ,"REVIEWED_BY_NM":"" ,"REVIEWED_ON":null ,"TABLE_NM":"DC_JSLIB.MPE_X_TEST" ,"BASE_TABLE":"DC_JSLIB.MPE_X_TEST" ,"REVIEWED_ON_DTTM":"." ,"SUBMITTED_ON_DTTM":"07OCT2022:12:23:26" ,"LIB_ENGINE":"WPD" } -] -,"_DEBUG" : "" -,"_PROGRAM" : "/30.SASApps/app/mihajlo/services/public/getchangeinfo" -,"AUTOEXEC" : "%2Fhome%2Fmihajlo%2Fsasjs_root%2Fsessions%2F20221007122549-44480-1665145549621%2Fautoexec.sas" -,"MF_GETUSER" : "mihajlo" -,"SYSCC" : "0" -,"SYSENCODING" : "utf-8" -,"SYSERRORTEXT" : "" -,"SYSHOSTINFOLONG" : "" -,"SYSHOSTNAME" : "sas.4gl.io" -,"SYSPROCESSID" : "41DD8404936CF0DC0000000000000000" -,"SYSPROCESSMODE" : "Stored Program" -,"SYSPROCESSNAME" : "" -,"SYSJOBID" : "726819" -,"SYSSCPL" : "LINUX" -,"syssite" : "123" -,"SYSTCPIPHOSTNAME" : "https://sas.4gl.io:5002" -,"SYSUSERID" : "mihajlo" -,"SYSVLONG" : "05.00.00.02.001146" -,"SYSWARNINGTEXT" : "" -,"END_DTTM" : "2022-10-07T12:25:57.433259" -,"MEMSIZE" : "0KB" +const nodePath = require('path') + +let appLoc = nodePath.join(..._program.split('services')[0].split('/')) +const sasjsRoot = nodePath.resolve(weboutPath, '..', '..', '..') +const driveRoot = nodePath.resolve(sasjsRoot, 'drive') +const dcLibref = 'DC_JSLIB' +const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) + +eval(fs.readFileSync(nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js'), 'utf8')) +const loadTableData = makeTableLoader(dataDir) + +// ─── Parse input: frontend sends SASControlTable with TABLE column ─────────── + +const _sctRow = fetchTable('SASControlTable')[0] || {} +const tableId = _sctRow.TABLE || _sctRow.table || '' + +// ─── Load submit info from MPE_SUBMIT ──────────────────────────────────────── + +let jsparams = [] +if (tableId) { + const submitData = loadTableData('MPE_SUBMIT') + if (submitData && submitData.rows) { + const row = submitData.rows.find(r => r.TABLE_ID === tableId) + if (row) { + const baseTable = row.BASE_LIB + '.' + row.BASE_DS + const submittedOn = new Date(row.SUBMITTED_ON_DTTM * 1000) + const pad = n => String(n).padStart(2, '0') + const submittedOnStr = submittedOn.getFullYear() + '-' + pad(submittedOn.getMonth()+1) + '-' + pad(submittedOn.getDate()) + ' ' + + pad(submittedOn.getHours()) + ':' + pad(submittedOn.getMinutes()) + ':' + pad(submittedOn.getSeconds()) + + jsparams = [{ + TABLE_ID: row.TABLE_ID, + REVIEW_STATUS_ID: row.SUBMIT_STATUS_CD, + BASE_LIB: row.BASE_LIB, + BASE_DS: row.BASE_DS, + SUBMITTED_BY_NM: row.SUBMITTED_BY_NM, + SUBMITTED_ON: row.SUBMITTED_ON_DTTM, + SUBMITTED_REASON_TXT: row.SUBMITTED_REASON_TXT || '', + INPUT_OBS: row.INPUT_OBS, + INPUT_VARS: row.INPUT_VARS, + NUM_OF_APPROVALS_REQUIRED: row.NUM_OF_APPROVALS_REQUIRED, + NUM_OF_APPROVALS_REMAINING: row.NUM_OF_APPROVALS_REMAINING, + REVIEWED_BY_NM: row.REVIEWED_BY_NM || '', + REVIEWED_ON: row.REVIEWED_ON_DTTM, + TABLE_NM: baseTable, + BASE_TABLE: baseTable, + REVIEWED_ON_DTTM: row.REVIEWED_ON_DTTM + ? new Date(row.REVIEWED_ON_DTTM * 1000).toISOString().replace('T',' ').replace(/\.\d+Z$/, '') + : '.', + SUBMITTED_ON_DTTM: submittedOnStr, + LIB_ENGINE: 'V9', + ALLOW_RESTORE: 'NO' + }] + } + } } -` \ No newline at end of file + +_webout = JSON.stringify({ + SYSDATE: '26SEP22', SYSTIME: '08:43', jsparams, + _DEBUG: '', _METAUSER: 'sasdemo@SAS', _METAPERSON: 'sasdemo', + _PROGRAM: _program, AUTOEXEC: '', MF_GETUSER: 'sasdemo', + SYSCC: '0', SYSENCODING: 'utf-8', SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', SYSPROCESSNAME: '', + SYSJOBID: '0', SYSSCPL: 'LINUX', SYSSITE: '123', + SYSUSERID: 'sasdemo', SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', END_DTTM: new Date().toISOString(), MEMSIZE: '0KB' +}) diff --git a/sas/mocks/sasjs/services/public/getcolvals.js b/sas/mocks/sasjs/services/public/getcolvals.js index 065f513..3176cbf 100644 --- a/sas/mocks/sasjs/services/public/getcolvals.js +++ b/sas/mocks/sasjs/services/public/getcolvals.js @@ -1,449 +1,57 @@ -_webout = `{ - "SYSDATE": "26SEP22", - "SYSTIME": "08:49", - "vals": [ - { - "FORMATTED": "0", - "UNFORMATTED": 0 - }, - { - "FORMATTED": "1", - "UNFORMATTED": 1 - }, - { - "FORMATTED": "3", - "UNFORMATTED": 3 - }, - { - "FORMATTED": "4", - "UNFORMATTED": 4 - }, - { - "FORMATTED": "5", - "UNFORMATTED": 5 - }, - { - "FORMATTED": "6", - "UNFORMATTED": 6 - }, - { - "FORMATTED": "7", - "UNFORMATTED": 7 - }, - { - "FORMATTED": "8", - "UNFORMATTED": 8 - }, - { - "FORMATTED": "9", - "UNFORMATTED": 9 - }, - { - "FORMATTED": "10", - "UNFORMATTED": 10 - }, - { - "FORMATTED": "11", - "UNFORMATTED": 11 - }, - { - "FORMATTED": "12", - "UNFORMATTED": 12 - }, - { - "FORMATTED": "13", - "UNFORMATTED": 13 - }, - { - "FORMATTED": "14", - "UNFORMATTED": 14 - }, - { - "FORMATTED": "15", - "UNFORMATTED": 15 - }, - { - "FORMATTED": "16", - "UNFORMATTED": 16 - }, - { - "FORMATTED": "17", - "UNFORMATTED": 17 - }, - { - "FORMATTED": "18", - "UNFORMATTED": 18 - }, - { - "FORMATTED": "19", - "UNFORMATTED": 19 - }, - { - "FORMATTED": "20", - "UNFORMATTED": 20 - }, - { - "FORMATTED": "21", - "UNFORMATTED": 21 - }, - { - "FORMATTED": "22", - "UNFORMATTED": 22 - }, - { - "FORMATTED": "23", - "UNFORMATTED": 23 - }, - { - "FORMATTED": "24", - "UNFORMATTED": 24 - }, - { - "FORMATTED": "25", - "UNFORMATTED": 25 - }, - { - "FORMATTED": "26", - "UNFORMATTED": 26 - }, - { - "FORMATTED": "27", - "UNFORMATTED": 27 - }, - { - "FORMATTED": "28", - "UNFORMATTED": 28 - }, - { - "FORMATTED": "29", - "UNFORMATTED": 29 - }, - { - "FORMATTED": "30", - "UNFORMATTED": 30 - }, - { - "FORMATTED": "31", - "UNFORMATTED": 31 - }, - { - "FORMATTED": "32", - "UNFORMATTED": 32 - }, - { - "FORMATTED": "33", - "UNFORMATTED": 33 - }, - { - "FORMATTED": "34", - "UNFORMATTED": 34 - }, - { - "FORMATTED": "35", - "UNFORMATTED": 35 - }, - { - "FORMATTED": "36", - "UNFORMATTED": 36 - }, - { - "FORMATTED": "37", - "UNFORMATTED": 37 - }, - { - "FORMATTED": "38", - "UNFORMATTED": 38 - }, - { - "FORMATTED": "39", - "UNFORMATTED": 39 - }, - { - "FORMATTED": "40", - "UNFORMATTED": 40 - }, - { - "FORMATTED": "41", - "UNFORMATTED": 41 - }, - { - "FORMATTED": "42", - "UNFORMATTED": 42 - }, - { - "FORMATTED": "43", - "UNFORMATTED": 43 - }, - { - "FORMATTED": "44", - "UNFORMATTED": 44 - }, - { - "FORMATTED": "45", - "UNFORMATTED": 45 - }, - { - "FORMATTED": "46", - "UNFORMATTED": 46 - }, - { - "FORMATTED": "47", - "UNFORMATTED": 47 - }, - { - "FORMATTED": "48", - "UNFORMATTED": 48 - }, - { - "FORMATTED": "49", - "UNFORMATTED": 49 - }, - { - "FORMATTED": "50", - "UNFORMATTED": 50 - }, - { - "FORMATTED": "51", - "UNFORMATTED": 51 - }, - { - "FORMATTED": "52", - "UNFORMATTED": 52 - }, - { - "FORMATTED": "53", - "UNFORMATTED": 53 - }, - { - "FORMATTED": "54", - "UNFORMATTED": 54 - }, - { - "FORMATTED": "55", - "UNFORMATTED": 55 - }, - { - "FORMATTED": "56", - "UNFORMATTED": 56 - }, - { - "FORMATTED": "57", - "UNFORMATTED": 57 - }, - { - "FORMATTED": "58", - "UNFORMATTED": 58 - }, - { - "FORMATTED": "59", - "UNFORMATTED": 59 - }, - { - "FORMATTED": "60", - "UNFORMATTED": 60 - }, - { - "FORMATTED": "61", - "UNFORMATTED": 61 - }, - { - "FORMATTED": "62", - "UNFORMATTED": 62 - }, - { - "FORMATTED": "63", - "UNFORMATTED": 63 - }, - { - "FORMATTED": "64", - "UNFORMATTED": 64 - }, - { - "FORMATTED": "65", - "UNFORMATTED": 65 - }, - { - "FORMATTED": "66", - "UNFORMATTED": 66 - }, - { - "FORMATTED": "67", - "UNFORMATTED": 67 - }, - { - "FORMATTED": "68", - "UNFORMATTED": 68 - }, - { - "FORMATTED": "69", - "UNFORMATTED": 69 - }, - { - "FORMATTED": "70", - "UNFORMATTED": 70 - }, - { - "FORMATTED": "71", - "UNFORMATTED": 71 - }, - { - "FORMATTED": "72", - "UNFORMATTED": 72 - }, - { - "FORMATTED": "73", - "UNFORMATTED": 73 - }, - { - "FORMATTED": "74", - "UNFORMATTED": 74 - }, - { - "FORMATTED": "75", - "UNFORMATTED": 75 - }, - { - "FORMATTED": "76", - "UNFORMATTED": 76 - }, - { - "FORMATTED": "77", - "UNFORMATTED": 77 - }, - { - "FORMATTED": "78", - "UNFORMATTED": 78 - }, - { - "FORMATTED": "79", - "UNFORMATTED": 79 - }, - { - "FORMATTED": "80", - "UNFORMATTED": 80 - }, - { - "FORMATTED": "81", - "UNFORMATTED": 81 - }, - { - "FORMATTED": "82", - "UNFORMATTED": 82 - }, - { - "FORMATTED": "83", - "UNFORMATTED": 83 - }, - { - "FORMATTED": "84", - "UNFORMATTED": 84 - }, - { - "FORMATTED": "85", - "UNFORMATTED": 85 - }, - { - "FORMATTED": "86", - "UNFORMATTED": 86 - }, - { - "FORMATTED": "87", - "UNFORMATTED": 87 - }, - { - "FORMATTED": "88", - "UNFORMATTED": 88 - }, - { - "FORMATTED": "89", - "UNFORMATTED": 89 - }, - { - "FORMATTED": "90", - "UNFORMATTED": 90 - }, - { - "FORMATTED": "91", - "UNFORMATTED": 91 - }, - { - "FORMATTED": "92", - "UNFORMATTED": 92 - }, - { - "FORMATTED": "93", - "UNFORMATTED": 93 - }, - { - "FORMATTED": "94", - "UNFORMATTED": 94 - }, - { - "FORMATTED": "95", - "UNFORMATTED": 95 - }, - { - "FORMATTED": "96", - "UNFORMATTED": 96 - }, - { - "FORMATTED": "97", - "UNFORMATTED": 97 - }, - { - "FORMATTED": "98", - "UNFORMATTED": 98 - }, - { - "FORMATTED": "99", - "UNFORMATTED": 99 - }, - { - "FORMATTED": "100", - "UNFORMATTED": 100 +const nodePath = require('path') + +let appLoc = nodePath.join(..._program.split('services')[0].split('/')) +const sasjsRoot = nodePath.resolve(weboutPath, '..', '..', '..') +const driveRoot = nodePath.resolve(sasjsRoot, 'drive') +const dcLibref = 'DC_JSLIB' +const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) + +eval(fs.readFileSync(nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js'), 'utf8')) +const loadTableData = makeTableLoader(dataDir) + +// ─── Parse input: LIBDS format is LIBREF.TABLE.COLUMN ──────────────────────── + +const _sctRow = fetchTable('SASControlTable')[0] || {} +let libds = _sctRow.LIBDS || '' +const parts = libds.split('.') +let libref = parts[0] || dcLibref +let tableName = parts[1] || '' +let colName = parts[2] || '' + +// ─── Extract distinct values for the requested column ──────────────────────── + +let sasdata = [] +if (tableName && colName) { + const tableData = loadTableData(tableName) + if (tableData && tableData.rows) { + const lcName = colName.toLowerCase() + const seen = new Set() + for (const row of tableData.rows) { + let val = row[colName] + if (val === undefined) val = row[lcName] + if (val === undefined) { + const key = Object.keys(row).find(k => k.toLowerCase() === lcName) + if (key) val = row[key] + } + if (val !== undefined && val !== null) { + const strVal = String(val) + if (!seen.has(strVal)) { + seen.add(strVal) + sasdata.push({ val: strVal }) + } + } + } } - ], - "$vals": { - "vars": { - "FORMATTED": { - "format": "$12.", - "label": "FORMATTED", - "length": "12", - "type": "char" - }, - "UNFORMATTED": { - "format": "best.", - "label": "UNFORMATTED", - "length": "8", - "type": "num" - } - } - }, - "meta": [ - { - "COLUMN": "SOME_BESTNUM", - "SASFORMAT": "BEST." - } - ], - "_DEBUG": "", - "_METAUSER": "sasdemo@SAS", - "_METAPERSON": "sasdemo", - "_PROGRAM": "/Projects/app/dc/services/public/getcolvals", - "AUTOEXEC": "D%3A%5Copt%5Csasinside%5CConfig%5CLev1%5CSASApp%5CStoredProcessServer%5Cautoexec.sas", - "MF_GETUSER": "sasdemo", - "SYSCC": "0", - "SYSENCODING": "wlatin1", - "SYSERRORTEXT": "", - "SYSHOSTNAME": "SAS", - "SYSPROCESSID": "41DD8057C8923D7140BEF70000000000", - "SYSPROCESSMODE": "SAS Stored Process Server", - "SYSPROCESSNAME": "", - "SYSJOBID": "27448", - "SYSSCPL": "Linunx", - "SYSSITE": "123", - "SYSUSERID": "sassrv", - "SYSVLONG": "9.04.01M7P080520", - "SYSWARNINGTEXT": "ENCODING option ignored for files opened with RECFM=N.", - "END_DTTM": "2022-09-26T08:49:39.137000", - "MEMSIZE": "46GB" -}` \ No newline at end of file +} + +_webout = JSON.stringify({ + SYSDATE: '26SEP22', SYSTIME: '08:43', sasdata, + _DEBUG: '', _METAUSER: 'sasdemo@SAS', _METAPERSON: 'sasdemo', + _PROGRAM: _program, AUTOEXEC: '', MF_GETUSER: 'sasdemo', + SYSCC: '0', SYSENCODING: 'utf-8', SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', SYSPROCESSNAME: '', + SYSJOBID: '0', SYSSCPL: 'LINUX', SYSSITE: '123', + SYSUSERID: 'sasdemo', SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', END_DTTM: new Date().toISOString(), MEMSIZE: '0KB' +}) diff --git a/sas/mocks/sasjs/services/public/getgroups.js b/sas/mocks/sasjs/services/public/getgroups.js index 745fc2b..0a774f0 100644 --- a/sas/mocks/sasjs/services/public/getgroups.js +++ b/sas/mocks/sasjs/services/public/getgroups.js @@ -1,30 +1,73 @@ -_webout = `{"SYSDATE" : "06OCT22" -,"SYSTIME" : "14:43" -, "groups": -[ -{"GROUPURI":"1" ,"GROUPNAME":"AllUsers" ,"GROUPDESC":"Group contains all users" } -,{"GROUPURI":"2" ,"GROUPNAME":"Public" ,"GROUPDESC":"A special group that can be used to bypass authentication for pa" } -] -,"_DEBUG" : "" -,"_PROGRAM" : "/30.SASApps/app/mihajlo/services/public/getgroups" -,"AUTOEXEC" : "%2Fhome%2Fmihajlo%2Fsasjs_root%2Fsessions%2F20221006144330-61991-1665067410430%2Fautoexec.sas" -,"MF_GETUSER" : "mihajlo" -,"SYSCC" : "0" -,"SYSENCODING" : "utf-8" -,"SYSERRORTEXT" : "" -,"SYSHOSTINFOLONG" : "" -,"SYSHOSTNAME" : "sas.4gl.io" -,"SYSPROCESSID" : "41DD83B844F8941D0000000000000000" -,"SYSPROCESSMODE" : "Stored Program" -,"SYSPROCESSNAME" : "" -,"SYSJOBID" : "717841" -,"SYSSCPL" : "LINUX" -,"syssite" : "123" -,"SYSTCPIPHOSTNAME" : "https://sas.4gl.io:5002" -,"SYSUSERID" : "mihajlo" -,"SYSVLONG" : "05.00.00.02.001146" -,"SYSWARNINGTEXT" : "" -,"END_DTTM" : "2022-10-06T14:46:29.671882" -,"MEMSIZE" : "0KB" +const nodePath = require('path') + +let appLoc = nodePath.join(..._program.split('services')[0].split('/')) +const sasjsRoot = nodePath.resolve(weboutPath, '..', '..', '..') +const driveRoot = nodePath.resolve(sasjsRoot, 'drive') +const dcLibref = 'DC_JSLIB' +const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) + +// Helper: load a table JSON file +function loadTableData(tableName) { + try { + const file = nodePath.resolve(dataDir, tableName.toLowerCase() + '.json') + return JSON.parse(fs.readFileSync(file, {encoding:'utf8'}).toString()) + } catch(err) { return null } } -` \ No newline at end of file + +// Build groups list from MPE_GROUPS data (one row per group_name + user_name) +// The real Viya service queries the identity provider for custom groups; +// the mock derives them from the MPE_GROUPS table instead. +let groups = [] +const groupsData = loadTableData('MPE_GROUPS') +if (groupsData && groupsData.rows) { + const seen = new Set() + for (const r of groupsData.rows) { + const groupName = r.GROUP_NAME || r.group_name + const groupDesc = r.GROUP_DESC || r.group_desc + if (groupName && !seen.has(groupName)) { + seen.add(groupName) + groups.push({ + GROUPDESC: groupDesc || ' ', + GROUPURI: groupName, + GROUPNAME: groupName + }) + } + } + groups.sort((a, b) => a.GROUPNAME.localeCompare(b.GROUPNAME)) +} + +// Always include the AllUsers group so the frontend can reference it +if (!groups.find(g => g.GROUPNAME === 'AllUsers')) { + groups.unshift({ + GROUPDESC: 'Group contains all users', + GROUPURI: 'AllUsers', + GROUPNAME: 'AllUsers' + }) +} + +_webout = JSON.stringify({ + SYSDATE: '26SEP22', + SYSTIME: '08:48', + groups: groups, + _DEBUG: '', + _METAUSER: 'sasdemo@SAS', + _METAPERSON: 'sasdemo', + _PROGRAM: _program, + AUTOEXEC: '', + MF_GETUSER: 'sasdemo', + SYSCC: '0', + SYSENCODING: 'utf-8', + SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', + SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', + SYSPROCESSNAME: '', + SYSJOBID: '0', + SYSSCPL: 'LINUX', + SYSSITE: '123', + SYSUSERID: 'sasdemo', + SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', + END_DTTM: new Date().toISOString(), + MEMSIZE: '0KB' +}) diff --git a/sas/mocks/sasjs/services/public/getrawdata.js b/sas/mocks/sasjs/services/public/getrawdata.js index bafbb4f..dae6c44 100644 --- a/sas/mocks/sasjs/services/public/getrawdata.js +++ b/sas/mocks/sasjs/services/public/getrawdata.js @@ -1,530 +1,122 @@ -let mimeType = `application/octet-stream` -let filename = `dc-download` +const nodePath = require('path') -if (table) filename = `${table}` +let appLoc = nodePath.join(..._program.split('services')[0].split('/')) +const sasjsRoot = nodePath.resolve(weboutPath, '..', '..', '..') +const driveRoot = nodePath.resolve(sasjsRoot, 'drive') +const dcLibref = 'DC_JSLIB' +const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) -switch(type) { - case 'CSV': { +// Helper: load a table JSON file +function loadTableData(tableName) { + try { + const file = nodePath.resolve(dataDir, tableName.toLowerCase() + '.json') + return JSON.parse(fs.readFileSync(file, {encoding:'utf8'}).toString()) + } catch(err) { return null } +} + +// SAS epoch for date/datetime/time formatting +const SAS_EPOCH_MS = Date.UTC(1960, 0, 1) +const MONTHS = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'] + +function formatSasValue(value, ddtype) { + if (value === null || value === undefined || value === '') return '' + if (typeof value !== 'number') return String(value) + if (ddtype === 'DATE') { + const d = new Date(SAS_EPOCH_MS + value * 86400000) + return `${d.getUTCDate()}${MONTHS[d.getUTCMonth()]}${d.getUTCFullYear()}` + } + if (ddtype === 'DATETIME') { + const d = new Date(SAS_EPOCH_MS + value * 1000) + return `${d.getUTCDate()}${MONTHS[d.getUTCMonth()]}${d.getUTCFullYear()}:${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}:${String(d.getUTCSeconds()).padStart(2,'0')}` + } + if (ddtype === 'TIME') { + const h = Math.floor(value / 3600) + const m = Math.floor((value % 3600) / 60) + const s = value % 60 + return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}` + } + return String(value) +} + +// Determine mime type and filename from request params +let mimeType = 'application/octet-stream' +let filename = table || 'dc-download' + +switch((type || '').toUpperCase()) { + case 'CSV': mimeType = 'text/csv' filename += '.csv' break - } - case 'EXCEL': { + case 'EXCEL': mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' filename += '.xlsx' break - } - case 'MARKDOWN': { + case 'MARKDOWN': mimeType = 'application/octet-stream' filename += '.md' break - } default: mimeType = 'application/octet-stream' filename += '.sas' break } -fs.writeFile(_SASJS_WEBOUT_HEADERS, `Content-type: ${mimeType} \n Content-Disposition: attachment;filename=\"${filename}\"`, function (err) { - if (err) throw err; -}) +// Write headers +fs.writeFile(_SASJS_WEBOUT_HEADERS, + `Content-type: ${mimeType} \n Content-Disposition: attachment;filename="${filename}"`, + function (err) { if (err) throw err } +) -_webout=` -2,even more dummy data,Option/* */ 3,42,42,42,142,3,44 -3,"It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:",Option 2,1613.001,423,423,44,3,44 -4,if you can fill the unforgiving minute,Option 1,1613.001123456,4231,423123123,412,3,44 -1010,10 bottles of beer on the wall,Option 1,0.36778671125312,429,29804,35,1,72 -1011,11 bottles of beer on the wall,Option 1,0.86933304968724,385,5119,1,6,54 -1012,12 bottles of beer on the wall,Option 1,0.54327790650691,644,10655,35,54,62 -1013,13 bottles of beer on the wall,Option 1,0.50519398669954,781,24475,41,38,4 -1014,14 bottles of beer on the wall,Option 1,0.0130502507151339,12,13693,14,92,57 -1015,15 bottles of beer on the wall,Option 1,0.58227080087283,923,43518,54,92,80 -1016,16 bottles of beer on the wall,Option 1,0.13827249786736,241,10081,1,28,91 -1017,17 bottles of beer on the wall,Option 1,0.89270132402549,622,25438,97,91,72 -1018,18 bottles of beer on the wall,Option 1,0.18527885674744,432,1368,32,93,79 -1019,19 bottles of beer on the wall,Option 1,0.07375510180078,389,11673,21,22,90 -1020,20 bottles of beer on the wall,Option 1,0.71285699387679,404,6623,100,65,34 -1021,21 bottles of beer on the wall,Option 1,0.67061384425992,433,17575,13,44,97 -1022,22 bottles of beer on the wall,Option 1,0.14232157922457,933,26701,70,66,98 -1023,23 bottles of beer on the wall,Option 1,0.12598480662609,974,34354,76,44,98 -1024,24 bottles of beer on the wall,Option 1,0.38994686370247,705,24831,33,30,90 -1025,25 bottles of beer on the wall,Option 1,0.0310356193366626,60,10687,27,73,59 -1026,26 bottles of beer on the wall,Option 1,0.90578842391529,277,40648,41,82,46 -1027,27 bottles of beer on the wall,Option 1,0.59206758560243,926,12941,22,46,73 -1028,28 bottles of beer on the wall,Option 1,0.65800300457421,281,47610,40,35,40 -1029,29 bottles of beer on the wall,Option 1,0.80704259397789,360,43034,19,80,12 -1030,30 bottles of beer on the wall,Option 1,0.88014504075057,500,36665,25,70,19 -1031,31 bottles of beer on the wall,Option 1,0.41501947046025,757,41229,64,94,48 -1032,32 bottles of beer on the wall,Option 1,0.9743401203185,739,27875,67,43,3 -1033,33 bottles of beer on the wall,Option 1,0.20355956917794,250,42739,42,29,56 -1034,34 bottles of beer on the wall,Option 1,0.67924355560878,111,26224,74,68,9 -1035,35 bottles of beer on the wall,Option 1,0.9494116971965,18,36938,76,91,10 -1036,36 bottles of beer on the wall,Option 1,0.54461349106608,299,14127,84,72,36 -1037,37 bottles of beer on the wall,Option 1,0.45877589399869,325,48877,95,97,32 -1038,38 bottles of beer on the wall,Option 1,0.15371942387601,491,22453,29,60,98 -1039,39 bottles of beer on the wall,Option 1,0.4935002562094,156,25182,45,95,55 -1040,40 bottles of beer on the wall,Option 1,0.12472885899465,433,10986,83,35,79 -1041,41 bottles of beer on the wall,Option 1,0.27944220010165,917,19766,51,86,66 -1042,42 bottles of beer on the wall,Option 1,0.70307754990788,223,43884,38,86,97 -1043,43 bottles of beer on the wall,Option 1,0.0701107536769,394,13449,3,25,8 -1044,44 bottles of beer on the wall,Option 1,0.64232929267097,745,3427,9,97,37 -1045,45 bottles of beer on the wall,Option 1,0.72064477425098,652,44732,7,58,58 -1046,46 bottles of beer on the wall,Option 1,0.0431997366450726,255,18777,95,17,8 -1047,47 bottles of beer on the wall,Option 1,0.3704071367953,182,9877,6,45,26 -1048,48 bottles of beer on the wall,Option 1,0.65441703500897,489,4987,98,41,13 -1049,49 bottles of beer on the wall,Option 1,0.13002125645523,371,19649,81,37,66 -1050,50 bottles of beer on the wall,Option 1,0.0058409725343068,204,264,40,15,32 -1051,51 bottles of beer on the wall,Option 1,0.7239382587019,160,11709,4,42,82 -1052,52 bottles of beer on the wall,Option 1,0.83190037116031,225,27515,36,69,81 -1053,53 bottles of beer on the wall,Option 1,0.50308288750382,538,41129,53,39,75 -1054,54 bottles of beer on the wall,Option 1,0.71480455143135,238,36609,39,6,4 -1055,55 bottles of beer on the wall,Option 1,0.85579457872351,292,8252,8,93,36 -1056,56 bottles of beer on the wall,Option 1,0.97004633069506,922,40721,51,25,35 -1057,57 bottles of beer on the wall,Option 1,0.93803994261568,542,47713,52,57,66 -1058,58 bottles of beer on the wall,Option 1,0.84844994863888,153,4491,0,80,58 -1059,59 bottles of beer on the wall,Option 1,0.14157076279705,574,23596,58,11,32 -1060,60 bottles of beer on the wall,Option 1,0.28267451295753,816,1537,56,79,58 -1061,61 bottles of beer on the wall,Option 1,0.37272800801914,734,18463,60,86,92 -1062,62 bottles of beer on the wall,Option 1,0.95173373164224,606,9605,5,30,93 -1063,63 bottles of beer on the wall,Option 1,0.09674986828898,778,27041,29,90,82 -1064,64 bottles of beer on the wall,Option 1,0.054067135348016,511,47623,8,88,45 -1065,65 bottles of beer on the wall,Option 1,0.64616364643264,757,10601,19,41,38 -1066,66 bottles of beer on the wall,Option 1,0.9053011983192,275,12949,64,68,39 -1067,67 bottles of beer on the wall,Option 1,0.97752588101547,930,21200,28,28,34 -1068,68 bottles of beer on the wall,Option 1,0.21655531610201,133,6242,72,63,23 -1069,69 bottles of beer on the wall,Option 1,0.22483527950236,494,273,9,26,93 -1070,70 bottles of beer on the wall,Option 1,0.13862833666551,868,12720,96,83,89 -1071,71 bottles of beer on the wall,Option 1,0.93373314148454,501,49614,47,27,56 -1072,72 bottles of beer on the wall,Option 1,0.035223550645273,522,32960,76,7,27 -1073,73 bottles of beer on the wall,Option 1,0.32066626954854,72,38291,68,3,50 -1074,74 bottles of beer on the wall,Option 1,0.46108617049692,608,34541,68,54,68 -1075,75 bottles of beer on the wall,Option 1,0.45277456215246,746,24567,45,96,63 -1076,76 bottles of beer on the wall,Option 1,0.35812440577807,136,3400,73,72,24 -1077,77 bottles of beer on the wall,Option 1,0.89399213338922,386,33391,75,88,69 -1078,78 bottles of beer on the wall,Option 1,0.24457270663444,356,11474,97,88,32 -1079,79 bottles of beer on the wall,Option 1,0.96830294652297,591,17143,69,51,60 -1080,80 bottles of beer on the wall,Option 1,0.13035413675492,789,8090,21,79,87 -1081,81 bottles of beer on the wall,Option 1,0.76569796528932,580,24590,91,58,30 -1082,82 bottles of beer on the wall,Option 1,0.18556296740917,350,23241,33,1,72 -1083,83 bottles of beer on the wall,Option 1,0.47821786416611,471,29123,70,0,1 -1084,84 bottles of beer on the wall,Option 1,0.16702721322282,902,49400,27,53,6 -1085,85 bottles of beer on the wall,Option 1,0.60682491893266,141,39911,8,17,68 -1086,86 bottles of beer on the wall,Option 1,0.09360499172173,931,26169,46,73,37 -1087,87 bottles of beer on the wall,Option 1,0.65382491781088,114,7614,59,95,32 -1088,88 bottles of beer on the wall,Option 1,0.88461585616907,688,20127,61,87,30 -1089,89 bottles of beer on the wall,Option 1,0.15782083159211,427,32522,23,60,53 -1090,90 bottles of beer on the wall,Option 1,0.42257537526198,78,44044,60,57,64 -1091,91 bottles of beer on the wall,Option 1,0.65989433539095,625,10993,60,41,28 -1092,92 bottles of beer on the wall,Option 1,0.62935016892354,656,1273,71,64,7 -1093,93 bottles of beer on the wall,Option 1,0.43788449859148,540,37239,27,30,78 -1094,94 bottles of beer on the wall,Option 1,0.9838584968745,875,10746,59,48,98 -1095,95 bottles of beer on the wall,Option 1,0.08925237697048,532,17660,10,75,33 -1096,96 bottles of beer on the wall,Option 1,0.45782051536153,19,38160,41,14,17 -1097,97 bottles of beer on the wall,Option 1,0.58632715865332,840,40451,88,66,84 -1098,98 bottles of beer on the wall,Option 1,0.29942320580567,185,29741,88,99,85 -1099,99 bottles of beer on the wall,Option 1,0.09813780528406,35,40211,43,23,65 -10100,100 bottles of beer on the wall,Option 1,0.98297226521324,31,20706,76,28,44 -10101,101 bottles of beer on the wall,Option 1,0.45407949129775,945,34023,70,42,44 -10102,102 bottles of beer on the wall,Option 1,0.84521743694563,275,11327,23,10,14 -10103,103 bottles of beer on the wall,Option 1,0.59049196056578,211,47398,10,68,53 -10104,104 bottles of beer on the wall,Option 1,0.70833886773713,205,20990,89,84,33 -10105,105 bottles of beer on the wall,Option 1,0.31835502587182,622,8914,51,69,97 -10106,106 bottles of beer on the wall,Option 1,0.97297559723862,585,16241,10,91,29 -10107,107 bottles of beer on the wall,Option 1,0.5090773215094,266,19231,43,92,73 -10108,108 bottles of beer on the wall,Option 1,0.60501769259805,442,8188,35,88,21 -10109,109 bottles of beer on the wall,Option 1,0.36126818152203,732,39786,39,67,1 -10110,110 bottles of beer on the wall,Option 1,0.92257953710974,181,27439,53,45,96 -10111,111 bottles of beer on the wall,Option 1,0.57620774422595,1,10732,13,85,94 -10112,112 bottles of beer on the wall,Option 1,0.72213579794491,368,24381,11,81,4 -10113,113 bottles of beer on the wall,Option 1,0.68396092331221,876,7786,88,88,35 -10114,114 bottles of beer on the wall,Option 1,0.06275422035844,242,39301,65,37,30 -10115,115 bottles of beer on the wall,Option 1,0.14623936319082,763,31112,32,26,92 -10116,116 bottles of beer on the wall,Option 1,0.88700955448998,461,29947,67,93,100 -10117,117 bottles of beer on the wall,Option 1,0.58625199812755,772,33489,84,17,65 -10118,118 bottles of beer on the wall,Option 1,0.11950912844366,88,18030,83,74,91 -10119,119 bottles of beer on the wall,Option 1,0.25274958287028,73,40520,3,35,48 -10120,120 bottles of beer on the wall,Option 1,0.20096259713217,322,37079,25,72,46 -10121,121 bottles of beer on the wall,Option 1,0.51715718140693,696,35623,70,2,29 -10122,122 bottles of beer on the wall,Option 1,0.62535265396598,349,18863,77,55,42 -10123,123 bottles of beer on the wall,Option 1,0.0397578571176892,616,2222,57,21,21 -10124,124 bottles of beer on the wall,Option 1,0.15299692198308,781,31566,49,77,36 -10125,125 bottles of beer on the wall,Option 1,0.55944163518,848,18125,98,89,81 -10126,126 bottles of beer on the wall,Option 1,0.0545679247260876,102,12528,93,78,97 -10127,127 bottles of beer on the wall,Option 1,0.37134892836741,650,8167,52,54,88 -10128,128 bottles of beer on the wall,Option 1,0.64729823528197,93,1934,51,6,19 -10129,129 bottles of beer on the wall,Option 1,0.83002288491931,4,49511,33,81,10 -10130,130 bottles of beer on the wall,Option 1,0.26566079038458,556,19220,44,64,96 -10131,131 bottles of beer on the wall,Option 1,0.3515388878768,465,9268,65,33,47 -10132,132 bottles of beer on the wall,Option 1,0.58481269170754,369,3487,9,27,23 -10133,133 bottles of beer on the wall,Option 1,0.35976610256348,821,16327,12,6,79 -10134,134 bottles of beer on the wall,Option 1,0.66825788732071,692,43769,43,39,24 -10135,135 bottles of beer on the wall,Option 1,0.6738104655751,506,29856,85,25,81 -10136,136 bottles of beer on the wall,Option 1,0.61094559291887,719,22506,35,54,62 -10137,137 bottles of beer on the wall,Option 1,0.90374875157314,72,5697,78,47,95 -10138,138 bottles of beer on the wall,Option 1,0.80794565184411,642,42356,27,12,85 -10139,139 bottles of beer on the wall,Option 1,0.38307386654572,96,27737,91,35,33 -10140,140 bottles of beer on the wall,Option 1,0.56504530905049,50,436,92,79,7 -10141,141 bottles of beer on the wall,Option 1,0.88745929388676,790,2062,19,22,14 -10142,142 bottles of beer on the wall,Option 1,0.40486131394508,401,39461,31,67,8 -10143,143 bottles of beer on the wall,Option 1,0.46010804244322,141,38118,13,5,56 -10144,144 bottles of beer on the wall,Option 1,0.80973751089057,391,29194,40,99,51 -10145,145 bottles of beer on the wall,Option 1,0.99359150975644,439,46193,72,64,36 -10146,146 bottles of beer on the wall,Option 1,0.0452116918960641,118,22701,24,60,96 -10147,147 bottles of beer on the wall,Option 1,0.82811712977854,260,12725,17,62,66 -10148,148 bottles of beer on the wall,Option 1,0.10816729632586,938,26840,50,84,81 -10149,149 bottles of beer on the wall,Option 1,0.38052640686767,683,3221,86,85,46 -10150,150 bottles of beer on the wall,Option 1,0.75572601973811,982,19125,32,95,95 -10151,151 bottles of beer on the wall,Option 1,0.14433648304284,978,10712,67,60,46 -10152,152 bottles of beer on the wall,Option 1,0.0478734295106835,195,20803,16,11,31 -10153,153 bottles of beer on the wall,Option 1,0.99183906660966,846,17321,72,56,75 -10154,154 bottles of beer on the wall,Option 1,0.9159638098981,247,6539,24,83,15 -10155,155 bottles of beer on the wall,Option 1,0.36824048886459,754,32386,14,80,100 -10156,156 bottles of beer on the wall,Option 1,0.26111941517382,730,2622,30,83,10 -10157,157 bottles of beer on the wall,Option 1,0.88903899951327,332,11975,96,11,35 -10158,158 bottles of beer on the wall,Option 1,0.13194132788662,604,26809,16,51,85 -10159,159 bottles of beer on the wall,Option 1,0.62617524323341,174,9160,21,48,61 -10160,160 bottles of beer on the wall,Option 1,0.57026278207556,696,43887,57,83,1 -10161,161 bottles of beer on the wall,Option 1,0.85756031324042,271,49234,27,2,27 -10162,162 bottles of beer on the wall,Option 1,0.1826881850989,47,10724,78,2,97 -10163,163 bottles of beer on the wall,Option 1,0.23421087266607,482,10522,88,20,48 -10164,164 bottles of beer on the wall,Option 1,0.35695342596478,161,38915,54,32,10 -10165,165 bottles of beer on the wall,Option 1,0.91366467481184,378,38006,70,63,6 -10166,166 bottles of beer on the wall,Option 1,0.99905551783696,818,43080,35,10,36 -10167,167 bottles of beer on the wall,Option 1,0.52259193245442,62,47314,77,73,92 -10168,168 bottles of beer on the wall,Option 1,0.37612532376131,455,10513,79,37,41 -10169,169 bottles of beer on the wall,Option 1,0.32677267646732,900,43022,33,56,4 -10170,170 bottles of beer on the wall,Option 1,0.78282068659682,584,35703,11,96,19 -10171,171 bottles of beer on the wall,Option 1,0.86384059622131,383,16268,72,97,52 -10172,172 bottles of beer on the wall,Option 1,0.78971071298686,274,40013,11,12,77 -10173,173 bottles of beer on the wall,Option 1,0.80251133851823,141,24834,3,24,52 -10174,174 bottles of beer on the wall,Option 1,0.75300996366562,391,25598,69,10,50 -10175,175 bottles of beer on the wall,Option 1,0.71632067054338,957,17312,75,80,50 -10176,176 bottles of beer on the wall,Option 1,0.79148860219469,146,43778,51,4,35 -10177,177 bottles of beer on the wall,Option 1,0.09253642060446,108,5706,3,54,99 -10178,178 bottles of beer on the wall,Option 1,0.11753227706883,629,20998,30,9,4 -10179,179 bottles of beer on the wall,Option 1,0.7680506472327,480,16698,64,29,40 -10180,180 bottles of beer on the wall,Option 1,0.72647327497902,3,692,98,26,63 -10181,181 bottles of beer on the wall,Option 1,0.61187958652706,804,15515,35,74,9 -10182,182 bottles of beer on the wall,Option 1,0.37813100422645,947,17165,45,40,70 -10183,183 bottles of beer on the wall,Option 1,0.77219476074548,333,18572,47,75,13 -10184,184 bottles of beer on the wall,Option 1,0.08129415338919,544,41948,73,69,42 -10185,185 bottles of beer on the wall,Option 1,0.31256898320865,788,40399,79,46,81 -10186,186 bottles of beer on the wall,Option 1,0.17449143863026,792,48899,82,75,11 -10187,187 bottles of beer on the wall,Option 1,0.96775995286542,287,49710,56,53,48 -10188,188 bottles of beer on the wall,Option 1,0.89112877840694,64,19729,89,53,7 -10189,189 bottles of beer on the wall,Option 1,0.06672863013424,76,28616,10,62,87 -10190,190 bottles of beer on the wall,Option 1,0.94082600806878,147,42133,34,45,53 -10191,191 bottles of beer on the wall,Option 1,0.31033688146171,836,30910,16,47,17 -10192,192 bottles of beer on the wall,Option 1,0.18763532265445,337,40521,3,51,83 -10193,193 bottles of beer on the wall,Option 1,0.96240949442722,291,15471,49,39,59 -10194,194 bottles of beer on the wall,Option 1,0.8183761731807,419,40108,43,39,67 -10195,195 bottles of beer on the wall,Option 1,0.24989856139286,670,15344,92,43,42 -10196,196 bottles of beer on the wall,Option 1,0.30300405309675,389,23570,23,54,49 -10197,197 bottles of beer on the wall,Option 1,0.72431187039441,253,33148,8,44,61 -10198,198 bottles of beer on the wall,Option 1,0.28154017463398,990,5590,35,70,58 -10199,199 bottles of beer on the wall,Option 1,0.91922048801519,128,34408,69,54,0 -10200,200 bottles of beer on the wall,Option 1,0.50593937258512,107,18850,78,83,97 -10201,201 bottles of beer on the wall,Option 1,0.14390773612256,946,17067,64,26,8 -10202,202 bottles of beer on the wall,Option 1,0.22146828529493,610,24559,46,49,59 -10203,203 bottles of beer on the wall,Option 1,0.25821849576114,663,34629,1,6,71 -10204,204 bottles of beer on the wall,Option 1,0.07666777590134,466,27138,26,99,30 -10205,205 bottles of beer on the wall,Option 1,0.99336800817091,694,16168,65,11,46 -10206,206 bottles of beer on the wall,Option 1,0.87276060547342,576,25229,80,91,90 -10207,207 bottles of beer on the wall,Option 1,0.78940974864615,6,47096,26,63,27 -10208,208 bottles of beer on the wall,Option 1,0.34292900066027,14,31458,36,22,82 -10209,209 bottles of beer on the wall,Option 1,0.85305621747535,993,2251,86,89,14 -10210,210 bottles of beer on the wall,Option 1,0.15697739560016,198,45644,46,91,4 -10211,211 bottles of beer on the wall,Option 1,0.28367675109006,906,13627,42,90,59 -10212,212 bottles of beer on the wall,Option 1,0.31982582077375,378,7369,78,38,8 -10213,213 bottles of beer on the wall,Option 1,0.23638620564545,648,44244,93,90,55 -10214,214 bottles of beer on the wall,Option 1,0.74210127105102,24,20396,94,44,25 -10215,215 bottles of beer on the wall,Option 1,0.86374284972611,74,48658,100,86,85 -10216,216 bottles of beer on the wall,Option 1,0.75050161255081,59,46121,89,20,42 -10217,217 bottles of beer on the wall,Option 1,0.66522030609902,994,42468,11,56,86 -10218,218 bottles of beer on the wall,Option 1,0.31411136375465,655,27263,65,25,96 -10219,219 bottles of beer on the wall,Option 1,0.47560207800734,499,48744,43,95,39 -10220,220 bottles of beer on the wall,Option 1,0.29217880745054,499,11765,39,38,34 -10221,221 bottles of beer on the wall,Option 1,0.34208696444616,782,48914,46,28,67 -10222,222 bottles of beer on the wall,Option 1,0.3618709479281,17,35101,36,32,96 -10223,223 bottles of beer on the wall,Option 1,0.57169641674109,255,39862,46,25,32 -10224,224 bottles of beer on the wall,Option 1,0.81918335930406,52,678,48,93,9 -10225,225 bottles of beer on the wall,Option 1,0.0242283884548714,85,42068,83,16,80 -10226,226 bottles of beer on the wall,Option 1,0.41845829338694,302,24667,57,21,94 -10227,227 bottles of beer on the wall,Option 1,0.85522656275668,25,4962,39,16,92 -10228,228 bottles of beer on the wall,Option 1,0.61243204661292,211,29623,51,3,47 -10229,229 bottles of beer on the wall,Option 1,0.0565994717444291,895,43578,3,89,43 -10230,230 bottles of beer on the wall,Option 1,0.23499503882368,490,4308,62,37,48 -10231,231 bottles of beer on the wall,Option 1,0.90549826105381,400,6623,89,33,46 -10232,232 bottles of beer on the wall,Option 1,0.48989009600593,744,25625,56,51,95 -10233,233 bottles of beer on the wall,Option 1,0.45866525287677,218,44935,69,85,54 -10234,234 bottles of beer on the wall,Option 1,0.85541833977001,639,24786,23,38,89 -10235,235 bottles of beer on the wall,Option 1,0.85759922622591,668,22410,58,67,99 -10236,236 bottles of beer on the wall,Option 1,0.84442021411118,101,1665,82,65,33 -10237,237 bottles of beer on the wall,Option 1,0.47433742809777,378,24480,58,22,29 -10238,238 bottles of beer on the wall,Option 1,0.32656197777323,516,43954,3,94,67 -10239,239 bottles of beer on the wall,Option 1,0.92182368269275,712,4843,71,46,48 -10240,240 bottles of beer on the wall,Option 1,0.12125048326386,352,9277,22,2,32 -10241,241 bottles of beer on the wall,Option 1,0.94974516935169,523,24517,34,3,67 -10242,242 bottles of beer on the wall,Option 1,0.25598201866074,802,27462,64,76,70 -10243,243 bottles of beer on the wall,Option 1,0.75983338978133,179,29989,61,29,71 -10244,244 bottles of beer on the wall,Option 1,0.70059782718336,205,18615,38,53,10 -10245,245 bottles of beer on the wall,Option 1,0.22119579474497,248,32376,67,44,33 -10246,246 bottles of beer on the wall,Option 1,0.20074144573916,83,8942,9,37,88 -10247,247 bottles of beer on the wall,Option 1,0.28749029351747,570,10557,57,30,33 -10248,248 bottles of beer on the wall,Option 1,0.56116712678278,176,7262,96,49,90 -10249,249 bottles of beer on the wall,Option 1,0.34936685829859,424,16012,95,81,25 -10250,250 bottles of beer on the wall,Option 1,0.2531839875752,400,13060,93,38,32 -10251,251 bottles of beer on the wall,Option 1,0.74716722860334,104,36045,95,12,91 -10252,252 bottles of beer on the wall,Option 1,0.2776136003796,621,32945,24,43,56 -10253,253 bottles of beer on the wall,Option 1,0.10966447093974,824,1582,82,47,71 -10254,254 bottles of beer on the wall,Option 1,0.20580046819793,515,48029,52,84,12 -10255,255 bottles of beer on the wall,Option 1,0.25379118707673,528,20603,24,50,93 -10256,256 bottles of beer on the wall,Option 1,0.91665374763154,340,33728,92,65,18 -10257,257 bottles of beer on the wall,Option 1,0.76795360016075,986,2750,46,80,97 -10258,258 bottles of beer on the wall,Option 1,0.54184350210327,343,36848,56,32,90 -10259,259 bottles of beer on the wall,Option 1,0.32811180051793,455,28710,88,44,88 -10260,260 bottles of beer on the wall,Option 1,0.21353805913288,312,29544,13,52,11 -10261,261 bottles of beer on the wall,Option 1,0.9443434551099,512,8527,20,62,6 -10262,262 bottles of beer on the wall,Option 1,0.85606485691669,897,5860,30,57,8 -10263,263 bottles of beer on the wall,Option 1,0.99083470878695,807,13039,86,67,60 -10264,264 bottles of beer on the wall,Option 1,0.81836992586886,962,4598,39,30,45 -10265,265 bottles of beer on the wall,Option 1,0.42497428805798,61,6536,89,38,44 -10266,266 bottles of beer on the wall,Option 1,0.0560880252421312,251,6156,80,39,13 -10267,267 bottles of beer on the wall,Option 1,0.62838333175908,976,41904,69,80,5 -10268,268 bottles of beer on the wall,Option 1,0.86274688498244,801,38326,57,37,67 -10269,269 bottles of beer on the wall,Option 1,0.90785965924517,430,4760,53,46,12 -10270,270 bottles of beer on the wall,Option 1,0.96423306221339,881,13359,26,76,85 -10271,271 bottles of beer on the wall,Option 1,0.4007478316318,386,9372,92,46,37 -10272,272 bottles of beer on the wall,Option 1,0.1693538530587,770,30938,83,37,54 -10273,273 bottles of beer on the wall,Option 1,0.31195034380627,685,24382,100,69,19 -10274,274 bottles of beer on the wall,Option 1,0.76835356129722,187,47688,55,89,64 -10275,275 bottles of beer on the wall,Option 1,0.55031902461793,584,3730,14,0,81 -10276,276 bottles of beer on the wall,Option 1,0.95770672334251,363,3953,24,30,88 -10277,277 bottles of beer on the wall,Option 1,0.6728389773857,420,8182,39,53,83 -10278,278 bottles of beer on the wall,Option 1,0.89363865595946,704,46747,31,74,16 -10279,279 bottles of beer on the wall,Option 1,0.40898148175747,924,47734,97,7,24 -10280,280 bottles of beer on the wall,Option 1,0.66577262182988,64,20201,32,44,54 -10281,281 bottles of beer on the wall,Option 1,0.51660140814101,280,12454,79,10,71 -10282,282 bottles of beer on the wall,Option 1,0.53854070070131,104,43120,90,44,24 -10283,283 bottles of beer on the wall,Option 1,0.23819732723673,558,15573,20,6,23 -10284,284 bottles of beer on the wall,Option 1,0.47946875704427,243,36842,53,32,21 -10285,285 bottles of beer on the wall,Option 1,0.77190031845676,651,28782,56,39,60 -10286,286 bottles of beer on the wall,Option 1,0.32027244256822,383,42285,47,100,10 -10287,287 bottles of beer on the wall,Option 1,0.74313893157203,31,16412,3,74,65 -10288,288 bottles of beer on the wall,Option 1,0.76955895022002,602,446,96,3,76 -10289,289 bottles of beer on the wall,Option 1,0.34185171050105,952,13939,62,5,36 -10290,290 bottles of beer on the wall,Option 1,0.56766681958346,767,45844,22,63,94 -10291,291 bottles of beer on the wall,Option 1,0.08876820145583,35,44015,18,67,75 -10292,292 bottles of beer on the wall,Option 1,0.60991968662008,537,25458,62,97,31 -10293,293 bottles of beer on the wall,Option 1,0.49679509200937,425,2032,71,57,28 -10294,294 bottles of beer on the wall,Option 1,0.75629847392267,128,18192,74,4,18 -10295,295 bottles of beer on the wall,Option 1,0.68437125379423,823,49500,26,26,27 -10296,296 bottles of beer on the wall,Option 1,0.93925402124377,544,41748,5,35,80 -10297,297 bottles of beer on the wall,Option 1,0.32460635729348,55,9662,69,4,43 -10298,298 bottles of beer on the wall,Option 1,0.69933199170014,168,2056,58,37,59 -10299,299 bottles of beer on the wall,Option 1,0.47544649638023,840,30210,53,53,68 -10300,300 bottles of beer on the wall,Option 1,0.0061305039590832,271,12274,76,89,63 -10301,301 bottles of beer on the wall,Option 1,0.48602948453558,62,326,17,10,61 -10302,302 bottles of beer on the wall,Option 1,0.66281101091895,85,25075,34,20,0 -10303,303 bottles of beer on the wall,Option 1,0.91012760247575,766,2167,24,10,59 -10304,304 bottles of beer on the wall,Option 1,0.25942224276225,900,10747,64,56,26 -10305,305 bottles of beer on the wall,Option 1,0.1962652361003,289,96,17,33,93 -10306,306 bottles of beer on the wall,Option 1,0.43586940478341,29,13418,16,53,29 -10307,307 bottles of beer on the wall,Option 1,0.59681117515862,118,27786,86,65,36 -10308,308 bottles of beer on the wall,Option 1,0.74818741471887,206,4604,24,24,25 -10309,309 bottles of beer on the wall,Option 1,0.08160290591493,312,21554,64,63,72 -10310,310 bottles of beer on the wall,Option 1,0.93582592389351,242,10979,27,81,61 -10311,311 bottles of beer on the wall,Option 1,0.80891675353465,209,5348,87,54,90 -10312,312 bottles of beer on the wall,Option 1,0.60018050931402,439,38290,61,90,76 -10313,313 bottles of beer on the wall,Option 1,0.24727975961159,881,20239,40,51,51 -10314,314 bottles of beer on the wall,Option 1,0.38803865312972,653,24155,89,31,85 -10315,315 bottles of beer on the wall,Option 1,0.87407688278429,313,37785,77,92,16 -10316,316 bottles of beer on the wall,Option 1,0.56551623696718,547,21387,24,23,97 -10317,317 bottles of beer on the wall,Option 1,0.76403702691385,58,9872,38,64,21 -10318,318 bottles of beer on the wall,Option 1,0.90192810953684,602,11179,10,90,11 -10319,319 bottles of beer on the wall,Option 1,0.19516248544452,214,4047,63,28,92 -10320,320 bottles of beer on the wall,Option 1,0.94210496449009,873,18179,99,68,3 -10321,321 bottles of beer on the wall,Option 1,0.38397014298661,768,6961,34,4,75 -10322,322 bottles of beer on the wall,Option 1,0.48192969592378,241,40181,42,3,47 -10323,323 bottles of beer on the wall,Option 1,0.9110809797007,103,40820,33,3,68 -10324,324 bottles of beer on the wall,Option 1,0.87276527372783,826,32198,4,39,97 -10325,325 bottles of beer on the wall,Option 1,0.79484101282192,372,7572,44,96,94 -10326,326 bottles of beer on the wall,Option 1,0.14490672719893,292,7727,52,59,76 -10327,327 bottles of beer on the wall,Option 1,0.39760627709217,61,39145,43,84,64 -10328,328 bottles of beer on the wall,Option 1,0.59372476655697,986,45366,56,32,42 -10329,329 bottles of beer on the wall,Option 1,0.84270271325609,730,48646,83,33,93 -10330,330 bottles of beer on the wall,Option 1,0.97886023948847,579,12493,3,47,51 -10331,331 bottles of beer on the wall,Option 1,0.06287532628647,13,33428,15,95,25 -10332,332 bottles of beer on the wall,Option 1,0.0144214248351853,986,14183,40,42,55 -10333,333 bottles of beer on the wall,Option 1,0.46513955689274,279,24669,74,6,96 -10334,334 bottles of beer on the wall,Option 1,0.65515650233959,940,7983,58,90,50 -10335,335 bottles of beer on the wall,Option 1,0.74576339346625,40,32933,42,66,64 -10336,336 bottles of beer on the wall,Option 1,0.25071058620266,249,35506,55,38,21 -10337,337 bottles of beer on the wall,Option 1,0.83827515358025,901,40729,55,96,40 -10338,338 bottles of beer on the wall,Option 1,0.37835469487046,787,39991,85,24,78 -10339,339 bottles of beer on the wall,Option 1,0.47124984649533,325,9386,13,46,83 -10340,340 bottles of beer on the wall,Option 1,0.81143325092803,276,21424,20,35,49 -10341,341 bottles of beer on the wall,Option 1,0.18923743590211,278,33884,35,66,56 -10342,342 bottles of beer on the wall,Option 1,0.31555957641245,652,3104,43,41,96 -10343,343 bottles of beer on the wall,Option 1,0.63393646135643,787,7242,50,79,80 -10344,344 bottles of beer on the wall,Option 1,0.42426411873859,900,7126,3,47,55 -10345,345 bottles of beer on the wall,Option 1,0.46336023717343,202,15278,8,27,22 -10346,346 bottles of beer on the wall,Option 1,0.74511298199422,941,9263,62,12,8 -10347,347 bottles of beer on the wall,Option 1,0.0509991273521442,175,17983,84,69,63 -10348,348 bottles of beer on the wall,Option 1,0.73515439673101,104,44565,39,89,43 -10349,349 bottles of beer on the wall,Option 1,0.54621437869324,419,43815,82,14,44 -10350,350 bottles of beer on the wall,Option 1,0.67394142862127,565,15879,83,78,24 -10351,351 bottles of beer on the wall,Option 1,0.14206720569267,724,48097,47,78,59 -10352,352 bottles of beer on the wall,Option 1,0.77307720937443,542,8575,12,59,56 -10353,353 bottles of beer on the wall,Option 1,0.30727765350941,969,2407,60,24,16 -10354,354 bottles of beer on the wall,Option 1,0.0010556476195602,556,26857,10,10,14 -10355,355 bottles of beer on the wall,Option 1,0.18200497244578,184,34597,36,44,73 -10356,356 bottles of beer on the wall,Option 1,0.13798288029524,955,24751,69,56,11 -10357,357 bottles of beer on the wall,Option 1,0.81430039406488,268,18672,62,91,35 -10358,358 bottles of beer on the wall,Option 1,0.43832174522724,693,32350,29,44,16 -10359,359 bottles of beer on the wall,Option 1,0.029728665030435,459,5440,18,15,73 -10360,360 bottles of beer on the wall,Option 1,0.0315013211367192,722,16356,36,94,34 -10361,361 bottles of beer on the wall,Option 1,0.5826698898257,689,39648,77,16,31 -10362,362 bottles of beer on the wall,Option 1,0.6902719348158,482,34034,34,28,24 -10363,363 bottles of beer on the wall,Option 1,0.31280953544788,122,8845,82,13,11 -10364,364 bottles of beer on the wall,Option 1,0.40638104565738,59,17576,51,83,33 -10365,365 bottles of beer on the wall,Option 1,0.0494814422211989,427,16863,86,58,83 -10366,366 bottles of beer on the wall,Option 1,0.11973315715777,211,5076,50,91,7 -10367,367 bottles of beer on the wall,Option 1,0.64716058859935,267,31744,84,38,0 -10368,368 bottles of beer on the wall,Option 1,0.26110028115152,618,5356,52,14,45 -10369,369 bottles of beer on the wall,Option 1,0.28273768829309,318,1330,93,89,55 -10370,370 bottles of beer on the wall,Option 1,0.2922141674404,632,46096,5,81,23 -10371,371 bottles of beer on the wall,Option 1,0.41764038541244,906,49463,57,1,62 -10372,372 bottles of beer on the wall,Option 1,0.46991869503162,514,18143,97,74,74 -10373,373 bottles of beer on the wall,Option 1,0.37822098302572,895,32283,70,48,12 -10374,374 bottles of beer on the wall,Option 1,0.29593264744427,113,4983,7,27,41 -10375,375 bottles of beer on the wall,Option 1,0.53441656172946,220,39018,54,89,65 -10376,376 bottles of beer on the wall,Option 1,0.0352940624744138,109,20139,36,100,10 -10377,377 bottles of beer on the wall,Option 1,0.20479777232035,608,6078,64,74,17 -10378,378 bottles of beer on the wall,Option 1,0.50705856806927,135,49049,10,54,37 -10379,379 bottles of beer on the wall,Option 1,0.66727866961959,412,46966,29,91,57 -10380,380 bottles of beer on the wall,Option 1,0.92177499454551,580,26551,28,51,48 -10381,381 bottles of beer on the wall,Option 1,0.38939245948074,78,4703,90,2,83 -10382,382 bottles of beer on the wall,Option 1,0.61963193706219,174,43627,79,32,20 -10383,383 bottles of beer on the wall,Option 1,0.28356101283969,199,30131,69,41,95 -10384,384 bottles of beer on the wall,Option 1,0.31165435086547,77,14822,68,53,73 -10385,385 bottles of beer on the wall,Option 1,0.31375717619143,905,21353,48,53,98 -10386,386 bottles of beer on the wall,Option 1,0.49205248127321,25,43511,50,63,86 -10387,387 bottles of beer on the wall,Option 1,0.0871032425608,546,41998,56,8,54 -10388,388 bottles of beer on the wall,Option 1,0.58564706965612,707,32080,48,13,71 -10389,389 bottles of beer on the wall,Option 1,0.11565735010227,962,23374,70,87,46 -10390,390 bottles of beer on the wall,Option 1,0.67894278731147,712,3675,22,39,59 -10391,391 bottles of beer on the wall,Option 1,0.55559360494631,485,35672,4,91,33 -10392,392 bottles of beer on the wall,Option 1,0.19414928331698,181,5016,91,63,77 -10393,393 bottles of beer on the wall,Option 1,0.35980724001294,784,45193,40,85,1 -10394,394 bottles of beer on the wall,Option 1,0.56788968181604,558,29278,34,1,48 -10395,395 bottles of beer on the wall,Option 1,0.443418078331,67,40850,25,96,88 -10396,396 bottles of beer on the wall,Option 1,0.37440109084099,80,28866,54,92,41 -10397,397 bottles of beer on the wall,Option 1,0.98865012265213,251,47673,91,46,37 -10398,398 bottles of beer on the wall,Option 1,0.92300515804579,559,10806,54,44,62 -10399,399 bottles of beer on the wall,Option 1,0.64376400301408,567,44649,57,61,58 -10400,400 bottles of beer on the wall,Option 1,0.71294574752121,715,6570,31,26,3 -10401,401 bottles of beer on the wall,Option 1,0.0256445333481042,635,9939,72,83,63 -10402,402 bottles of beer on the wall,Option 1,0.29210200267476,328,35640,57,94,97 -10403,403 bottles of beer on the wall,Option 1,0.6438509126398,426,17030,58,99,72 -10404,404 bottles of beer on the wall,Option 1,0.72967141854095,719,23973,72,4,19 -10405,405 bottles of beer on the wall,Option 1,0.64815993963189,589,16332,78,22,8 -10406,406 bottles of beer on the wall,Option 1,0.68862963127374,792,19827,98,95,84 -10407,407 bottles of beer on the wall,Option 1,0.57015705461155,315,8751,26,88,99 -10408,408 bottles of beer on the wall,Option 1,0.37385381263394,936,22120,63,22,62 -10409,409 bottles of beer on the wall,Option 1,0.42852969580727,575,4535,43,73,1 -10410,410 bottles of beer on the wall,Option 1,0.09136778679274,974,6568,15,99,89 -10411,411 bottles of beer on the wall,Option 1,0.35649286925629,150,8757,11,21,13 -10412,412 bottles of beer on the wall,Option 1,0.78222061916357,343,22099,52,78,86 -10413,413 bottles of beer on the wall,Option 1,0.14624287939921,416,38106,39,98,94 -10414,414 bottles of beer on the wall,Option 1,0.90027753212502,496,20157,67,58,67 -10415,415 bottles of beer on the wall,Option 1,0.33255961040619,752,49026,33,28,14 -10416,416 bottles of beer on the wall,Option 1,0.77815227060492,640,35944,70,52,55 -10417,417 bottles of beer on the wall,Option 1,0.0933360564957,758,15354,32,19,55 -10418,418 bottles of beer on the wall,Option 1,0.0289132245950928,180,5242,85,50,98 -10419,419 bottles of beer on the wall,Option 1,0.84686978945828,458,1258,80,25,73 -10420,420 bottles of beer on the wall,Option 1,0.338352090371,509,42861,22,43,54 -10421,421 bottles of beer on the wall,Option 1,0.86671940137945,577,8605,34,90,100 -10422,422 bottles of beer on the wall,Option 1,0.62074221233871,60,22854,9,99,38 -10423,423 bottles of beer on the wall,Option 1,0.33002009630669,355,5698,48,91,58 -10424,424 bottles of beer on the wall,Option 1,0.74825180030812,425,17426,75,51,47 -10425,425 bottles of beer on the wall,Option 1,0.15292107088161,413,42526,51,59,44 -10426,426 bottles of beer on the wall,Option 1,0.94121787554641,513,31473,8,93,82 -10427,427 bottles of beer on the wall,Option 1,0.30919636381286,556,31101,27,16,50 -10428,428 bottles of beer on the wall,Option 1,0.4236965931131,398,29008,36,85,56 -10429,429 bottles of beer on the wall,Option 1,0.75479524198677,249,47750,51,15,7 -10430,430 bottles of beer on the wall,Option 1,0.49933161796039,918,42396,88,38,92 -10431,431 bottles of beer on the wall,Option 1,0.93344558539495,45,43421,17,90,69 -10432,432 bottles of beer on the wall,Option 1,0.9476499729546,937,16157,85,76,3 -10433,433 bottles of beer on the wall,Option 1,0.74850770120905,311,40579,39,22,72 -10434,434 bottles of beer on the wall,Option 1,0.73553585481622,817,15748,47,77,22 -10435,435 bottles of beer on the wall,Option 1,0.3191975039985,383,22167,85,79,73 -10436,436 bottles of beer on the wall,Option 1,0.30836578146944,849,12164,85,65,1 -10437,437 bottles of beer on the wall,Option 1,0.24748317443182,80,12922,65,62,16 -10438,438 bottles of beer on the wall,Option 1,0.81273083566349,246,18919,42,57,78 -10439,439 bottles of beer on the wall,Option 1,0.23397017793449,549,49719,50,15,17 -10440,440 bottles of beer on the wall,Option 1,0.52619479481419,742,35354,61,87,16 -10441,441 bottles of beer on the wall,Option 1,0.52756361594775,100,42930,55,64,29 -10442,442 bottles of beer on the wall,Option 1,0.65198407492227,782,42167,14,41,44 -10443,443 bottles of beer on the wall,Option 1,0.0511230966314315,280,15886,87,31,52 -10444,444 bottles of beer on the wall,Option 1,0.68976008365385,105,46257,66,37,6 -10445,445 bottles of beer on the wall,Option 1,0.4041808938627,759,44435,63,71,69 -10446,446 bottles of beer on the wall,Option 1,0.12014651304117,860,30110,54,61,64 -10447,447 bottles of beer on the wall,Option 1,0.9186017284722,305,4246,75,84,61 -10448,448 bottles of beer on the wall,Option 1,0.026245259226414,413,8816,67,79,12 -10449,449 bottles of beer on the wall,Option 1,0.56787226887786,67,4407,31,5,8 -10450,450 bottles of beer on the wall,Option 1,0.28620145064136,903,36836,25,77,29 -10451,451 bottles of beer on the wall,Option 1,0.76012127975007,253,13863,59,65,36 -10452,452 bottles of beer on the wall,Option 1,0.7600311770849,106,46566,99,57,79 -10453,453 bottles of beer on the wall,Option 1,0.40851825634414,894,38655,14,34,57 -10454,454 bottles of beer on the wall,Option 1,0.60583713725481,215,48030,77,9,82 -10455,455 bottles of beer on the wall,Option 1,0.36183023097078,75,5740,13,76,95 -10456,456 bottles of beer on the wall,Option 1,0.9295114483356,699,10234,52,74,92 -10457,457 bottles of beer on the wall,Option 1,0.10465663303837,107,23068,27,59,18 -10458,458 bottles of beer on the wall,Option 1,0.26722961909474,742,12522,12,50,38 -10459,459 bottles of beer on the wall,Option 1,0.69828339372681,761,33853,42,31,59 -10460,460 bottles of beer on the wall,Option 1,0.18323942002991,818,9662,0,84,61 -10461,461 bottles of beer on the wall,Option 1,0.74397214257343,852,42889,46,56,59 -10462,462 bottles of beer on the wall,Option 1,0.51641171915289,37,36520,76,3,21 -10463,463 bottles of beer on the wall,Option 1,0.5330098092244,366,5617,11,29,54 -10464,464 bottles of beer on the wall,Option 1,0.48579485178263,972,20268,70,91,72 -10465,465 bottles of beer on the wall,Option 1,0.88459145179232,169,49656,62,31,95 -10466,466 bottles of beer on the wall,Option 1,0.27047502681169,974,37531,63,98,68 -10467,467 bottles of beer on the wall,Option 1,0.41628622888414,389,43579,89,23,60 -10468,468 bottles of beer on the wall,Option 1,0.82779662722153,332,20692,50,28,57 -10469,469 bottles of beer on the wall,Option 1,0.70954406247918,490,47399,57,81,14 -10470,470 bottles of beer on the wall,Option 1,0.92123286515531,567,33351,22,98,86 -10471,471 bottles of beer on the wall,Option 1,0.12892824091433,123,7334,0,31,68 -10472,472 bottles of beer on the wall,Option 1,0.22435044833661,570,32382,22,95,74 -10473,473 bottles of beer on the wall,Option 1,0.54821905705529,871,18921,6,94,44 -10474,474 bottles of beer on the wall,Option 1,0.23450684651476,507,31401,81,72,16 -10475,475 bottles of beer on the wall,Option 1,0.07117847123703,180,25282,58,96,16 -10476,476 bottles of beer on the wall,Option 1,0.49323983792832,949,10498,11,82,84 -10477,477 bottles of beer on the wall,Option 1,0.58254413054443,588,40694,82,40,79 -10478,478 bottles of beer on the wall,Option 1,0.83353380106088,269,41193,66,92,15 -10479,479 bottles of beer on the wall,Option 1,0.69655849351387,334,45349,22,14,93 -10480,480 bottles of beer on the wall,Option 1,0.870674671545,80,18601,22,35,86 -10481,481 bottles of beer on the wall,Option 1,0.17769934897203,913,4216,47,16,36 -10482,482 bottles of beer on the wall,Option 1,0.62717400520442,518,35637,37,18,86 -10483,483 bottles of beer on the wall,Option 1,0.86194469587036,1,28857,4,43,96 -10484,484 bottles of beer on the wall,Option 1,0.44877002129739,724,7230,96,6,85 -10485,485 bottles of beer on the wall,Option 1,0.50334470556273,743,10148,74,28,80 -10486,486 bottles of beer on the wall,Option 1,0.90470263450625,278,20167,8,10,91 -10487,487 bottles of beer on the wall,Option 1,0.0564905135224064,243,34667,74,23,33 -10488,488 bottles of beer on the wall,Option 1,0.90503595858115,966,33336,51,73,40 -10489,489 bottles of beer on the wall,Option 1,0.53766181996914,79,44104,70,11,12 -10490,490 bottles of beer on the wall,Option 1,0.61526762722771,441,46213,85,55,10 -10491,491 bottles of beer on the wall,Option 1,0.68691758796894,182,38795,93,78,62 -10492,492 bottles of beer on the wall,Option 1,0.0052189598815604,231,18114,29,30,62 -10493,493 bottles of beer on the wall,Option 1,0.87267877760933,213,31756,7,35,21 -10494,494 bottles of beer on the wall,Option 1,0.49640192161146,534,47281,25,29,34 -10495,495 bottles of beer on the wall,Option 1,0.71941438537063,152,34329,30,10,39 -10496,496 bottles of beer on the wall,Option 1,0.65859091312558,965,11892,18,37,78 -10497,497 bottles of beer on the wall,Option 1,0.72376093534927,598,21067,34,67,48 -10498,498 bottles of beer on the wall,Option 1,0.98639214503876,299,43697,84,21,11 -10499,499 bottles of beer on the wall,Option 1,0.13237802085111,838,146,80,23,45 -10500,500 bottles of beer on the wall,Option 1,0.20127655621677,156,44782,60,99,19 -0,this is dummy datass,Option 1,42,42,42,42,3,44 -1,more dummy datass,Option 2,42,42,42,422,3,44 -;;;; -` \ No newline at end of file +// Generate CSV from the table data (data-driven, not hardcoded) +let csvLines = [] + +// Determine the table name from the `table` param (format: LIBREF.TABLE) +let tableName = '' +if (table) { + const parts = table.split('.') + tableName = parts[parts.length - 1] +} + +const tableData = loadTableData(tableName) +if (tableData && tableData.columns && tableData.rows) { + // Build column ddtype lookup + const colDdtypes = {} + for (const col of tableData.columns) { + const fmt = (col.format || '').toLowerCase() + let ddtype = col.type === 'N' ? 'N' : 'C' + if (fmt.startsWith('datetime') || fmt.startsWith('e8601dt')) ddtype = 'DATETIME' + else if (fmt.startsWith('date')) ddtype = 'DATE' + else if (fmt.startsWith('time')) ddtype = 'TIME' + colDdtypes[col.name.toUpperCase()] = ddtype + } + + for (const row of tableData.rows) { + const values = [] + for (const col of tableData.columns) { + let val = row[col.name] + if (val === undefined) val = row[col.name.toLowerCase()] + if (val === undefined) { + const key = Object.keys(row).find(k => k.toLowerCase() === col.name.toLowerCase()) + if (key) val = row[key] + } + if (val === null || val === undefined) { + val = '' + } else { + const ddtype = colDdtypes[col.name.toUpperCase()] + if (ddtype === 'DATE' || ddtype === 'DATETIME' || ddtype === 'TIME') { + val = formatSasValue(val, ddtype) + } else { + val = String(val) + } + // Quote if contains comma or quote + if (val.includes(',') || val.includes('"')) { + val = '"' + val.replace(/"/g, '""') + '"' + } + } + values.push(val) + } + csvLines.push(values.join(',')) + } +} + +_webout = csvLines.join('\n') diff --git a/sas/mocks/sasjs/services/public/startupservice.js b/sas/mocks/sasjs/services/public/startupservice.js index 1a8a69e..30bc073 100644 --- a/sas/mocks/sasjs/services/public/startupservice.js +++ b/sas/mocks/sasjs/services/public/startupservice.js @@ -41,7 +41,7 @@ try { } try { - fs.writeFileSync(usersStore, JSON.stringify(newUsers)) + fs.writeFileSync(usersStore, JSON.stringify(newUsers, null, 2)) } catch (err) {} } } catch(err) { @@ -121,6 +121,7 @@ ${sasdatasetsJson} [ {"DCLIB": "${dcLibref}", "SAS9LINEAGE_ENABLED": 1, +"ISADMIN": 1, "ISREGISTERED": ${users.ISREGISTERED}, "REGISTERCOUNT": ${users.REGISTERCOUNT}, "DC_ADMIN_GROUP": "${adminGroup}", diff --git a/sas/mocks/sasjs/services/public/validatefilter.js b/sas/mocks/sasjs/services/public/validatefilter.js index 7cbfa27..6e2fbe9 100644 --- a/sas/mocks/sasjs/services/public/validatefilter.js +++ b/sas/mocks/sasjs/services/public/validatefilter.js @@ -9,8 +9,8 @@ if (!fs.existsSync(sessionStoragePath)){ fs.mkdirSync(sessionStoragePath); } -if (_WEBIN_FILENAME1.includes('filterquery')) iwantFileText = _WEBIN_FILEREF1.toString() -if (_WEBIN_FILENAME2.includes('filterquery')) iwantFileText = _WEBIN_FILEREF2.toString() +if (_webin_filename1.includes('filterquery')) iwantFileText = _webin_fileref1.toString() +if (_webin_filename2.includes('filterquery')) iwantFileText = _webin_fileref2.toString() if (iwantFileText.length > 0) { const filterStore = path.resolve(sessionStoragePath, 'filter.txt') diff --git a/sas/mocks/sasjs/services/public/viewdata.js b/sas/mocks/sasjs/services/public/viewdata.js index 8fce838..a9c3220 100644 --- a/sas/mocks/sasjs/services/public/viewdata.js +++ b/sas/mocks/sasjs/services/public/viewdata.js @@ -6,45 +6,18 @@ const driveRoot = nodePath.resolve(sasjsRoot, 'drive') const dcLibref = 'DC_JSLIB' const dataDir = nodePath.resolve(driveRoot, 'files', appLoc, 'data', dcLibref) -// Helper: load a table JSON file -function loadTableData(tableName) { - try { - const file = nodePath.resolve(dataDir, tableName.toLowerCase() + '.json') - return JSON.parse(fs.readFileSync(file, {encoding:'utf8'}).toString()) - } catch(err) { return null } -} +eval(fs.readFileSync(nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js'), 'utf8')) +const loadTableData = makeTableLoader(dataDir) -// Parse SASControlTable for LIBDS and FILTER_RK -let libds = '' -let filterText = '' -let sasControlTableFileText = '' - -for (let i = 1; i <= 10; i++) { - const nameVar = '_WEBIN_NAME' + i - try { - const nameVal = eval(nameVar) - if (nameVal && nameVal.toLowerCase() === 'sascontroltable') { - const refVar = '_WEBIN_FILEREF' + i - sasControlTableFileText = eval(refVar).toString() - break - } - } catch(e) { break } -} - -try { - const lines = sasControlTableFileText.split(/\r?\n/) - if (lines.length >= 2) { - const headers = lines[0].trim().split(/\s+/).map(h => h.split(':')[0].trim().toLowerCase()) - const values = lines[1].trim().split(',') - const libdsIdx = headers.indexOf('libds') - if (libdsIdx >= 0) libds = values[libdsIdx] || '' - } -} catch(err) {} +// ─── Parse input ────────────────────────────────────────────────────────────── +const _sctRow = fetchTable('SASControlTable')[0] || {} +let libds = _sctRow.LIBDS || '' const libref = libds.split('.')[0] || dcLibref const dsn = libds.split('.')[1] || '' -// Load the requested table +// ─── Build response ────────────────────────────────────────────────────────── + const tableData = loadTableData(dsn) let cols = [] @@ -53,32 +26,20 @@ let vars = {} let nobs = 0 if (tableData && tableData.columns) { - // viewdata sends ALL columns (no MPE_TABLES filtering, no TX_FROM/TX_TO removal) - // cols format for viewdata is different from getdata - includes TYPE, LENGTH, FORMAT cols = tableData.columns.map(col => { - const fmt = col.format || '' - let ddtype = col.type === 'N' ? 'N' : 'C' - if (fmt.startsWith('datetime') || fmt.startsWith('E8601DT')) ddtype = 'DATETIME' - else if (fmt.startsWith('date')) ddtype = 'DATE' - else if (fmt.startsWith('time')) ddtype = 'TIME' - const fmtname = fmt - ? fmt.replace(/[\d.]+$/, '').replace('datetime', 'DATETIME').replace('date', 'DATE').replace('time', 'TIME').replace('best', 'BEST').replace('E8601DT', 'DATETIME') + const ddtype = getDdType(col) + const fmtname = col.format + ? col.format.replace(/[\d.]+$/, '').replace('datetime', 'DATETIME').replace('date', 'DATE').replace('time', 'TIME').replace('best', 'BEST').replace('E8601DT', 'DATETIME') : ' ' - const formatStr = col.type === 'N' - ? (fmt || (col.length === 4 ? '8.' : '8.')) - : '$' + col.length + '.' + const formatStr = col.type === 'N' ? (col.format || '8.') : ('$' + col.length + '.') return { - NAME: col.name, - TYPE: col.type === 'N' ? 'N' : 'C', - LENGTH: col.length, - FORMAT: formatStr, - FMTNAME: fmtname || ' ', - DDTYPE: ddtype, + NAME: col.name, TYPE: col.type === 'N' ? 'N' : 'C', LENGTH: col.length, + FORMAT: formatStr, FMTNAME: fmtname || ' ', DDTYPE: ddtype, LABEL: col.label || col.name } }).sort((a, b) => a.NAME.localeCompare(b.NAME)) - // viewdata rows: all values as strings (SAS proc print style) + // viewdata rows: all values as strings (formatted, SAS proc-print style) viewdata = (tableData.rows || []).map(r => { const row = {} for (const col of tableData.columns) { @@ -89,22 +50,24 @@ if (tableData && tableData.columns) { const key = Object.keys(r).find(k => k.toLowerCase() === lcName) if (key) val = r[key] } - // Format date/datetime/time values to SAS display format - const fmt = (col.format || '').toLowerCase() + const ddtype = getDdType(col) if (val !== undefined && val !== null && val !== '') { - if (fmt.startsWith('datetime') || fmt.startsWith('e8601dt')) { - // Convert ISO datetime to SAS format: DDMMMYY:HH:MM:SS - const m = String(val).match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/) - if (m) { - const months = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'] - val = `${parseInt(m[3])}${months[parseInt(m[2])-1]}${m[1].slice(2)}:${m[4]}:${m[5]}:${m[6]}` - } - } else if (fmt.startsWith('date')) { - // Convert ISO date to SAS format: DDMMMYYYY - const m = String(val).match(/^(\d{4})-(\d{2})-(\d{2})/) - if (m) { - const months = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'] - val = `${parseInt(m[3])}${months[parseInt(m[2])-1]}${m[1]}` + if (typeof val === 'number') { + val = formatSasValue(val, ddtype) + } else { + // Try converting ISO/old-format strings to SAS display format + if (ddtype === 'DATETIME') { + const m = String(val).match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})/) + if (m) { + const mn = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'] + val = `${parseInt(m[3])}${mn[parseInt(m[2])-1]}${m[1].slice(2)}:${m[4]}:${m[5]}:${m[6]}` + } + } else if (ddtype === 'DATE') { + const m = String(val).match(/^(\d{4})-(\d{2})-(\d{2})/) + if (m) { + const mn = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'] + val = `${parseInt(m[3])}${mn[parseInt(m[2])-1]}${m[1]}` + } } } } @@ -113,33 +76,18 @@ if (tableData && tableData.columns) { return row }) - // $viewdata vars - keeps original SAS types (num stays num, char stays char) + // $viewdata vars: keep original SAS types (num stays num, char stays char) vars = {} for (const col of tableData.columns) { - const fmt = col.format || '' - let formatStr - if (col.type === 'N') { - formatStr = fmt || 'best.' - } else { - formatStr = '$' + col.length + '.' - } - vars[col.name] = { - format: formatStr, - label: col.label || col.name, - length: String(col.length), - type: col.type === 'N' ? 'num' : 'char' - } + vars[col.name] = sasVarsEntry(col) } nobs = tableData.rows ? tableData.rows.length : 0 } _webout = JSON.stringify({ - SYSDATE: '27AUG26', - SYSTIME: '11:26', - cls_rules: [], - cols: cols, - dsmeta: [ + SYSDATE: '26SEP22', SYSTIME: '08:43', + cls_rules: [], cols, dsmeta: [ { ODS_TABLE: 'ATTRIBUTES', NAME: 'Data Set Name', VALUE: libref + '.' + dsn }, { ODS_TABLE: 'ATTRIBUTES', NAME: 'Observations', VALUE: String(nobs) }, { ODS_TABLE: 'ATTRIBUTES', NAME: 'Member Type', VALUE: 'DATA' }, @@ -148,36 +96,16 @@ _webout = JSON.stringify({ ], query: [], sasparams: [{ - TABLEURI: ' ', - TABLENAME: dsn, - FILTER_TEXT: filterText || ' ', - PK_FIELDS: ' ', - NOBS: nobs, - VARS: cols.length, - MAXROWS: 500 + TABLEURI: ' ', TABLENAME: dsn, FILTER_TEXT: ' ', + PK_FIELDS: ' ', NOBS: nobs, VARS: cols.length, MAXROWS: 500 }], - versions: [], - viewdata: viewdata, - $viewdata: { vars: vars }, - _DEBUG: '', - _METAUSER: 'sasdemo@SAS', - _METAPERSON: 'sasdemo', - _PROGRAM: _program, - AUTOEXEC: '', - MF_GETUSER: 'sasdemo', - SYSCC: '0', - SYSENCODING: 'utf-8', - SYSERRORTEXT: '', - SYSHOSTNAME: 'sasjs-server', - SYSPROCESSID: '0', - SYSPROCESSMODE: 'Stored Program', - SYSPROCESSNAME: '', - SYSJOBID: '0', - SYSSCPL: 'LINUX', - SYSSITE: '123', - SYSUSERID: 'sasdemo', - SYSVLONG: '9.04.01M7P080520', - SYSWARNINGTEXT: '', - END_DTTM: new Date().toISOString(), - MEMSIZE: '0KB' + versions: [], viewdata, $viewdata: { vars }, + _DEBUG: '', _METAUSER: 'sasdemo@SAS', _METAPERSON: 'sasdemo', + _PROGRAM: _program, AUTOEXEC: '', MF_GETUSER: 'sasdemo', + SYSCC: '0', SYSENCODING: 'utf-8', SYSERRORTEXT: '', + SYSHOSTNAME: 'sasjs-server', SYSPROCESSID: '0', + SYSPROCESSMODE: 'Stored Program', SYSPROCESSNAME: '', + SYSJOBID: '0', SYSSCPL: 'LINUX', SYSSITE: '123', + SYSUSERID: 'sasdemo', SYSVLONG: '9.04.01M7P080520', + SYSWARNINGTEXT: '', END_DTTM: new Date().toISOString(), MEMSIZE: '0KB' }) diff --git a/sas/mocks/sasjs/services/public/viewlibs.js b/sas/mocks/sasjs/services/public/viewlibs.js index 58b5476..6d977d3 100644 --- a/sas/mocks/sasjs/services/public/viewlibs.js +++ b/sas/mocks/sasjs/services/public/viewlibs.js @@ -11,7 +11,7 @@ let saslibs = [ LIBRARYID: 'DC_JSLIB', LIBRARYNAME: 'Data Controller(' + dcLibref + ')', LIBRARYREF: dcLibref, - ENGINE: 'BASE' + ENGINE: 'V9' } ]