- bms-logging.sh: fixed brace expansion bug, JSON compaction, dynamic log path, single-line JSONL entries
- bms-tickets.sh: support {success:true,result:{...}} responses; fixed ID extraction for create/note; updated update fetch
Tested full lifecycle with logging: create → note → delete note → close → delete
570 lines
22 KiB
Bash
570 lines
22 KiB
Bash
#!/usr/bin/env bash
|
|
# bms-tickets.sh — Kaseya BMS ticket CRUD operations
|
|
|
|
set -euo pipefail
|
|
|
|
# Import logging
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "${SCRIPT_DIR}/bms-logging.sh"
|
|
|
|
BMS_API_BASE="${BMS_API_BASE:-https://api.bms.kaseya.com}"
|
|
|
|
# ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
die() { echo "ERROR: $*" >&2; exit 1; }
|
|
|
|
get_token() {
|
|
bash "${SCRIPT_DIR}/bms-auth.sh" get-token
|
|
}
|
|
|
|
bms_curl() {
|
|
local method="$1"; shift
|
|
local path="$1"; shift
|
|
local token
|
|
token=$(get_token)
|
|
|
|
curl -sf -X "$method" \
|
|
"${BMS_API_BASE}${path}" \
|
|
-H "Authorization: Bearer ${token}" \
|
|
-H "Content-Type: application/json" \
|
|
"$@"
|
|
}
|
|
|
|
# Pretty-print a ticket list
|
|
format_ticket_list() {
|
|
jq -r '
|
|
(.result // .) |
|
|
if type == "array" then .[] else empty end |
|
|
"\(.ticketNumber // .Id)\t[\(.statusName // "?")] \(.title // "?")\t| \(.accountName // "?")\t| Assignee: \(.assigneeName // "unassigned")\t| Priority: \(.priorityName // "?")"
|
|
' | sed 's/\t/ /g'
|
|
}
|
|
|
|
format_ticket_detail() {
|
|
jq -r '
|
|
.Data // . |
|
|
"Ticket: \(.TicketNumber) (ID: \(.Id))
|
|
Title: \(.Title)
|
|
Status: \(.StatusName) (ID: \(.StatusId))
|
|
Priority: \(.PriorityName) (ID: \(.PriorityId))
|
|
Account: \(.AccountName) (ID: \(.AccountId))
|
|
Location: \(.LocationName // "N/A")
|
|
Contact: \(.ContactName // "N/A")
|
|
Queue: \(.QueueName // "N/A")
|
|
Assignee: \(.AssigneeName // "unassigned")
|
|
Type: \(.TypeName // "N/A")
|
|
Created: \(.CreatedOn)
|
|
Modified: \(.ModifiedOn)
|
|
Due: \(.DueDate // "none")
|
|
---
|
|
\(.Details // "(no details)")"
|
|
'
|
|
}
|
|
|
|
# ─── Commands ────────────────────────────────────────────────────────────────
|
|
|
|
cmd_list() {
|
|
local status="" assignee="" from="" to="" priority="" queue="" account=""
|
|
local page=1 page_size=25 format="table"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--status) status="$2"; shift 2 ;;
|
|
--assignee) assignee="$2"; shift 2 ;;
|
|
--from) from="$2"; shift 2 ;;
|
|
--to) to="$2"; shift 2 ;;
|
|
--priority) priority="$2"; shift 2 ;;
|
|
--queue) queue="$2"; shift 2 ;;
|
|
--account) account="$2"; shift 2 ;;
|
|
--page) page="$2"; shift 2 ;;
|
|
--page-size) page_size="$2"; shift 2 ;;
|
|
--format) format="$2"; shift 2 ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
# Build filter JSON
|
|
local filter="{"
|
|
local sep=""
|
|
if [[ "$status" == "open" ]]; then
|
|
filter+="\"StatusNames\":\"Escalated,Open,Waiting for Customer,Waiting for Product(s),Waiting for Vendor\""
|
|
sep=","
|
|
status=""
|
|
fi
|
|
[[ -n "$status" ]] && { filter+="${sep}\"StatusNames\":\"${status}\""; sep=","; }
|
|
[[ -n "$assignee" ]] && { filter+="${sep}\"AssigneeName\":\"${assignee}\""; sep=","; }
|
|
[[ -n "$from" ]] && { filter+="${sep}\"CreatedOnFrom\":\"${from}T00:00:00\""; sep=","; }
|
|
[[ -n "$to" ]] && { filter+="${sep}\"CreatedOnTo\":\"${to}T23:59:59\""; sep=","; }
|
|
[[ -n "$priority" ]] && { filter+="${sep}\"PriorityNames\":\"${priority}\""; sep=","; }
|
|
[[ -n "$queue" ]] && { filter+="${sep}\"QueueNames\":\"${queue}\""; sep=","; }
|
|
[[ -n "$account" ]] && { filter+="${sep}\"Account\":\"${account}\""; sep=","; }
|
|
filter+="}"
|
|
|
|
local body
|
|
body=$(jq -n \
|
|
--argjson filter "$filter" \
|
|
--argjson page "$page" \
|
|
--argjson page_size "$page_size" \
|
|
'{Filter: $filter, PageNumber: $page, PageSize: $page_size}')
|
|
|
|
local response
|
|
response=$(bms_curl POST "/v2/servicedesk/tickets/search" -d "$body")
|
|
|
|
if [[ "$format" == "json" ]]; then
|
|
echo "$response" | jq .
|
|
else
|
|
local total
|
|
total=$(echo "$response" | jq -r '.TotalCount // .Total // "?"')
|
|
echo "Tickets (page ${page}, ${page_size} per page, total: ${total}):" >&2
|
|
echo "$response" | format_ticket_list
|
|
fi
|
|
}
|
|
|
|
cmd_get() {
|
|
local ticket_id="${1:-}"
|
|
local format="${2:-table}"
|
|
[[ -n "$ticket_id" ]] || die "Usage: bms tickets get <ticketId>"
|
|
|
|
local response
|
|
response=$(bms_curl GET "/v2/servicedesk/tickets/${ticket_id}")
|
|
|
|
if [[ "$format" == "json" ]] || [[ "${2:-}" == "--json" ]]; then
|
|
echo "$response" | jq .
|
|
else
|
|
echo "$response" | format_ticket_detail
|
|
fi
|
|
}
|
|
|
|
# Prompt for a value if empty; first arg is field label, second is current value (by nameref)
|
|
# Usage: prompt_if_empty "Label" varname
|
|
prompt_if_empty() {
|
|
local label="$1"
|
|
local -n _ref="$2"
|
|
if [[ -z "${_ref:-}" ]]; then
|
|
read -r -p "${label}: " _ref
|
|
fi
|
|
}
|
|
|
|
cmd_create() {
|
|
local title="" details="" account_id="" location_id="" contact_id=""
|
|
local status_id="" priority_id="" type_id="" source_id=""
|
|
local assignee_id="" queue_id="" due_date="" open_date=""
|
|
local template_id=""
|
|
local interactive=false
|
|
local response="" ticket_id="" ticket_number="" success=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--template-id) template_id="$2"; shift 2 ;;
|
|
--title) title="$2"; shift 2 ;;
|
|
--details|--description) details="$2"; shift 2 ;;
|
|
--account-id) account_id="$2"; shift 2 ;;
|
|
--location-id) location_id="$2"; shift 2 ;;
|
|
--contact-id) contact_id="$2"; shift 2 ;;
|
|
--status-id) status_id="$2"; shift 2 ;;
|
|
--priority-id) priority_id="$2"; shift 2 ;;
|
|
--type-id) type_id="$2"; shift 2 ;;
|
|
--source-id) source_id="$2"; shift 2 ;;
|
|
--assignee-id) assignee_id="$2"; shift 2 ;;
|
|
--queue-id) queue_id="$2"; shift 2 ;;
|
|
--due-date) due_date="$2"; shift 2 ;;
|
|
--interactive) interactive=true; shift ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
# ── Template pre-fill ────────────────────────────────────────────────────
|
|
if [[ -n "$template_id" ]]; then
|
|
echo "Fetching template ${template_id}..." >&2
|
|
local tmpl
|
|
tmpl=$(bms_curl GET "/v2/servicedesk/templates/tickets/${template_id}" | jq '.Data // .')
|
|
|
|
# Fill only fields not already set by CLI flags
|
|
[[ -z "$title" ]] && title=$(echo "$tmpl" | jq -r '.Title // empty')
|
|
[[ -z "$details" ]] && details=$(echo "$tmpl" | jq -r '.Details // empty')
|
|
[[ -z "$status_id" ]] && status_id=$(echo "$tmpl" | jq -r '.StatusId // empty')
|
|
[[ -z "$priority_id" ]] && priority_id=$(echo "$tmpl" | jq -r '.PriorityId // empty')
|
|
[[ -z "$type_id" ]] && type_id=$(echo "$tmpl" | jq -r '(.TypeId // .IssueTypeId) // empty')
|
|
[[ -z "$source_id" ]] && source_id=$(echo "$tmpl" | jq -r '.SourceId // empty')
|
|
[[ -z "$queue_id" ]] && queue_id=$(echo "$tmpl" | jq -r '.QueueId // empty')
|
|
[[ -z "$assignee_id" ]] && assignee_id=$(echo "$tmpl" | jq -r '.AssigneeId // empty')
|
|
[[ -z "$account_id" ]] && account_id=$(echo "$tmpl" | jq -r '.AccountId // empty')
|
|
[[ -z "$location_id" ]] && location_id=$(echo "$tmpl" | jq -r '.LocationId // empty')
|
|
[[ -z "$contact_id" ]] && contact_id=$(echo "$tmpl" | jq -r '.ContactId // empty')
|
|
fi
|
|
|
|
# ── Interactive prompts ───────────────────────────────────────────────────
|
|
if $interactive; then
|
|
prompt_if_empty "Title" title
|
|
prompt_if_empty "Details" details
|
|
prompt_if_empty "Account ID" account_id
|
|
prompt_if_empty "Location ID" location_id
|
|
prompt_if_empty "Status ID" status_id
|
|
prompt_if_empty "Priority ID" priority_id
|
|
prompt_if_empty "Type ID" type_id
|
|
prompt_if_empty "Source ID" source_id
|
|
prompt_if_empty "Queue ID (optional if Assignee ID provided)" queue_id
|
|
if [[ -z "$queue_id" ]]; then
|
|
prompt_if_empty "Assignee ID (optional if Queue ID provided)" assignee_id
|
|
fi
|
|
elif [[ -n "$template_id" ]]; then
|
|
# When using a template, prompt only for fields still missing that are required
|
|
[[ -n "$title" ]] || { read -r -p "Title: " title; }
|
|
[[ -n "$details" ]] || { read -r -p "Details: " details; }
|
|
[[ -n "$account_id" ]] || { read -r -p "Account ID: " account_id; }
|
|
[[ -n "$location_id" ]] || { read -r -p "Location ID: " location_id; }
|
|
[[ -n "$status_id" ]] || { read -r -p "Status ID: " status_id; }
|
|
[[ -n "$priority_id" ]] || { read -r -p "Priority ID: " priority_id; }
|
|
[[ -n "$type_id" ]] || { read -r -p "Type ID: " type_id; }
|
|
[[ -n "$source_id" ]] || { read -r -p "Source ID: " source_id; }
|
|
if [[ -z "$queue_id" && -z "$assignee_id" ]]; then
|
|
read -r -p "Queue ID (or leave blank to provide Assignee ID): " queue_id
|
|
if [[ -z "$queue_id" ]]; then
|
|
read -r -p "Assignee ID: " assignee_id
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
[[ -n "$title" ]] || die "Missing required field: --title"
|
|
[[ -n "$details" ]] || die "Missing required field: --details"
|
|
[[ -n "$account_id" ]] || die "Missing required field: --account-id"
|
|
[[ -n "$location_id" ]] || die "Missing required field: --location-id"
|
|
[[ -n "$status_id" ]] || die "Missing required field: --status-id"
|
|
[[ -n "$priority_id" ]] || die "Missing required field: --priority-id"
|
|
[[ -n "$type_id" ]] || die "Missing required field: --type-id"
|
|
[[ -n "$source_id" ]] || die "Missing required field: --source-id"
|
|
[[ -n "$queue_id" || -n "$assignee_id" ]] || die "Missing required routing: provide either --queue-id or --assignee-id"
|
|
|
|
open_date="${open_date:-$(date -u +%Y-%m-%dT%H:%M:%S)}"
|
|
|
|
local body
|
|
body=$(jq -n \
|
|
--arg title "$title" \
|
|
--arg details "$details" \
|
|
--argjson account_id "$account_id" \
|
|
--argjson location_id "$location_id" \
|
|
--argjson status_id "$status_id" \
|
|
--argjson priority_id "$priority_id" \
|
|
--argjson type_id "$type_id" \
|
|
--argjson source_id "$source_id" \
|
|
--arg open_date "$open_date" \
|
|
'{
|
|
Title: $title,
|
|
Details: $details,
|
|
AccountId: $account_id,
|
|
LocationId: $location_id,
|
|
StatusId: $status_id,
|
|
PriorityId: $priority_id,
|
|
TypeId: $type_id,
|
|
SourceId: $source_id,
|
|
OpenDate: $open_date
|
|
}')
|
|
|
|
# Optionally add non-required fields
|
|
[[ -n "$contact_id" ]] && body=$(echo "$body" | jq --argjson v "$contact_id" '. + {ContactId: $v}')
|
|
[[ -n "$assignee_id" ]] && body=$(echo "$body" | jq --argjson v "$assignee_id" '. + {AssigneeId: $v}')
|
|
[[ -n "$queue_id" ]] && body=$(echo "$body" | jq --argjson v "$queue_id" '. + {QueueId: $v}')
|
|
[[ -n "$due_date" ]] && body=$(echo "$body" | jq --arg v "$due_date" '. + {DueDate: $v}')
|
|
|
|
# Single create call only. No retries here.
|
|
response=$(bms_curl POST "/v2/servicedesk/tickets" -d "$body")
|
|
|
|
success=$(echo "$response" | jq -r '.success // .Success // empty')
|
|
ticket_id=$(echo "$response" | jq -r '.Data.Id // .Id // .result.id // .result.id // empty')
|
|
ticket_number=$(echo "$response" | jq -r '.Data.TicketNumber // .TicketNumber // .result.ticketNumber // empty')
|
|
|
|
if [[ "$success" != "true" ]] || [[ -z "$ticket_id" ]] || [[ "$ticket_id" == "null" ]]; then
|
|
echo "Create ticket failed or returned ambiguous response:" >&2
|
|
echo "$response" | jq . >&2
|
|
# Log failure
|
|
local args_json result_json
|
|
args_json=$(jq -n \
|
|
--arg title "$title" \
|
|
--arg details "$details" \
|
|
--argjson account_id "$account_id" \
|
|
--argjson location_id "$location_id" \
|
|
--argjson status_id "$status_id" \
|
|
--argjson priority_id "$priority_id" \
|
|
--argjson type_id "$type_id" \
|
|
--argjson source_id "$source_id" \
|
|
--argjson queue_id "${queue_id:-null}" \
|
|
--argjson assignee_id "${assignee_id:-null}" \
|
|
'{title: $title, details: $details, account_id: $account_id, location_id: $location_id, status_id: $status_id, priority_id: $priority_id, type_id: $type_id, source_id: $source_id, queue_id: $queue_id, assignee_id: $assignee_id}')
|
|
result_json=$(jq -n '{error: "creation_failed", response: ("$response" | fromjson? // "$response")}')
|
|
log_action "tickets.create" "$args_json" "$result_json" "error"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Created ticket ID: ${ticket_id} — ${ticket_number:-N/A}"
|
|
# Log success
|
|
local args_json result_json
|
|
args_json=$(jq -n \
|
|
--arg title "$title" \
|
|
--arg details "$details" \
|
|
--argjson account_id "$account_id" \
|
|
--argjson location_id "$location_id" \
|
|
--argjson status_id "$status_id" \
|
|
--argjson priority_id "$priority_id" \
|
|
--argjson type_id "$type_id" \
|
|
--argjson source_id "$source_id" \
|
|
--argjson queue_id "${queue_id:-null}" \
|
|
--argjson assignee_id "${assignee_id:-null}" \
|
|
'{title: $title, details: $details, account_id: $account_id, location_id: $location_id, status_id: $status_id, priority_id: $priority_id, type_id: $type_id, source_id: $source_id, queue_id: $queue_id, assignee_id: $assignee_id}')
|
|
result_json=$(jq -n --argjson tid "$ticket_id" --arg tn "${ticket_number:-}" '{ticket_id: $tid, ticket_number: $tn}')
|
|
log_action "tickets.create" "$args_json" "$result_json" "success"
|
|
|
|
}
|
|
|
|
cmd_update() {
|
|
local ticket_id="${1:-}"
|
|
[[ -n "$ticket_id" ]] || die "Usage: bms tickets update <ticketId> [options]"
|
|
shift
|
|
|
|
# Fetch current ticket first so we can do a full PUT with changes merged
|
|
local current
|
|
current=$(bms_curl GET "/v2/servicedesk/tickets/${ticket_id}" | jq '.Data // .result // .')
|
|
|
|
local patch="{}"
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--title) patch=$(echo "$patch" | jq --arg v "$2" '. + {Title: $v}'); shift 2 ;;
|
|
--details) patch=$(echo "$patch" | jq --arg v "$2" '. + {Details: $v}'); shift 2 ;;
|
|
--status-id) patch=$(echo "$patch" | jq --argjson v "$2" '. + {StatusId: $v}'); shift 2 ;;
|
|
--priority-id) patch=$(echo "$patch" | jq --argjson v "$2" '. + {PriorityId: $v}'); shift 2 ;;
|
|
--assignee-id) patch=$(echo "$patch" | jq --argjson v "$2" '. + {AssigneeId: $v}'); shift 2 ;;
|
|
--queue-id) patch=$(echo "$patch" | jq --argjson v "$2" '. + {QueueId: $v}'); shift 2 ;;
|
|
--due-date) patch=$(echo "$patch" | jq --arg v "$2" '. + {DueDate: $v}'); shift 2 ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
# Merge patch onto current (keep required fields from current ticket)
|
|
local body
|
|
body=$(echo "$current" | jq \
|
|
--argjson patch "$patch" \
|
|
'{
|
|
Title: .Title,
|
|
Details: .Details,
|
|
AccountId: .AccountId,
|
|
LocationId: .LocationId,
|
|
StatusId: .StatusId,
|
|
PriorityId: .PriorityId,
|
|
TypeId: .TypeId,
|
|
SourceId: .SourceId,
|
|
OpenDate: .OpenDate,
|
|
AssigneeId: .AssigneeId,
|
|
QueueId: .QueueId,
|
|
ContactId: .ContactId
|
|
} * $patch')
|
|
|
|
local response
|
|
response=$(bms_curl PUT "/v2/servicedesk/tickets/${ticket_id}" -d "$body")
|
|
echo "$response" | jq -r '"Updated ticket \(.Data.Id // .Id // .result.id // "'"$ticket_id"'")"'
|
|
# Log success
|
|
local args_json result_json
|
|
args_json=$(jq -n --argjson ticket_id "$ticket_id" --argjson patch "$patch" '{ticket_id: $ticket_id, patch: $patch}')
|
|
result_json=$(jq -n --argjson tid "$ticket_id" '{ticket_id: $tid}')
|
|
log_action "tickets.update" "$args_json" "$result_json" "success"
|
|
}
|
|
|
|
cmd_note() {
|
|
local ticket_id="${1:-}"
|
|
[[ -n "$ticket_id" ]] || die "Usage: bms tickets note <ticketId> --message <text> [options]"
|
|
shift
|
|
|
|
local message="" is_internal=false type_id=1 status_id="" note_date=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--message|-m) message="$2"; shift 2 ;;
|
|
--internal) is_internal=true; shift ;;
|
|
--type-id) type_id="$2"; shift 2 ;;
|
|
--status-id) status_id="$2"; shift 2 ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$message" ]] || die "--message is required"
|
|
note_date=$(date -u +%Y-%m-%dT%H:%M:%S)
|
|
|
|
local body
|
|
body=$(jq -n \
|
|
--arg details "$message" \
|
|
--argjson is_internal "$is_internal" \
|
|
--argjson type_id "$type_id" \
|
|
--arg note_date "$note_date" \
|
|
'{
|
|
Details: $details,
|
|
IsInternal: $is_internal,
|
|
TypeId: $type_id,
|
|
NoteDate: $note_date
|
|
}')
|
|
|
|
[[ -n "$status_id" ]] && body=$(echo "$body" | jq --argjson v "$status_id" '. + {StatusId: $v}')
|
|
|
|
local response
|
|
response=$(bms_curl POST "/v2/servicedesk/tickets/${ticket_id}/notes" -d "$body")
|
|
local note_id
|
|
note_id=$(echo "$response" | jq -r '.Data.Id // .Id // .result.id // .result.id // empty')
|
|
if [[ -z "$note_id" || "$note_id" == "null" ]]; then
|
|
echo "Note add failed or returned ambiguous response:" >&2
|
|
echo "$response" | jq . >&2
|
|
# Log failure
|
|
local args_json result_json
|
|
args_json=$(jq -n --argjson ticket_id "$ticket_id" --arg message "$message" --argjson type_id "$type_id" '{ticket_id: $ticket_id, message: $message, type_id: $type_id}')
|
|
result_json=$(jq -n '{error: "note_add_failed", response: ("$response" | fromjson? // "$response")}')
|
|
log_action "tickets.note" "$args_json" "$result_json" "error"
|
|
exit 1
|
|
fi
|
|
echo "Note added (ID: ${note_id})"
|
|
# Log success
|
|
local args_json result_json
|
|
args_json=$(jq -n --argjson ticket_id "$ticket_id" --arg message "$message" --argjson type_id "$type_id" '{ticket_id: $ticket_id, message: $message, type_id: $type_id}')
|
|
result_json=$(jq -n --argjson note_id "$note_id" '{note_id: $note_id}')
|
|
log_action "tickets.note" "$args_json" "$result_json" "success"
|
|
|
|
}
|
|
|
|
cmd_assign() {
|
|
local ticket_id="${1:-}"
|
|
[[ -n "$ticket_id" ]] || die "Usage: bms tickets assign <ticketId> [options]"
|
|
shift
|
|
|
|
local assignee_id="" queue_id="" note="" is_internal=false type_id=1 status_id=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--assignee-id) assignee_id="$2"; shift 2 ;;
|
|
--queue-id) queue_id="$2"; shift 2 ;;
|
|
--note|-n) note="$2"; shift 2 ;;
|
|
--internal) is_internal=true; shift ;;
|
|
--type-id) type_id="$2"; shift 2 ;;
|
|
--status-id) status_id="$2"; shift 2 ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$note" ]] || note="Ticket reassigned."
|
|
local note_date
|
|
note_date=$(date -u +%Y-%m-%dT%H:%M:%S)
|
|
|
|
local body
|
|
body=$(jq -n \
|
|
--arg details "$note" \
|
|
--argjson is_internal "$is_internal" \
|
|
--argjson type_id "$type_id" \
|
|
--arg note_date "$note_date" \
|
|
'{
|
|
Details: $details,
|
|
IsInternal: $is_internal,
|
|
TypeId: $type_id,
|
|
NoteDate: $note_date
|
|
}')
|
|
|
|
[[ -n "$status_id" ]] && body=$(echo "$body" | jq --argjson v "$status_id" '. + {StatusId: $v}')
|
|
[[ -n "$assignee_id" ]] && body=$(echo "$body" | jq --argjson v "$assignee_id" '. + {AssigneeId: $v}')
|
|
[[ -n "$queue_id" ]] && body=$(echo "$body" | jq --argjson v "$queue_id" '. + {QueueId: $v}')
|
|
|
|
local response
|
|
response=$(bms_curl POST "/v2/servicedesk/tickets/${ticket_id}/assignticket" -d "$body")
|
|
echo "$response" | jq -r '"Ticket \("'"$ticket_id"'") assigned."'
|
|
# Log success
|
|
local args_json result_json
|
|
args_json=$(jq -n --argjson ticket_id "$ticket_id" --argjson assignee_id "${assignee_id:-null}" --argjson queue_id "${queue_id:-null}" '{ticket_id: $ticket_id, assignee_id: $assignee_id, queue_id: $queue_id}')
|
|
result_json=$(jq -n --argjson tid "$ticket_id" '{ticket_id: $tid}')
|
|
log_action "tickets.assign" "$args_json" "$result_json" "success"
|
|
}
|
|
|
|
cmd_resolve() {
|
|
local ticket_id="${1:-}"
|
|
[[ -n "$ticket_id" ]] || die "Usage: bms tickets resolve <ticketId> --comment <text> [options]"
|
|
shift
|
|
|
|
local comment="" status_id="" publish_kb=false is_internal=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--comment|-c) comment="$2"; shift 2 ;;
|
|
--status-id) status_id="$2"; shift 2 ;;
|
|
--publish-kb) publish_kb=true; shift ;;
|
|
--internal) is_internal=true; shift ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$comment" ]] || die "--comment is required"
|
|
|
|
local body
|
|
body=$(jq -n \
|
|
--arg comment "$comment" \
|
|
--argjson publish_kb "$publish_kb" \
|
|
--argjson is_internal "$is_internal" \
|
|
'{Comment: $comment, IsPublishToKnowledgeBase: $publish_kb, IsInternal: $is_internal}')
|
|
|
|
[[ -n "$status_id" ]] && body=$(echo "$body" | jq --argjson v "$status_id" '. + {StatusId: $v}')
|
|
|
|
local response
|
|
response=$(bms_curl POST "/v2/servicedesk/tickets/${ticket_id}/resolve" -d "$body")
|
|
echo "$response" | jq -r '"Ticket \("'"$ticket_id"'") resolved."'
|
|
# Log success
|
|
local args_json result_json
|
|
args_json=$(jq -n --argjson ticket_id "$ticket_id" --argjson comment "$comment" '{ticket_id: $ticket_id, comment: $comment}')
|
|
result_json=$(jq -n --argjson tid "$ticket_id" '{ticket_id: $tid}')
|
|
log_action "tickets.resolve" "$args_json" "$result_json" "success"
|
|
}
|
|
|
|
cmd_delete() {
|
|
local ids=("$@")
|
|
[[ ${#ids[@]} -gt 0 ]] || die "Usage: bms tickets delete <id> [id2 ...]"
|
|
|
|
local response=""
|
|
if [[ ${#ids[@]} -eq 1 ]]; then
|
|
response=$(bms_curl DELETE "/v2/servicedesk/tickets/${ids[0]}")
|
|
echo "Deleted ticket ${ids[0]}"
|
|
else
|
|
local body
|
|
body=$(printf '%s\n' "${ids[@]}" | jq -Rs 'split("\n") | map(select(. != "")) | map(tonumber) | {Ids: .}')
|
|
response=$(bms_curl DELETE "/v2/servicedesk/tickets" -d "$body")
|
|
echo "Deleted tickets: ${ids[*]}"
|
|
fi
|
|
|
|
# Check success (DELETE often returns { success: true } or empty)
|
|
local success
|
|
success=$(echo "$response" | jq -r '.success // .Success // ""')
|
|
if [[ "$success" != "true" ]] && [[ -n "$response" ]] && echo "$response" | jq -e . >/dev/null 2>&1; then
|
|
# If response is JSON but not success=true, treat as failure
|
|
echo "Delete operation may have failed:" >&2
|
|
echo "$response" | jq . >&2
|
|
local args_json result_json
|
|
args_json=$(jq -n --argjson ids "${ids}" '{ids: $ids}')
|
|
result_json=$(jq -n '{error: "delete_failed", response: ("$response" | fromjson? // "$response")}')
|
|
log_action "tickets.delete" "$args_json" "$result_json" "error"
|
|
exit 1
|
|
fi
|
|
|
|
# Log success
|
|
local args_json result_json
|
|
args_json=$(jq -n --argjson ids "${ids}" '{ids: $ids}')
|
|
result_json=$(jq -n --argjson deleted_ids "${ids}" '{deleted_ids: $deleted_ids}')
|
|
log_action "tickets.delete" "$args_json" "$result_json" "success"
|
|
}
|
|
|
|
# ─── Dispatch ────────────────────────────────────────────────────────────────
|
|
|
|
subcmd="${1:-list}"
|
|
[[ $# -gt 0 ]] && shift
|
|
|
|
case "$subcmd" in
|
|
list) cmd_list "$@" ;;
|
|
get) cmd_get "$@" ;;
|
|
create) cmd_create "$@" ;;
|
|
update) cmd_update "$@" ;;
|
|
note) cmd_note "$@" ;;
|
|
assign) cmd_assign "$@" ;;
|
|
resolve) cmd_resolve "$@" ;;
|
|
delete) cmd_delete "$@" ;;
|
|
*)
|
|
echo "Usage: bms tickets <list|get|create|update|note|assign|resolve|delete>" >&2
|
|
exit 1
|
|
;;
|
|
esac
|