1 | |
2 |
|
3 | function authHeader(auth: RT.Servicenow) { |
4 | return auth.token |
5 | ? `Bearer ${auth.token}` |
6 | : `Basic ${btoa(`${auth.username}:${auth.password}`)}` |
7 | } |
8 |
|
9 | |
10 | function value(field: any) { |
11 | return field != null && typeof field === "object" ? field.value : field |
12 | } |
13 |
|
14 | function label(field: any) { |
15 | return field != null && typeof field === "object" |
16 | ? field.display_value |
17 | : field |
18 | } |
19 |
|
20 | |
21 | * Create Change Request |
22 | * Create a normal, emergency or standard change request through the Change Management API (/api/sn_chg_rest, documented in the Yokohama and Brazil API references; needs the itil, change_manager or admin role). A standard change takes the sys_id of a standard change template (std_change_record_producer), whose description and plans cannot be overridden. Reference fields (assignment_group, cmdb_ci) take sys_ids; dates use 'YYYY-MM-DD HH:MM:SS' in UTC. Returns sys_id, number, state and the full record; fields ServiceNow did not apply are listed in ignored_fields. |
23 | */ |
24 | export async function main( |
25 | auth: RT.Servicenow, |
26 | type: "normal" | "emergency" | "standard", |
27 | short_description: string, |
28 | standard_change_template_id: string | undefined, |
29 | description: string | undefined, |
30 | assignment_group: string | undefined, |
31 | cmdb_ci: string | undefined, |
32 | start_date: string | undefined, |
33 | end_date: string | undefined, |
34 | fields: { [key: string]: any } | undefined |
35 | ) { |
36 | let path: string |
37 | if (type === "standard") { |
38 | if (!standard_change_template_id) { |
39 | throw new Error( |
40 | "standard_change_template_id is required for a standard change" |
41 | ) |
42 | } |
43 | path = `change/standard/${encodeURIComponent(standard_change_template_id)}` |
44 | } else if (type === "normal" || type === "emergency") { |
45 | path = `change/${type}` |
46 | } else { |
47 | throw new Error(`Unknown change type: ${type}`) |
48 | } |
49 |
|
50 | |
51 | const url = new URL(`${auth.instance_url}/api/sn_chg_rest/${path}`) |
52 | const values: { [key: string]: any } = { |
53 | ...(fields ?? {}), |
54 | short_description, |
55 | description, |
56 | assignment_group, |
57 | cmdb_ci, |
58 | start_date, |
59 | end_date, |
60 | } |
61 | for (const [key, v] of Object.entries(values)) { |
62 | if (v != null && v !== "") { |
63 | url.searchParams.append( |
64 | key, |
65 | typeof v === "object" ? JSON.stringify(v) : String(v) |
66 | ) |
67 | } |
68 | } |
69 |
|
70 | const response = await fetch(url, { |
71 | method: "POST", |
72 | headers: { |
73 | Authorization: authHeader(auth), |
74 | Accept: "application/json", |
75 | }, |
76 | }) |
77 |
|
78 | if (!response.ok) { |
79 | throw new Error(`${response.status} ${await response.text()}`) |
80 | } |
81 |
|
82 | const { result } = (await response.json()) as { result: any } |
83 | return { |
84 | sys_id: value(result.sys_id), |
85 | number: value(result.number), |
86 | state: value(result.state), |
87 | state_label: label(result.state), |
88 | ignored_fields: result.__meta?.ignoredFields ?? [], |
89 | record: result, |
90 | } |
91 | } |
92 |
|