@pipedream/servicenow 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
- // legacy_hash_id: a_4riE2J
2
- import { axios } from "@pipedream/platform";
1
+ import servicenow from "../../servicenow.app.mjs";
2
+ import { parseObject } from "../../common/utils.mjs";
3
3
 
4
4
  export default {
5
5
  key: "servicenow-update-table-record",
6
6
  name: "Update Table Record",
7
- description: "Updates the specified record with the name-value pairs included in the request body.",
8
- version: "0.1.2",
7
+ description: "Updates the specified record with the name-value pairs included in the request body. [See the documentation](https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html#title_table-PATCH)",
8
+ version: "1.0.0",
9
9
  annotations: {
10
10
  destructiveHint: true,
11
11
  openWorldHint: true,
@@ -13,123 +13,82 @@ export default {
13
13
  },
14
14
  type: "action",
15
15
  props: {
16
- servicenow: {
17
- type: "app",
18
- app: "servicenow",
19
- },
20
- table_name: {
21
- type: "string",
22
- description: "The name of the table containing the record to update.",
23
- },
24
- sys_id: {
25
- type: "string",
26
- description: "Unique identifier of the record to update.",
27
- },
28
- update_fields: {
29
- type: "object",
30
- description: "An object with name-value pairs with the fields to update in the specified record.\n**Note:** All fields within a record may not be available for update. For example, fields that have a prefix of \"sys_\" are typically system parameters that are automatically generated and cannot be updated.",
31
- },
32
- api_version: {
33
- type: "string",
34
- description: "API version number. Version numbers identify the endpoint version that a URI accesses. By specifying a version number in your URIs, you can ensure that future updates to the REST API do not negatively impact your integration. Use `lastest` to use the latest REST endpoint for your instance version.",
35
- optional: true,
36
- options: [
37
- "lastest",
38
- "v1",
39
- "v2",
16
+ servicenow,
17
+ table: {
18
+ propDefinition: [
19
+ servicenow,
20
+ "table",
40
21
  ],
41
22
  },
42
- request_format: {
43
- type: "string",
44
- description: "Format of REST request body",
45
- optional: true,
46
- options: [
47
- "application/json",
48
- "application/xml",
49
- "text/xml",
23
+ recordId: {
24
+ propDefinition: [
25
+ servicenow,
26
+ "recordId",
50
27
  ],
51
28
  },
52
- response_format: {
53
- type: "string",
54
- description: "Format of REST response body.",
55
- optional: true,
56
- options: [
57
- "application/json",
58
- "application/xml",
59
- "text/xml",
60
- ],
29
+ updateFields: {
30
+ label: "Update Fields",
31
+ type: "object",
32
+ description: "The fields to update in the record, as key-value pairs (e.g. `{ \"name\": \"Jane Doe\", \"status\": \"active\" }`). **Note:** System fields (prefixed with `sys_`) are typically auto-generated and cannot be updated.",
61
33
  },
62
- x_no_response_body: {
34
+ replaceRecord: {
63
35
  type: "boolean",
64
- description: "By default, responses include body content detailing the modified record. Set this request header to true to suppress the response body.",
36
+ label: "Replace Record",
37
+ description: "If true, replaces the entire record with the provided fields (uses PUT instead of PATCH). Any fields not provided will be cleared.",
65
38
  optional: true,
66
39
  },
67
- sysparm_display_value: {
68
- type: "string",
69
- description: "Return field display values (true), actual values (false), or both (all) (default: false).",
70
- optional: true,
71
- options: [
72
- "true",
73
- "false",
74
- "all",
40
+ responseDataFormat: {
41
+ propDefinition: [
42
+ servicenow,
43
+ "responseDataFormat",
75
44
  ],
76
45
  },
77
- sysparm_fields: {
78
- type: "string",
79
- description: "A comma-separated list of fields to return in the response.",
80
- optional: true,
46
+ responseFields: {
47
+ propDefinition: [
48
+ servicenow,
49
+ "responseFields",
50
+ ],
81
51
  },
82
- sysparm_input_display_value: {
83
- type: "boolean",
84
- description: "Flag that indicates whether to set field values using the display value or the actual value.\n* `true`: Treats input values as display values and they are manipulated so they can be stored properly in the database.\n* `false`: Treats input values as actual values and stored them in the database without manipulation.",
85
- optional: true,
52
+ inputDisplayValue: {
53
+ propDefinition: [
54
+ servicenow,
55
+ "inputDisplayValue",
56
+ ],
86
57
  },
87
- sysparm_view: {
88
- type: "string",
89
- description: "Render the response according to the specified UI view (overridden by sysparm_fields).",
90
- optional: true,
91
- options: [
92
- "desktop",
93
- "mobile",
94
- "both",
58
+ responseView: {
59
+ propDefinition: [
60
+ servicenow,
61
+ "responseView",
95
62
  ],
96
63
  },
97
- sysparm_query_no_domain: {
98
- type: "boolean",
99
- description: "True to access data across domains if authorized (default: false).",
100
- optional: true,
64
+ queryNoDomain: {
65
+ propDefinition: [
66
+ servicenow,
67
+ "queryNoDomain",
68
+ ],
101
69
  },
102
70
  },
103
71
  async run({ $ }) {
104
- /* See the API docs: https://docs.servicenow.com/bundle/paris-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html#c_TableAPI
105
- Section Table - PATCH /now/table/{tableName}/{sys_id} */
106
-
107
- if (!this.table_name || !this.sys_id || !this.update_fields) {
108
- throw new Error("Must provide table_name, sys_id, and update_fields parameters.");
109
- }
110
-
111
- var apiVersion = "";
112
- if (this.api_version == "v1" || this.api_version == "v2") {
113
- apiVersion = this.api_version + "/";
114
- }
115
-
116
- return await axios($, {
117
- method: "patch",
118
- url: `https://${this.servicenow.$auth.instance_name}.service-now.com/api/now/${apiVersion}table/${this.table_name}/${this.sys_id}`,
119
- headers: {
120
- "Authorization": `Bearer ${this.servicenow.$auth.oauth_access_token}`,
121
- "Accept": this.request_format || "application/json",
122
- "Content-Type": this.response_format || "application/json",
123
- "X-no-response-body": this.x_no_response_body,
124
- },
72
+ const response = await this.servicenow.updateTableRecord({
73
+ $,
74
+ table: this.table,
75
+ recordId: this.recordId,
76
+ replace: this.replaceRecord,
77
+ data: parseObject(this.updateFields),
125
78
  params: {
126
- sysparm_display_value: this.sysparm_display_value,
127
- sysparm_fields: this.sysparm_fields,
128
- sysparm_input_display_value: this.sysparm_input_display_value,
129
- sysparm_view: this.sysparm_view,
130
- sysparm_query_no_domain: this.sysparm_query_no_domain,
79
+ sysparm_display_value: this.responseDataFormat,
80
+ sysparm_fields: this.responseFields?.join?.() || this.responseFields,
81
+ sysparm_input_display_value: this.inputDisplayValue,
82
+ sysparm_view: this.responseView,
83
+ sysparm_query_no_domain: this.queryNoDomain,
131
84
  },
132
- data: this.update_fields,
133
85
  });
86
+
87
+ const action = this.replaceRecord
88
+ ? "replaced"
89
+ : "updated";
90
+ $.export("$summary", `Successfully ${action} record ${this.recordId} in table "${this.table}"`);
91
+
92
+ return response;
134
93
  },
135
94
  };
@@ -0,0 +1,15 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
2
+
3
+ export function parseObject(value) {
4
+ if (!value) return undefined;
5
+
6
+ if (typeof value === "string") {
7
+ try {
8
+ return JSON.parse(value);
9
+ } catch (e) {
10
+ throw new ConfigurationError(`Invalid JSON: ${value}`);
11
+ }
12
+ }
13
+
14
+ return value;
15
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pipedream/servicenow",
3
- "version": "0.7.0",
4
- "description": "Pipedream servicenow Components",
3
+ "version": "0.8.0",
4
+ "description": "Pipedream ServiceNow Components",
5
5
  "main": "servicenow.app.mjs",
6
6
  "keywords": [
7
7
  "pipedream",
@@ -13,6 +13,6 @@
13
13
  "access": "public"
14
14
  },
15
15
  "dependencies": {
16
- "@pipedream/platform": "^3.1.0"
16
+ "@pipedream/platform": "^3.1.1"
17
17
  }
18
18
  }
@@ -1,160 +1,168 @@
1
1
  import { axios } from "@pipedream/platform";
2
- import constants from "./common/constants.mjs";
3
-
4
- const {
5
- DEFAULT_SEVERITY_OPTIONS,
6
- INCIDENT_SEVERITY_OPTIONS,
7
- } = constants;
8
2
 
9
3
  export default {
10
4
  type: "app",
11
5
  app: "servicenow",
12
6
  propDefinitions: {
13
- name: {
7
+ table: {
14
8
  type: "string",
15
- label: "Name",
16
- description: "A short description of the ticket issue.",
17
- optional: true,
18
- },
19
- description: {
20
- type: "string",
21
- label: "Description",
22
- description: "A detailed description of the issue.",
23
- },
24
- caseSeverity: {
25
- type: "string",
26
- label: "Severity",
27
- description: "The priority/severity of the case.",
28
- options: DEFAULT_SEVERITY_OPTIONS,
29
- },
30
- incidentSeverity: {
31
- type: "string",
32
- label: "Severity",
33
- description: "The priority/severity of the incident.",
34
- options: INCIDENT_SEVERITY_OPTIONS,
35
- },
36
- status: {
37
- type: "string",
38
- label: "Status",
39
- description: "The current status of the ticket.",
40
- optional: true,
41
- default: "New",
42
- },
43
- channelName: {
44
- type: "string",
45
- label: "Channel Name",
46
- description: "The channel (`contact_type`) that the ticket was created through.",
47
- optional: true,
48
- },
49
- contactMethod: {
9
+ label: "Table",
10
+ description: "Search for a table or provide a table name (not label)",
11
+ useQuery: true,
12
+ async options({ query }) {
13
+ if (!(query?.length > 1)) {
14
+ console.log("Please input a search term");
15
+ return [];
16
+ }
17
+ const data = await this.getTableRecords({
18
+ table: "sys_db_object",
19
+ params: {
20
+ sysparm_query: `nameLIKE${query}^ORlabelLIKE${query}`,
21
+ sysparm_fields: "name,label",
22
+ },
23
+ });
24
+ return data.map(({
25
+ label, name,
26
+ }) => ({
27
+ label,
28
+ value: name,
29
+ }));
30
+ },
31
+ },
32
+ recordId: {
50
33
  type: "string",
51
- label: "Contact Method",
52
- description: "Name of the contact method (`contact_type`) that the ticket was created through.",
53
- optional: true,
34
+ label: "Record ID",
35
+ description: "The ID (`sys_id` field) of the record",
54
36
  },
55
- accountId: {
37
+ responseDataFormat: {
38
+ label: "Response Data Format",
56
39
  type: "string",
57
- label: "Account ID",
58
- description: "`Sys_id` of the account related to the case.",
40
+ description: "The format to return response fields in",
59
41
  optional: true,
60
- },
61
- contactId: {
62
- type: "string",
63
- label: "Contact ID",
64
- description: "`Sys_id` of the contact related to the case.",
42
+ options: [
43
+ {
44
+ value: "true",
45
+ label: "Returns the display values for all fields",
46
+ },
47
+ {
48
+ value: "false",
49
+ label: "Returns the actual values from the database",
50
+ },
51
+ {
52
+ value: "all",
53
+ label: "Returns both actual and display values",
54
+ },
55
+ ],
56
+ },
57
+ excludeReferenceLinks: {
58
+ type: "boolean",
59
+ label: "Exclude Reference Links",
60
+ description: "If true, the response excludes Table API links for reference fields",
65
61
  optional: true,
66
62
  },
67
- companyId: {
68
- type: "string",
69
- label: "Company ID",
70
- description: "`Sys_id` of the company related to the incident.",
63
+ responseFields: {
64
+ type: "string[]",
65
+ label: "Response Fields",
66
+ description: "The fields to return in the response. By default, all fields are returned",
71
67
  optional: true,
72
68
  },
73
- userId: {
74
- type: "string",
75
- label: "User ID",
76
- description: "`Sys_id` of the user related to the incident.",
69
+ inputDisplayValue: {
70
+ label: "Input Display Value",
71
+ type: "boolean",
72
+ description: "If true, the input values are treated as display values (and are manipulated so they can be stored properly in the database)",
77
73
  optional: true,
78
74
  },
79
- workNote: {
75
+ responseView: {
76
+ label: "Response View",
80
77
  type: "string",
81
- label: "Work Note",
82
- description: "Internal work note for the ticket.",
78
+ description: "Render the response according to the specified UI view (overridden by `Response Fields`)",
83
79
  optional: true,
84
- },
85
- comment: {
86
- type: "string",
87
- label: "Comment",
88
- description: "Additional comment for the ticket.",
80
+ options: [
81
+ "desktop",
82
+ "mobile",
83
+ "both",
84
+ ],
85
+ },
86
+ queryNoDomain: {
87
+ type: "boolean",
88
+ label: "Query Across Domains",
89
+ description: "If true, allows access to data across domains (if authorized)",
89
90
  optional: true,
90
91
  },
91
92
  },
92
93
  methods: {
93
- baseUrl() {
94
- const { instance_name: instanceName } = this.$auth;
95
- return `https://${instanceName}.service-now.com`;
94
+ async _makeRequest({
95
+ $ = this,
96
+ headers,
97
+ ...args
98
+ }) {
99
+ const response = await axios($, {
100
+ baseURL: `https://${this.$auth.instance_name}.service-now.com/api/now`,
101
+ headers: {
102
+ ...headers,
103
+ "Authorization": `Bearer ${this.$auth.oauth_access_token}`,
104
+ },
105
+ ...args,
106
+ });
107
+ return response.result;
96
108
  },
97
- authHeaders() {
98
- const { oauth_access_token: oauthAccessToken } = this.$auth;
99
- return {
100
- "Authorization": `Bearer ${oauthAccessToken}`,
101
- "Accept": "application/json",
102
- "Content-Type": "application/json",
103
- };
109
+ async createTableRecord({
110
+ table, ...args
111
+ }) {
112
+ return this._makeRequest({
113
+ method: "post",
114
+ url: `/table/${table}`,
115
+ headers: {
116
+ "Content-Type": "application/json",
117
+ },
118
+ ...args,
119
+ });
104
120
  },
105
- buildChannel(name) {
106
- if (!name) {
107
- return;
108
- }
109
- return {
110
- name,
111
- };
121
+ async updateTableRecord({
122
+ table, recordId, replace, ...args
123
+ }) {
124
+ return this._makeRequest({
125
+ method: replace
126
+ ? "put"
127
+ : "patch",
128
+ url: `/table/${table}/${recordId}`,
129
+ headers: {
130
+ "Content-Type": "application/json",
131
+ },
132
+ ...args,
133
+ });
112
134
  },
113
- buildNotes({
114
- workNote,
115
- comment,
116
- } = {}) {
117
- const notes = [];
118
- if (workNote) {
119
- notes.push({
120
- "text": workNote,
121
- "@type": "work_notes",
122
- });
123
- }
124
- if (comment) {
125
- notes.push({
126
- "text": comment,
127
- "@type": "comments",
128
- });
129
- }
130
- return notes.length
131
- ? notes
132
- : undefined;
135
+ async deleteTableRecord({
136
+ table, recordId, ...args
137
+ }) {
138
+ return this._makeRequest({
139
+ method: "delete",
140
+ url: `/table/${table}/${recordId}`,
141
+ ...args,
142
+ });
133
143
  },
134
- buildRelatedParties(partyTypeToId = {}) {
135
- const entries = Object.entries(partyTypeToId)
136
- .filter(([
137
- , id,
138
- ]) => id);
139
- if (!entries.length) {
140
- return;
141
- }
142
- return entries.map(([
143
- type,
144
- id,
145
- ]) => ({
146
- id,
147
- "@referredType": type,
148
- }));
144
+ async getTableRecordById({
145
+ table, recordId, ...args
146
+ }) {
147
+ return this._makeRequest({
148
+ url: `/table/${table}/${recordId}`,
149
+ ...args,
150
+ });
149
151
  },
150
- async createTroubleTicket({
151
- $ = this, data,
152
- } = {}) {
153
- return axios($, {
154
- method: "post",
155
- url: `${this.baseUrl()}/api/sn_ind_tsm_sdwan/ticket/troubleTicket`,
156
- headers: this.authHeaders(),
157
- data,
152
+ async getTableRecords({
153
+ table, ...args
154
+ }) {
155
+ return this._makeRequest({
156
+ url: `/table/${table}`,
157
+ ...args,
158
+ });
159
+ },
160
+ async getRecordCountsByField({
161
+ table, ...args
162
+ }) {
163
+ return this._makeRequest({
164
+ url: `/stats/${table}`,
165
+ ...args,
158
166
  });
159
167
  },
160
168
  },
@@ -1,114 +0,0 @@
1
- import servicenow from "../../servicenow.app.mjs";
2
-
3
- export default {
4
- key: "servicenow-create-case",
5
- name: "Create Case",
6
- description: "Creates a new case record in ServiceNow. [See the docs here](https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/trouble-ticket-open-api.html#title_trouble-ticket-POST-ticket-tt).",
7
- version: "0.0.1",
8
- type: "action",
9
- annotations: {
10
- destructiveHint: false,
11
- openWorldHint: true,
12
- readOnlyHint: false,
13
- },
14
- props: {
15
- servicenow,
16
- name: {
17
- propDefinition: [
18
- servicenow,
19
- "name",
20
- ],
21
- },
22
- description: {
23
- propDefinition: [
24
- servicenow,
25
- "description",
26
- ],
27
- },
28
- severity: {
29
- propDefinition: [
30
- servicenow,
31
- "caseSeverity",
32
- ],
33
- },
34
- status: {
35
- propDefinition: [
36
- servicenow,
37
- "status",
38
- ],
39
- },
40
- channelName: {
41
- propDefinition: [
42
- servicenow,
43
- "channelName",
44
- ],
45
- },
46
- accountId: {
47
- propDefinition: [
48
- servicenow,
49
- "accountId",
50
- ],
51
- },
52
- contactId: {
53
- propDefinition: [
54
- servicenow,
55
- "contactId",
56
- ],
57
- },
58
- workNote: {
59
- propDefinition: [
60
- servicenow,
61
- "workNote",
62
- ],
63
- },
64
- comment: {
65
- propDefinition: [
66
- servicenow,
67
- "comment",
68
- ],
69
- },
70
- },
71
- async run({ $ }) {
72
- const {
73
- name,
74
- description,
75
- severity,
76
- status,
77
- channelName,
78
- accountId,
79
- contactId,
80
- workNote,
81
- comment,
82
- } = this;
83
-
84
- const channel = this.servicenow.buildChannel(channelName);
85
-
86
- const notes = this.servicenow.buildNotes({
87
- workNote,
88
- comment,
89
- });
90
-
91
- const relatedParties = this.servicenow.buildRelatedParties({
92
- customer: accountId,
93
- customer_contact: contactId,
94
- });
95
-
96
- const response = await this.servicenow.createTroubleTicket({
97
- $,
98
- data: {
99
- ticketType: "Case",
100
- name,
101
- description,
102
- severity,
103
- status,
104
- channel,
105
- notes,
106
- relatedParties,
107
- },
108
- });
109
-
110
- $.export("$summary", "Successfully created a case.");
111
-
112
- return response;
113
- },
114
- };