Compare commits

...
3 Commits
Author SHA1 Message Date
YuryShkoda 88f55b9d06 chore(merge): merge origin/issue-251 into issue-251
Build / Build-and-ng-test (pull_request) Successful in 3m52s
Build / Build-and-test-development (pull_request) Successful in 10m21s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m45s
2026-07-09 17:51:49 +03:00
YuryShkoda 301675052f fix: adapt column validation to vertical-array COLTYPE (#253)
getdata.sas now sends one COLTYPE JSON-object string per cols[] row (via
a LEFT JOIN on the real dataset's columns) instead of one comma-joined
sasparams.COLTYPE string, avoiding the 32K cats() buffer overflow on
wide tables (#253). Adapt the client accordingly:

- parseColTypeRow replaces parseColType; DcValidator builds validation
  rules per cols[] entry instead of from sasparams.COLTYPE
- _____DELETE__THIS__RECORD_____'s rule is never present in cols[] (it's
  a client-only synthetic column %mp_getcols doesn't know about), so
  it's now hardcoded via deleteRecordColumnRule.ts
- cols[] isn't guaranteed to arrive in COLHEADERS/VARNUM order (backend
  can serialize it alphabetically by NAME) but editor.component.ts pairs
  rules with headers positionally, so DcValidator now sorts cols by
  VARNUM before building rules
- update sas/mocks/sasjs getdata.js to the new per-column COLTYPE shape
- add Cypress coverage for the delete-record dropdown and an ordinary
  dropdown column end to end
2026-07-09 17:48:33 +03:00
YuryShkoda 41fd618884 chore(merge): merge issue240 into issue-251
Brings in the SAS-side fixes for issue #253 (getdata.sas passing
variable formats via a vertical cols.COLTYPE array instead of a
single concatenated sasparams.COLTYPE string, which overflowed SAS's
32K buffer on wide datasets) plus related label/datadictionary fixes.
No client TypeScript files are touched by this merge.
2026-07-09 11:52:37 +03:00
11 changed files with 506 additions and 157 deletions
@@ -0,0 +1,132 @@
// Marks this file as an ES module (rather than a global script) so its
// top-level consts don't collide, under the TS type-checker, with the same
// names declared in other spec files — see viewer-labels.cy.ts for the same
// pattern.
export {}
const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
// Regression coverage for issue #253's vertical-array COLTYPE fix — see
// getdata.sas now sends one COLTYPE JSON-object string per cols[] row (via
// a LEFT JOIN keyed off the real dataset's columns), instead of one
// comma-joined sasparams.COLTYPE string covering every column. Two things
// are specifically at risk from that change and aren't covered elsewhere:
// 1. `_____DELETE__THIS__RECORD_____` (the delete checkbox column) is a
// client-side-only concept — %mp_getcols never has a row for it, so
// it can no longer travel via cols[].COLTYPE at all. Its Yes/No
// dropdown rule is now hardcoded client-side (deleteRecordColumnRule.ts)
// — test 1 proves that hardcode actually renders end to end.
// 2. Ordinary columns (e.g. SOME_DROPDOWN) still get their rule via the
// new per-row cols[].COLTYPE path — test 2 proves the migration didn't
// silently drop real columns' rules either.
// Column positions below come from COLHEADERS in
// sas/mocks/sasjs/services/editors/getdata.js:
// "_____DELETE__THIS__RECORD_____,PRIMARY_KEY_FIELD,SOME_CHAR,SOME_DROPDOWN,..."
// — childNodes[0] on a body <tr> is the row-header <th>, so childNodes[N+1]
// is the Nth (0-indexed) column in that list.
context('coltype vertical-array regression tests: ', function () {
this.beforeAll(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
cy.loginAndUpdateValidKey(true)
})
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
visitPage('home')
})
it('1 | delete-record column renders its hardcoded Yes/No dropdown', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
clickOnEdit()
cy.get('.ht_master tbody tr', { timeout: longerCommandTimeout }).then(
(rows: any) => {
// childNodes[1] = _____DELETE__THIS__RECORD_____ (1st column)
cy.get(rows[1].childNodes[1])
.click({ force: true })
.then(($td) => {
cy.get('.htAutocompleteArrow', { withinSubject: $td })
.should('exist')
.click({ force: true })
cy.get('.autocompleteEditor .htCore tbody td').should(
($choices) => {
const texts = [...$choices].map((el) => el.innerText.trim())
expect(texts).to.deep.equal(['No', 'Yes'])
}
)
})
}
)
})
it('2 | an ordinary dropdown column (SOME_DROPDOWN) still gets its rule from cols[].COLTYPE', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
clickOnEdit()
cy.get('.ht_master tbody tr', { timeout: longerCommandTimeout }).then(
(rows: any) => {
// childNodes[4] = SOME_DROPDOWN (4th column, 0-indexed as 3). This
// only holds if DcValidator emits rules in VARNUM order — see the
// "orders rules by VARNUM" regression test in dc-validator.spec.ts.
cy.get(rows[1].childNodes[4])
.click({ force: true })
.then(($td) => {
cy.get('.htAutocompleteArrow', { withinSubject: $td }).should(
'exist'
)
})
}
)
})
})
const clickOnEdit = (callback?: any) => {
cy.get('.btnCtrl button.btn-primary', { timeout: longerCommandTimeout })
.click()
.then(() => {
if (callback) callback()
})
}
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let viyaLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
viyaLib = node
break
}
}
cy.get(viyaLib).within(() => {
cy.get('.clr-tree-node-content-container > button').click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
}
const visitPage = (url: string) => {
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
-1
View File
@@ -37,7 +37,6 @@ export interface MaxVarLength {
export interface SASParam {
COLHEADERS: string
COLTYPE: string
DTTMVARS: string
DTVARS: string
CLS_FLAG: number
@@ -17,7 +17,8 @@ import { DQRule, DQRuleTypes } from './models/dq-rules.model'
import { getDqDataCols } from './utils/getDqDataCols'
import { getNotNullDefault } from './utils/getNotNullDefault'
import { mergeColsRules } from './utils/mergeColsRules'
import { parseColType } from './utils/parseColType'
import { parseColTypeRow } from './utils/parseColTypeRow'
import { DELETE_RECORD_COLUMN_RULE } from './utils/deleteRecordColumnRule'
import { dqValidate } from './validations/dq-validation'
import {
datetimeValidator,
@@ -50,7 +51,24 @@ export class DcValidator {
this.sasparams = sasparams
this.hotInstance = hotInstance
this.rules = parseColType(sasparams.COLTYPE)
// Each cols[i].COLTYPE is now a single JSON-object string (one per
// column, as of the vertical-array getdata.sas fix — issue #253), not
// the old comma-joined sasparams.COLTYPE list. DELETE_RECORD_COLUMN_RULE
// is prepended separately — see its own doc comment for why.
//
// cols[] is NOT guaranteed to arrive in COLHEADERS/display order — e.g.
// %mp_getcols can return it sorted by NAME. editor.component.ts pairs
// getRules()[i] with headerColumns[i] positionally, so this array must
// be built in VARNUM order (which does match COLHEADERS) rather than
// cols[]'s own order, or every rule after the first alphabetically-out-
// of-place column ends up bound to the wrong header.
const colsByVarnum = [...cols].sort((a, b) => a.VARNUM - b.VARNUM)
this.rules = [
DELETE_RECORD_COLUMN_RULE,
...colsByVarnum
.map((col) => parseColTypeRow(col.COLTYPE))
.filter((rule): rule is DcValidation => rule !== undefined)
]
this.rules = mergeColsRules(cols, this.rules, $dataFormats)
this.rules = applyNumericFormats(this.rules)
this.rules = mapIntlCellTypes(this.rules)
@@ -9,4 +9,8 @@ export interface Col {
MEMLABEL: string
DESC: string
LONGDESC: string
// Handsontable column-formatting spec (JSON-object string), one per column
// — see parseColTypeRow.ts. Optional: getdata.sas LEFT JOINs this in, so
// an unmatched row can leave it unset.
COLTYPE?: string
}
@@ -23,11 +23,11 @@ describe('DC Validator', () => {
// Check if COLS merged with FORMATS
expect(cols[0].TYPE).toEqual('char')
// Get all
// Get all — one rule per cols[] entry, plus the injected
// DELETE_RECORD_COLUMN_RULE (never present in cols[], see its own
// doc comment)
const validationRules = dcValidator.getRules()
expect(validationRules).toHaveSize(
JSON.parse(`[${example_COLTYPE}]`).length
)
expect(validationRules).toHaveSize(example_cols.length + 1)
// Get col with notnull validation
const someNumRule = dcValidator.getRule('SOME_NUM')
@@ -66,6 +66,15 @@ describe('DC Validator', () => {
const someDropdownHardRule = dcValidator.getRule('SOME_DROPDOWN_HARD')
// Check strict - it is hardselect so strict should be true
expect(someDropdownHardRule?.strict).toBeTrue()
// _____DELETE__THIS__RECORD_____ never has a cols[] entry (%mp_getcols
// doesn't know about it — see DELETE_RECORD_COLUMN_RULE), but its rule
// must still exist with its fixed dropdown shape, independent of cols
const deleteRecordRule = dcValidator.getRule(
'_____DELETE__THIS__RECORD_____'
)
expect(deleteRecordRule?.type).toEqual('dropdown')
expect(deleteRecordRule?.source).toEqual(['No', 'Yes'])
})
it('should create an instance of validator and execute its functions', () => {
@@ -278,6 +287,36 @@ describe('DC Validator', () => {
expect(dcValidator.getRoundDigits('SOME_SHORTNUM')).toEqual(2)
expect(dcValidator.getRoundDigits('SOME_NUM')).toBeUndefined()
})
it('5 | orders rules by VARNUM even when cols[] arrives in a different order', () => {
// Regression test: %mp_getcols/cols1 can serialize cols[] alphabetically
// by NAME (confirmed against a real getdata.sas response) rather than in
// VARNUM/COLHEADERS order. editor.component.ts pairs
// dcValidator.getRules()[i] with headerColumns[i] positionally, so if
// DcValidator built rules in cols[]'s own (alphabetical) order, every
// rule after the first out-of-place column would end up bound to the
// wrong header — silently mis-rendering cell types/dropdowns.
const alphabeticalCols = [...example_cols].sort((a, b) =>
a.NAME.localeCompare(b.NAME)
)
const dcValidator: DcValidator = new DcValidator(
example_sasparams,
example_dataformats,
alphabeticalCols,
example_dqRules,
example_dqData
)
const rules = dcValidator.getRules()
const colsByVarnum = [...example_cols].sort((a, b) => a.VARNUM - b.VARNUM)
// rules[0] is the injected DELETE_RECORD_COLUMN_RULE (no VARNUM of its
// own — see its own doc comment); the rest must follow ascending VARNUM.
for (let i = 0; i < colsByVarnum.length; i++) {
expect(rules[i + 1].data).toEqual(colsByVarnum[i].NAME)
}
})
})
const example_dqData = [
@@ -383,6 +422,13 @@ const example_dqRules: any = [
}
]
// One row per column, each carrying its own COLTYPE JSON-object string — the
// vertical-array shape from issue #253's getdata.sas fix. Order: SOME_DROPDOWN
// then SOME_NUM first (the `cols[0].TYPE` assertion above is positional),
// remaining columns in the same order the old concatenated
// sasparams.COLTYPE used to list them. _____DELETE__THIS__RECORD_____ is
// deliberately absent — DcValidator injects its rule directly (see
// DELETE_RECORD_COLUMN_RULE), since %mp_getcols never has a row for it.
const example_cols = [
{
CLS_RULE: 'READ',
@@ -394,7 +440,8 @@ const example_cols = [
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_DROPDOWN',
VARNUM: 3
VARNUM: 3,
COLTYPE: '{"data":"SOME_DROPDOWN"}'
},
{
CLS_RULE: 'READ',
@@ -406,71 +453,144 @@ const example_cols = [
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_NUM',
VARNUM: 4
VARNUM: 4,
COLTYPE: '{"data":"SOME_NUM","type":"numeric","format":"0"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'NUMERIC',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'PRIMARY_KEY_FIELD',
LONGDESC: '',
MEMLABEL: '',
NAME: 'PRIMARY_KEY_FIELD',
VARNUM: 1,
COLTYPE: '{"data":"PRIMARY_KEY_FIELD","type":"numeric","format":"0"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'CHARACTER',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_CHAR',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_CHAR',
VARNUM: 2,
COLTYPE: '{"data":"SOME_CHAR"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'CHARACTER',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_CHAR_LOW',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_CHAR_LOW',
VARNUM: 5,
COLTYPE: '{"data":"SOME_CHAR_LOW"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'CHARACTER',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_CHAR_ANY',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_CHAR_ANY',
VARNUM: 6,
COLTYPE: '{"data":"SOME_CHAR_ANY"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'CHARACTER',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_DROPDOWN_HARD',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_DROPDOWN_HARD',
VARNUM: 7,
COLTYPE: '{"data":"SOME_DROPDOWN_HARD"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'DATE',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_DATE',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_DATE',
VARNUM: 8,
COLTYPE: '{"data":"SOME_DATE","type":"date"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'DATETIME',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_DATETIME',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_DATETIME',
VARNUM: 9,
COLTYPE: '{"data":"SOME_DATETIME","type":"datetime"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'TIME',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_TIME',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_TIME',
VARNUM: 10,
COLTYPE: '{"data":"SOME_TIME","type":"time"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'NUMERIC',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_SHORTNUM',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_SHORTNUM',
VARNUM: 11,
COLTYPE: '{"data":"SOME_SHORTNUM","type":"numeric","format":"0"}'
},
{
CLS_RULE: 'READ',
DDTYPE: 'NUMERIC',
DESC: '',
TYPE: '',
FMTNAME: '',
LABEL: 'SOME_BESTNUM',
LONGDESC: '',
MEMLABEL: '',
NAME: 'SOME_BESTNUM',
VARNUM: 12,
COLTYPE: '{"data":"SOME_BESTNUM","type":"numeric","format":"0"}'
}
]
const example_COLTYPE = `
{
"data":"_____DELETE__THIS__RECORD_____",
"type":"dropdown",
"source":[
"No",
"Yes"
]
},
{
"data":"PRIMARY_KEY_FIELD",
"type":"numeric",
"format":"0"
},
{
"data":"SOME_CHAR"
},
{
"data":"SOME_CHAR_LOW"
},
{
"data":"SOME_CHAR_ANY"
},
{
"data":"SOME_DROPDOWN"
},
{
"data":"SOME_DROPDOWN_HARD"
},
{
"data":"SOME_NUM",
"type":"numeric",
"format":"0"
},
{
"data":"SOME_DATE",
"type":"date"
},
{
"data":"SOME_DATETIME",
"type":"datetime"
},
{
"data":"SOME_TIME",
"type":"time"
},
{
"data":"SOME_SHORTNUM",
"type":"numeric",
"format":"0"
},
{
"data":"SOME_BESTNUM",
"type":"numeric",
"format":"0"
}`
const example_sasparams = {
CLS_FLAG: 0,
COLHEADERS: 'head1 head2',
COLTYPE: example_COLTYPE,
DTTMVARS: 'dttm vars',
DTVARS: 'dt vars',
FILTER_TEXT: 'filter text',
@@ -507,6 +627,24 @@ const example_dataformats = {
length: '128',
type: 'char'
},
SOME_CHAR_LOW: {
format: '$128.',
label: 'SOME_CHAR_LOW',
length: '128',
type: 'char'
},
SOME_CHAR_ANY: {
format: '$128.',
label: 'SOME_CHAR_ANY',
length: '128',
type: 'char'
},
SOME_DROPDOWN_HARD: {
format: '$128.',
label: 'SOME_DROPDOWN_HARD',
length: '128',
type: 'char'
},
SOME_NUM: {
format: 'best.',
label: 'SOME_NUM',
@@ -1,23 +0,0 @@
import { HotColumnSettings } from '../models/dc-validation.model'
import { parseColType } from '../utils/parseColType'
describe('DC Validator - parse col type', () => {
it('should return array of parsed json', () => {
const colTypeString =
'{"data":"test","test2":"test2"}, {"data":"test3","test4":"test4"}'
const expected: HotColumnSettings[] = [
{ data: 'test', test2: 'test2' },
{ data: 'test3', test4: 'test4' }
]
expect(parseColType(colTypeString)).toEqual(expected)
})
it('should return empty array for invalid json', () => {
const colTypeString =
'{"test":"test""test:"test2"}, {"test3":"test3","test4":"test4"}'
const expected: HotColumnSettings[] = []
expect(parseColType(colTypeString)).toEqual(expected)
})
})
@@ -0,0 +1,24 @@
import { HotColumnSettings } from '../models/dc-validation.model'
import { parseColTypeRow } from '../utils/parseColTypeRow'
describe('DC Validator - parse col type row', () => {
it('parses a single JSON object', () => {
const expected: HotColumnSettings = { data: 'test', type: 'numeric' }
expect(parseColTypeRow('{"data":"test","type":"numeric"}')).toEqual(
expected
)
})
it('returns undefined for undefined input (LEFT JOIN can leave it unset)', () => {
expect(parseColTypeRow(undefined)).toBeUndefined()
})
it('returns undefined for an empty string', () => {
expect(parseColTypeRow('')).toBeUndefined()
})
it('returns undefined for malformed JSON, without throwing', () => {
expect(parseColTypeRow('{not valid')).toBeUndefined()
})
})
@@ -0,0 +1,16 @@
import { DcValidation } from '../models/dc-validation.model'
/**
* `_____DELETE__THIS__RECORD_____` is a client-side UI concept (the delete
* checkbox column) that getdata.sas synthesizes into vars3/vars4 for display
* purposes only — it's never a real column of the underlying dataset, so
* `%mp_getcols` (which builds `cols1`, the left side of the join into
* `cols`) never has a row for it, and its rule can no longer travel via
* `cols[].COLTYPE` (see issue #253's vertical-array fix). The rule is fixed
* and data-independent, so it's injected directly here instead.
*/
export const DELETE_RECORD_COLUMN_RULE: DcValidation = {
data: '_____DELETE__THIS__RECORD_____',
type: 'dropdown',
source: ['No', 'Yes']
}
@@ -1,16 +0,0 @@
import { HotColumnSettings } from '../models/dc-validation.model'
/**
* From sas we get a string instead of array of objects, in that string `[]` are missing so
* before parsing JSON we need to add them.
*
* @param coltype string (objects) that comes from sas
* @returns JSON Handsontable.ColumnSettings[]
*/
export const parseColType = (coltype: string): HotColumnSettings[] => {
try {
return JSON.parse(`[${coltype}]`)
} catch (err: any) {
return []
}
}
@@ -0,0 +1,23 @@
import { HotColumnSettings } from '../models/dc-validation.model'
/**
* Parses a single column's `COLTYPE` JSON-object string (one per `cols` row,
* as of the vertical-array getdata.sas fix — see issue #253). Unlike the
* previous comma-joined `sasparams.COLTYPE` format, each row is independent:
* a malformed or missing value here only drops that one column's rule,
* rather than every column's.
*
* @param coltype a single column's COLTYPE JSON string, or undefined (the
* LEFT JOIN in getdata.sas can leave it unset for unmatched rows)
*/
export const parseColTypeRow = (
coltype: string | undefined
): HotColumnSettings | undefined => {
if (!coltype) return undefined
try {
return JSON.parse(coltype)
} catch (err: any) {
return undefined
}
}
+85 -51
View File
@@ -73,7 +73,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"PRIMARY_KEY_FIELD\",\"type\":\"numeric\",\"format\":\"0\"}"
},
{
NAME: "SOME_BESTNUM",
@@ -84,7 +85,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_BESTNUM\",\"type\":\"numeric\",\"format\":\"0\"}"
},
{
NAME: "SOME_CHAR",
@@ -95,7 +97,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_CHAR\"}"
},
{
NAME: "SOME_DATE",
@@ -106,7 +109,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_DATE\",\"type\":\"date\"}"
},
{
NAME: "SOME_DATETIME",
@@ -117,7 +121,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_DATETIME\",\"type\":\"datetime\"}"
},
{
NAME: "SOME_DROPDOWN",
@@ -128,7 +133,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_DROPDOWN\"}"
},
{
NAME: "SOME_HARDSELECT",
@@ -139,7 +145,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_HARDSELECT\"}"
},
{
NAME: "SOME_NUM",
@@ -150,7 +157,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_NUM\",\"type\":\"numeric\",\"format\":\"0\"}"
},
{
NAME: "SOME_SHORTNUM",
@@ -161,7 +169,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_SHORTNUM\",\"type\":\"numeric\",\"format\":\"0\"}"
},
{
NAME: "SOME_TIME",
@@ -172,7 +181,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SOME_TIME\",\"type\":\"time\"}"
},
{
NAME: "READONLY_COL",
@@ -183,7 +193,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "Read-only: default value inserted on add-row, not editable",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"READONLY_COL\"}"
},
{
NAME: "HIDDEN_COL",
@@ -194,7 +205,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "Hidden: invisible in grid but submitted; default on add-row",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"HIDDEN_COL\"}"
},
{
NAME: "ROUND_COL",
@@ -205,7 +217,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "Round: edited values rounded Excel-style to 2 decimals",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"ROUND_COL\",\"type\":\"numeric\",\"format\":\"0\"}"
},
{
NAME: "NUMFMT_COL",
@@ -216,7 +229,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "Number format: displayed as EUR currency (value unchanged)",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"NUMFMT_COL\",\"type\":\"numeric\",\"format\":\"0\"}"
}
],
dqdata: [
@@ -323,7 +337,6 @@ let webouts = {
DTVARS: " SOME_DATE",
DTTMVARS: " SOME_DATETIME",
TMVARS: " SOME_TIME",
COLTYPE: "{\"data\":\"_____DELETE__THIS__RECORD_____\",\"type\":\"dropdown\",\"source\":[\"No\",\"Yes\"]},{\"data\":\"PRIMARY_KEY_FIELD\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"SOME_CHAR\"},{\"data\":\"SOME_DROPDOWN\"},{\"data\":\"SOME_HARDSELECT\"},{\"data\":\"SOME_NUM\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"SOME_DATE\",\"type\":\"date\"},{\"data\":\"SOME_DATETIME\",\"type\":\"datetime\"},{\"data\":\"SOME_TIME\",\"type\":\"time\"},{\"data\":\"SOME_SHORTNUM\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"SOME_BESTNUM\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"READONLY_COL\"},{\"data\":\"HIDDEN_COL\"},{\"data\":\"ROUND_COL\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"NUMFMT_COL\",\"type\":\"numeric\",\"format\":\"0\"}",
LOADTYPE: "UPDATE",
RK_FLAG: 0,
CLS_FLAG: 0
@@ -366,7 +379,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DD_LONGDESC\"}"
},
{
NAME: "DD_OWNER",
@@ -377,7 +391,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DD_OWNER\"}"
},
{
NAME: "DD_RESPONSIBLE",
@@ -388,7 +403,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DD_RESPONSIBLE\"}"
},
{
NAME: "DD_SENSITIVITY",
@@ -399,7 +415,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DD_SENSITIVITY\"}"
},
{
NAME: "DD_SHORTDESC",
@@ -410,7 +427,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DD_SHORTDESC\"}"
},
{
NAME: "DD_SOURCE",
@@ -421,7 +439,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DD_SOURCE\"}"
},
{
NAME: "DD_TYPE",
@@ -432,7 +451,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DD_TYPE\"}"
},
{
NAME: "TX_FROM",
@@ -593,7 +613,6 @@ let webouts = {
DTVARS: "",
DTTMVARS: "",
TMVARS: "",
COLTYPE: "{\"data\":\"_____DELETE__THIS__RECORD_____\",\"type\":\"dropdown\",\"source\":[\"No\",\"Yes\"]},{\"data\":\"DD_TYPE\"},{\"data\":\"DD_SOURCE\"},{\"data\":\"DD_SHORTDESC\"},{\"data\":\"DD_LONGDESC\"},{\"data\":\"DD_OWNER\"},{\"data\":\"DD_RESPONSIBLE\"},{\"data\":\"DD_SENSITIVITY\"}",
LOADTYPE: "TXTEMPORAL",
RK_FLAG: 0,
CLS_FLAG: 0
@@ -660,7 +679,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"USER_ID\"}"
}
],
dqdata: [],
@@ -722,7 +742,6 @@ let webouts = {
DTVARS: "",
DTTMVARS: "",
TMVARS: "",
COLTYPE: "{\"data\":\"_____DELETE__THIS__RECORD_____\",\"type\":\"dropdown\",\"source\":[\"No\",\"Yes\"]},{\"data\":\"USER_ID\"}",
LOADTYPE: "UPDATE",
RK_FLAG: 0,
CLS_FLAG: 0
@@ -765,7 +784,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"AUDIT_LIBDS\"}"
},
{
NAME: "BUSKEY",
@@ -776,7 +796,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"BUSKEY\"}"
},
{
NAME: "CLOSE_VARS",
@@ -787,7 +808,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"CLOSE_VARS\"}"
},
{
NAME: "DSN",
@@ -798,7 +820,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"DSN\"}"
},
{
NAME: "LIBREF",
@@ -809,7 +832,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"LIBREF\"}"
},
{
NAME: "LOADTYPE",
@@ -820,7 +844,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"LOADTYPE\"}"
},
{
NAME: "NOTES",
@@ -831,7 +856,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"NOTES\"}"
},
{
NAME: "NUM_OF_APPROVALS_REQUIRED",
@@ -842,7 +868,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"NUM_OF_APPROVALS_REQUIRED\",\"type\":\"numeric\",\"format\":\"0\"}"
},
{
NAME: "POST_APPROVE_HOOK",
@@ -853,7 +880,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"POST_APPROVE_HOOK\"}"
},
{
NAME: "POST_EDIT_HOOK",
@@ -864,7 +892,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"POST_EDIT_HOOK\"}"
},
{
NAME: "PRE_APPROVE_HOOK",
@@ -875,7 +904,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"PRE_APPROVE_HOOK\"}"
},
{
NAME: "PRE_EDIT_HOOK",
@@ -886,7 +916,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"PRE_EDIT_HOOK\"}"
},
{
NAME: "RK_UNDERLYING",
@@ -897,7 +928,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"RK_UNDERLYING\"}"
},
{
NAME: "SIGNOFF_COLS",
@@ -908,7 +940,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SIGNOFF_COLS\"}"
},
{
NAME: "SIGNOFF_HOOK",
@@ -919,7 +952,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"SIGNOFF_HOOK\"}"
},
{
NAME: "TX_FROM",
@@ -952,7 +986,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"VAR_BUSFROM\"}"
},
{
NAME: "VAR_BUSTO",
@@ -963,7 +998,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"VAR_BUSTO\"}"
},
{
NAME: "VAR_PROCESSED",
@@ -974,7 +1010,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"VAR_PROCESSED\"}"
},
{
NAME: "VAR_TXFROM",
@@ -985,7 +1022,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"VAR_TXFROM\"}"
},
{
NAME: "VAR_TXTO",
@@ -996,7 +1034,8 @@ let webouts = {
CLS_RULE: "READ",
MEMLABEL: "",
DESC: "",
LONGDESC: ""
LONGDESC: "",
COLTYPE: "{\"data\":\"VAR_TXTO\"}"
}
],
dqdata: [
@@ -1445,7 +1484,6 @@ let webouts = {
DTVARS: "",
DTTMVARS: "",
TMVARS: "",
COLTYPE: "{\"data\":\"_____DELETE__THIS__RECORD_____\",\"type\":\"dropdown\",\"source\":[\"No\",\"Yes\"]},{\"data\":\"LIBREF\"},{\"data\":\"DSN\"},{\"data\":\"NUM_OF_APPROVALS_REQUIRED\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"LOADTYPE\"},{\"data\":\"BUSKEY\"},{\"data\":\"VAR_TXFROM\"},{\"data\":\"VAR_TXTO\"},{\"data\":\"VAR_BUSFROM\"},{\"data\":\"VAR_BUSTO\"},{\"data\":\"VAR_PROCESSED\"},{\"data\":\"CLOSE_VARS\"},{\"data\":\"PRE_EDIT_HOOK\"},{\"data\":\"POST_EDIT_HOOK\"},{\"data\":\"PRE_APPROVE_HOOK\"},{\"data\":\"POST_APPROVE_HOOK\"},{\"data\":\"SIGNOFF_COLS\"},{\"data\":\"SIGNOFF_HOOK\"},{\"data\":\"NOTES\"},{\"data\":\"RK_UNDERLYING\"},{\"data\":\"AUDIT_LIBDS\"}",
LOADTYPE: "TXTEMPORAL",
RK_FLAG: 0,
CLS_FLAG: 0
@@ -1499,11 +1537,7 @@ function stripRuleCols(t) {
})
t.sasparams = t.sasparams.map((p) => {
const headers = p.COLHEADERS.split(',').filter((h) => !uc.has(h))
const coltype = JSON.parse('[' + p.COLTYPE + ']')
.filter((o) => !uc.has(o.data))
.map((o) => JSON.stringify(o))
.join(',')
return { ...p, COLHEADERS: headers.join(','), COLTYPE: coltype }
return { ...p, COLHEADERS: headers.join(',') }
})
return t
}