import { CallExpressionShape, Plugin, type Record, VariableStatementShape, IdentifierShape, } from '@servicenow/sdk-build-core' import { NowIdShape } from '../now-id-plugin' import { buildVariableRecords } from './shape-to-record' import { buildVariablesSchema } from './service-catalog-base' import { validateInternalName, convertTitleToInternalName, convertRolesToString, parseString, toValidIdentifier, shouldWriteAsCallExpression, convertToNumber, validateVariableSetVariables, getVariableSetTypeFromDb, getVariableSetTypeToDb, } from './utils' export const VariableSetPlugin = Plugin.create({ name: 'VariableSetPlugin', records: { item_option_new_set: { relationships: { item_option_new: { via: 'variable_set', descendant: true, relationships: { question_choice: { via: 'question', descendant: true, relationships: { fx_price: { via: 'id', descendant: true, }, }, }, fx_price: { via: 'id', descendant: true, }, }, }, }, toShape(record, { descendants, logger }) { const variablesSchema = buildVariablesSchema(descendants, logger, record.getId()?.getValue()) const callExpression = new CallExpressionShape({ source: record, callee: 'VariableSet', args: [ record.transform(({ $ }) => ({ $id: $.val(NowIdShape.from(record)), title: $.from('title'), internalName: $.from('internal_name'), name: $.from('name').def(''), description: $.from('description').def(''), type: $.from('type') .map((v) => getVariableSetTypeFromDb(v.toString().getValue())) .def('singleRow'), layout: $.from('layout').def('normal'), order: $.map((c) => convertToNumber(c, 100)).def(100), displayTitle: $.from('display_title').toBoolean().def(false), setAttributes: $.from('set_attributes').def(''), version: $.map((c) => convertToNumber(c, 1)).def(1), // Parse comma-separated role strings into arrays readRoles: $.from('read_roles') .map((v) => parseString(v)) .def([]), writeRoles: $.from('write_roles') .map((v) => parseString(v)) .def([]), createRoles: $.from('create_roles') .map((v) => parseString(v)) .def([]), variables: $.val(variablesSchema).def({}), })), ], }) return { success: true, value: shouldWriteAsCallExpression(record) ? callExpression : new VariableStatementShape({ source: record, isExported: true, variableName: new IdentifierShape({ source: record, name: toValidIdentifier(record.get('title').toString().getValue()), }), initializer: callExpression, }), } }, }, fx_price: { coalesce: ['id', 'field'], }, item_option_new: { coalesce: ['cat_item', 'variable_set', 'name'], }, question_choice: { coalesce: ['question', 'value'], }, }, shapes: [ { shape: CallExpressionShape, fileTypes: ['fluent'], async toRecord(callExpression, { diagnostics, factory }) { if (callExpression.getCallee() !== 'VariableSet') { return { success: false } } const arg = callExpression.getArgument(0).asObject() // Determine and validate internalName const providedInternalName = arg.get('internalName') let internalName: string if (providedInternalName.isDefined()) { // Use provided internalName and validate it internalName = providedInternalName.toString().getValue() const validation = validateInternalName(internalName) if (!validation.valid) { diagnostics.error(providedInternalName.getOriginalNode(), validation.error ?? '') return { success: false } } } else { // Auto-generate internalName from title (already validated format) const title = arg.get('title') internalName = convertTitleToInternalName(title.toString().getValue()) } // Get the variable set type for validation const variableSetType = arg.get('type').isDefined() ? arg.get('type').toString().getValue() : 'singleRow' // default value // Validate variables (RequestedForVariable count and multiRow restrictions) if (arg.get('variables').isDefined()) { const variablesConfig = arg.get('variables').asObject() if (!validateVariableSetVariables(variablesConfig, variableSetType, diagnostics, 'VariableSet')) { return { success: false } } } // Create the variable set record const variableSet = await factory.createRecord({ explicitId: arg.get('$id'), source: callExpression, table: 'item_option_new_set', properties: arg.transform(({ $ }) => ({ title: $, internal_name: $.val(internalName), name: $.def(''), description: $.def(''), type: $.from('type') .map((v) => getVariableSetTypeToDb(v.toString().getValue())) .def('one_to_one'), layout: $.def('normal'), order: $.def(100), display_title: $.from('displayTitle').toBoolean().def(false), set_attributes: $.from('setAttributes').def(''), version: $.def(1), // Convert role arrays to comma-separated strings for ServiceNow read_roles: $.from('readRoles').map(convertRolesToString).def(''), write_roles: $.from('writeRoles').map(convertRolesToString).def(''), create_roles: $.from('createRoles').map(convertRolesToString).def(''), })), }) // Build child variable records if variables are defined let variableRecords: Record[] = [] if (arg.get('variables').isDefined()) { const variablesConfig = arg.get('variables').asObject() variableRecords = await buildVariableRecords({ variablesConfig, factory, parent: variableSet, diagnostics, }) } return { success: true, value: variableSet.with(...variableRecords), } }, }, ], })