The fallback rewrite changed the published TYPE from C/N to the client's num/char, which is a breaking change to a public service. TYPE goes back to the SAS vocabulary it has always used, and the client maps it to its own num/char at the store boundary instead (sas-store.getCols). ddtype, fmtname, format and label are unchanged, so the payload is a superset of the old one. getcols.test now asserts the SAS contract (TYPE is C/N and agrees with ddtype) rather than the client vocabulary, and the mock mirrors the service.
81 lines
3.4 KiB
JavaScript
81 lines
3.4 KiB
JavaScript
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'
|
|
|
|
// Load shared DC mock utilities
|
|
eval(fs.readFileSync(nodePath.resolve(driveRoot, 'files', appLoc, 'services', 'dcMockUtils.js'), 'utf8'))
|
|
|
|
// ─── Parse input ──────────────────────────────────────────────────────────────
|
|
// getcols.sas reads %mf_getvalue(work.iwant,libds). The adapter serialises the
|
|
// input table as CSV, which fetchTable parses - so the row is `_iwantRow` (the
|
|
// runtime already owns the name `iwant`).
|
|
|
|
const _iwantRow = fetchTable('iwant')[0] || {}
|
|
const libds = String(_iwantRow.libds || _iwantRow.LIBDS || '').trim()
|
|
|
|
let libref = dcLibref
|
|
let table = ''
|
|
if (libds) {
|
|
const parts = libds.split('.')
|
|
if (parts.length >= 2) {
|
|
libref = parts[0].trim()
|
|
table = parts[1].trim()
|
|
}
|
|
}
|
|
|
|
// ─── Load the table schema ────────────────────────────────────────────────────
|
|
// Same resolution as getdata.js: the requested libref first, then any libref
|
|
// (a LIBDS that doesn't match the data folder still resolves).
|
|
|
|
let tableData = table ? makeTableLoader(libDataDir(libref))(table) : null
|
|
if (!tableData) {
|
|
// Fall back to searching every libref if the table isn't in the requested
|
|
// libref (e.g. a LIBDS that doesn't match the actual data folder), and take
|
|
// the libref that actually held it for the registry lookup below.
|
|
const found = loadTableAnyLib(table)
|
|
if (found) {
|
|
tableData = found.data
|
|
libref = found.libref
|
|
}
|
|
}
|
|
|
|
// ─── Build the column list ────────────────────────────────────────────────────
|
|
// Every column, as getdata.sas emits them: the temporal columns are dropped from
|
|
// the data, not from the cols payload, so a filter can still be built on them -
|
|
// which is exactly what "please filter and try again" wants to do.
|
|
|
|
let cols = []
|
|
|
|
if (tableData && tableData.columns) {
|
|
// mpGetcols, then the mapping getcols.sas applies on top of %mp_getcols:
|
|
// DDTYPE in the client's vocabulary, and TYPE as the SAS C/N it has always
|
|
// been. The client maps TYPE to its own num/char vocabulary in
|
|
// sas-store.getCols, because the picker keys its operator set and its value
|
|
// quoting on it and this list does not pass through mergeColsRules.
|
|
cols = mpGetcols(tableData.columns)
|
|
.map(c => {
|
|
const ddtype = c.DDTYPE === 'CHARACTER' ? 'C' : c.DDTYPE === 'NUMERIC' ? 'N' : c.DDTYPE
|
|
return {
|
|
NAME: c.NAME,
|
|
LABEL: c.LABEL,
|
|
FORMAT: c.FORMAT || ' ',
|
|
FMTNAME: c.FMTNAME || ' ',
|
|
DDTYPE: ddtype,
|
|
TYPE: ddtype === 'C' ? 'C' : 'N',
|
|
CLS_RULE: 'READ',
|
|
MEMLABEL: ' ',
|
|
DESC: ' ',
|
|
LONGDESC: ' ',
|
|
COLTYPE: ' '
|
|
}
|
|
})
|
|
.sort((a, b) => a.NAME.localeCompare(b.NAME))
|
|
}
|
|
|
|
webOutOpen()
|
|
webOutObj(cols, 'cols')
|
|
webOutClose()
|