import { CallExpressionShape, Plugin, isGUID, type Shape, type ShapeTransform } from '@servicenow/sdk-build-core' import { NowIdShape } from './now-id-plugin' import { NowIncludeShape } from './now-include-plugin' import { ModuleFunctionShape } from './server-module-plugin' import { showGuidFieldDiagnostic } from './utils' import { parseString, convertRolesToString } from './service-catalog/utils' /** * Plugin for handling Inbound Email Action records (sysevent_in_email_action). * * Default Behavior: * - When reading from XML: If 'action' field is missing or empty, defaults to 'record_action' * - When writing to Fluent: The 'action' field is required in the type definition for proper * discriminated union type narrowing */ export const InboundEmailActionPlugin = Plugin.create({ name: 'InboundEmailActionPlugin', records: { sysevent_in_email_action: { async toShape(record, { transform }) { const actionType = record.get('action').getValue() || 'record_action' // Process script const script = await NowIncludeShape.fromRecord(record, record.get('script'), transform) return { success: true, value: new CallExpressionShape({ source: record, callee: 'InboundEmailAction', args: [ record.transform(({ $ }) => { const props: Record = { $id: $.val(NowIdShape.from(record)), name: $, description: $.def(''), type: $.def('new'), action: $.val(actionType), active: $.toBoolean().def(false), order: $.toNumber().def(100), eventName: $.from('event_name').def('email.read'), stopProcessing: $.from('stop_processing').toBoolean().def(false), conditionScript: $.from('condition_script').def(''), filterCondition: $.from('filter_condition').def(''), from: $.def(''), requiredRoles: $.from('required_roles') .map((v: Shape) => parseString(v)) .def([]), } props['replyEmail'] = $.from('reply_email').def('') props['table'] = $.def('') props['script'] = $.val(script).def('') props['fieldAction'] = $.from('template').def('') props['assignmentOperator'] = $.from('assignment_operator').def('') return props }), ], }), } }, }, }, shapes: [ { shape: CallExpressionShape, fileTypes: ['fluent'], async toRecord(callExpression, { factory, diagnostics }) { if (callExpression.getCallee() !== 'InboundEmailAction') { return { success: false } } const args = callExpression.getArgument(0).asObject() // Get action type to validate field restrictions const actionArg = args.get('action') const actionType = actionArg?.getValue() ?? 'record_action' // Validate that fieldAction can only be used when table is set (for record_action) if (actionType === 'record_action') { const tableArg = args.get('table') const fieldActionArg = args.get('fieldAction') const hasTable = tableArg?.isDefined() && !tableArg.toString().isEmpty() const hasFieldAction = fieldActionArg?.isDefined() && !fieldActionArg.toString().isEmpty() if (hasFieldAction && !hasTable) { diagnostics.error( fieldActionArg, `Field 'fieldAction' can only be used when 'table' is specified.` ) } } // Validate the 'from' field: must be a GUID string or a Record<'sys_user'> const fromArg = args.get('from') if (fromArg?.isDefined()) { // Validate that if it's a string, it must be a valid GUID if (fromArg.isString() && !fromArg.isRecord()) { const fromStr = fromArg.asString().getValue() if (fromStr !== '' && !isGUID(fromStr)) { showGuidFieldDiagnostic(fromArg, 'from', 'sys_user', diagnostics) } } } const record = await factory.createRecord({ source: callExpression, table: 'sysevent_in_email_action', explicitId: args.get('$id'), properties: args.transform(({ $ }) => ({ name: $, description: $.def(''), table: $, type: $.def('new'), action: $.def('record_action'), active: $.toBoolean().def(false), order: $.toNumber().def(100), event_name: $.from('eventName').def('email.read'), stop_processing: $.from('stopProcessing').toBoolean().def(false), script: $.map( (v: Shape) => v .if(ModuleFunctionShape) ?.toString( (n) => `${n}({{PARAMS}})`, ['current', 'event', 'email', 'logger', 'classifier'] ) ?? v ).toCdata(), condition_script: $.from('conditionScript').toCdata().def(''), filter_condition: $.from('filterCondition').def(''), template: $.from('fieldAction').toCdata().def(''), from: $.toString(), reply_email: $.from('replyEmail').toCdata().def(''), required_roles: $.from('requiredRoles').map(convertRolesToString).def(''), assignment_operator: $.from('assignmentOperator').def(''), })), }) return { success: true, value: record } }, }, ], })