1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | 'use strict';
|
9 |
|
10 | const _ = require('lodash');
|
11 | const concat = require('concat-stream');
|
12 | const events = require('events');
|
13 | const hq = require('hyperquest');
|
14 | const js2xmlparser = require('js2xmlparser');
|
15 | const parseString = require('xml2js').Parser({ explicitArray: false }).parseString;
|
16 | const qs = require('querystring');
|
17 | const request = require('request');
|
18 | const sax = require('./sax-1.2.1.patched');
|
19 | const saxpath = require('saxpath');
|
20 |
|
21 | const api = {
|
22 | makeActionXml (actionName, actionData) {
|
23 | actionData = _.merge(actionData, {$: { 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema' }});
|
24 | return js2xmlparser.parse(actionName, actionData, {declaration: {encoding: 'utf-16'}, attributeString: '$'});
|
25 | },
|
26 |
|
27 |
|
28 | actionXmlStream (psaServerHostName, actionXml) {
|
29 | var uri = `https://${psaServerHostName}/v4_6_release/services/system_io/integration_io/processclientaction.rails`;
|
30 |
|
31 | var req = hq.post(uri);
|
32 | req.setHeader('content-type', 'application/x-www-form-urlencoded');
|
33 | req.end(qs.stringify({ actionString : actionXml }));
|
34 |
|
35 | return req;
|
36 | },
|
37 |
|
38 |
|
39 |
|
40 | actionStream (psaServerHostName, actionXml, actionName, xPath) {
|
41 | xPath = xPath || '//' + actionName;
|
42 |
|
43 | var strict = true;
|
44 | var saxParser = sax.createStream(strict, {entities: false});
|
45 | var streamer = new saxpath.SaXPath(saxParser, xPath);
|
46 | var objectEmitter = new events.EventEmitter();
|
47 |
|
48 | streamer.on('match', xml => {
|
49 | parseString(xml, (error, match) => {
|
50 | error && objectEmitter.emit('error', error);
|
51 | match && objectEmitter.emit('match', match);
|
52 | });
|
53 | });
|
54 |
|
55 | var xmlStream = api.actionXmlStream(psaServerHostName, actionXml)
|
56 | .on('response', response => {
|
57 | if (response.statusCode !== 200) {
|
58 | xmlStream.pipe(concat({encoding: 'string'}, body => {
|
59 | objectEmitter.emit('error', `http response status code ${response.statusCode}\n\n${body}`);
|
60 | }));
|
61 | return;
|
62 | }
|
63 | xmlStream.pipe(saxParser);
|
64 | })
|
65 | .on('error', error => {
|
66 | objectEmitter.emit('error', error);
|
67 | })
|
68 | .on('end', function () {
|
69 | objectEmitter.emit('end');
|
70 | });
|
71 |
|
72 | return objectEmitter;
|
73 | },
|
74 |
|
75 |
|
76 | action (psaServerHostName, actionXml, actionName, returnErrorAndResult) {
|
77 | var xmlStream = api.actionXmlStream(psaServerHostName, actionXml)
|
78 | .on('response', function (response) {
|
79 | xmlStream.pipe(concat({encoding: 'string'}, body => {
|
80 | if (response.statusCode !== 200) {
|
81 | returnErrorAndResult(`http response status code ${response.statusCode}\n\n${body}`);
|
82 | return;
|
83 | }
|
84 | parseString(body, returnErrorAndResult);
|
85 | }));
|
86 | })
|
87 | .on('error', error => returnErrorAndResult(error));
|
88 | },
|
89 |
|
90 | uploadDocumentToTicket (psaServerHostName, psaCompanyName, integrationLoginId, integrationPassword, sRServiceRecID, fileName, fileBufferOrString, returnErrorAndResult) {
|
91 | var mime = require('mime');
|
92 |
|
93 | var uri = `https://${psaServerHostName}/v4_6_release/services/system_io/integration_io/uploaddocumenttoticket.aspx`;
|
94 |
|
95 | var requestOptions = {
|
96 | method: 'POST',
|
97 | uri: uri,
|
98 | strictSSL: false,
|
99 | headers: {
|
100 | 'content-type' : 'multipart/form-data'
|
101 | },
|
102 | multipart: [
|
103 | {'Content-Disposition' : 'form-data; name="PsaCompanyName"', body: psaCompanyName },
|
104 | {'Content-Disposition' : 'form-data; name="IntegrationLoginId"', body: integrationLoginId },
|
105 | {'Content-Disposition' : 'form-data; name="IntegrationPassword"', body: integrationPassword },
|
106 | {'Content-Disposition' : 'form-data; name="SRServiceRecID"', body: sRServiceRecID },
|
107 | {'Content-Disposition' : 'form-data; name="file"; filename="' + fileName + '"', 'Content-Type' : mime.getType(fileName), body: fileBufferOrString}
|
108 | ]
|
109 | };
|
110 |
|
111 | request(requestOptions, (error, response, body) => {
|
112 | if (error) {
|
113 | returnErrorAndResult(error);
|
114 | return;
|
115 | }
|
116 |
|
117 | if (response.statusCode !== 200) {
|
118 | returnErrorAndResult(`response status code ${response.statusCode}\r\n${body}`);
|
119 | return;
|
120 | }
|
121 |
|
122 | returnErrorAndResult(null, body);
|
123 | });
|
124 | },
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 | normalizeCollection (collection) {
|
131 | if (_.isString(collection)) {
|
132 | return [];
|
133 | }
|
134 |
|
135 | var values = _.values(collection);
|
136 | if (values.length === 0) {
|
137 | return [];
|
138 | }
|
139 |
|
140 | var firstValue = values[0];
|
141 | if (!_.isArray(firstValue)) {
|
142 | return [firstValue];
|
143 | }
|
144 |
|
145 | return firstValue;
|
146 | },
|
147 |
|
148 |
|
149 |
|
150 | normalizeTicketsFromFindPartnerTicketsAction (tickets) {
|
151 | return _.map(tickets, ticket => {
|
152 | ticket.Id = ticket.Id || ticket.SRServiceRecID;
|
153 | return ticket;
|
154 | });
|
155 | },
|
156 |
|
157 | normalizeRunReportQueryActionResult (result) {
|
158 | return result.reduce((previous, metaValue) => {
|
159 | previous[metaValue.$.Name] = (value => { switch (metaValue.$.Type) {
|
160 | case 'DateTime': return api.parsePsaDate(value);
|
161 | case 'Numeric': return Number(value);
|
162 | case 'Boolean': return value && value.toLowerCase() === 'true';
|
163 | default: return value;
|
164 | }})(metaValue._);
|
165 | return previous;
|
166 | }, {});
|
167 | },
|
168 |
|
169 | normalizeRunReportQueryAction (metaData) {
|
170 | return api.normalizeCollection(metaData.RunReportQueryAction.Results).map(metaData => {
|
171 | return api.normalizeRunReportQueryActionResult(metaData.Value);
|
172 | });
|
173 | },
|
174 |
|
175 | normalizeGetAvailableReportsActionResponse (response) {
|
176 | var normalizedReports = api.normalizeCollection(response.GetAvailableReportsAction.AvailableReports);
|
177 | var reduced = normalizedReports.reduce((reports, report) => {
|
178 | reports[report.$.Name] = report.Field && report.Field.reduce((fields, field) => {
|
179 | fields[field.$.Name] = _.omit(field.$, 'Name');
|
180 | return fields;
|
181 | }, {});
|
182 | return reports;
|
183 | }, {});
|
184 |
|
185 | return reduced;
|
186 | },
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 | parsePsaDate (dateTime, optionalTimeZone) {
|
197 | if (typeof dateTime === 'undefined') {
|
198 | return dateTime;
|
199 | }
|
200 |
|
201 | var parts = dateTime.split('T');
|
202 | optionalTimeZone && parts.push(optionalTimeZone);
|
203 | return new Date(parts.join(' '));
|
204 | },
|
205 |
|
206 |
|
207 | connectWiseUrl (psaServerHostName) {
|
208 | return 'https://' + psaServerHostName;
|
209 | },
|
210 |
|
211 |
|
212 | ticketUrl (psaServerHostName, psaCompanyName, ticketId) {
|
213 | return `https://${psaServerHostName}/v4_6_release/services/system_io/Service/fv_sr100_request.rails?companyName=${psaCompanyName}&service_recid=${ticketId}`;
|
214 | },
|
215 |
|
216 |
|
217 | activityUrl (psaServerHostName, activityId) {
|
218 | return `https://${psaServerHostName}/v4_6_release/contact/activity/default.rails?action=viewSOActivity&screenid=tm100&ac_flag=false&recordid=${activityId}`;
|
219 | },
|
220 |
|
221 | configure (psaServerHostName, psaCompanyName, integrationLoginId, integrationPassword) {
|
222 | var configured = _.clone(api);
|
223 |
|
224 | configured.action = (actionName, actionData, returnErrorAndResult) => {
|
225 | var defaultActionData = {
|
226 | CompanyName: psaCompanyName,
|
227 | IntegrationLoginId: integrationLoginId,
|
228 | IntegrationPassword: integrationPassword
|
229 | };
|
230 | var actionXml = api.makeActionXml(actionName, _.merge(actionData, defaultActionData));
|
231 | api.action(psaServerHostName, actionXml, actionName, returnErrorAndResult);
|
232 | };
|
233 |
|
234 | configured.actionStream = (actionName, xPath, actionData) => {
|
235 | var defaultActionData = {
|
236 | CompanyName: psaCompanyName,
|
237 | IntegrationLoginId: integrationLoginId,
|
238 | IntegrationPassword: integrationPassword
|
239 | };
|
240 | var actionXml = api.makeActionXml(actionName, _.merge(actionData, defaultActionData));
|
241 | return api.actionStream(psaServerHostName, actionXml, actionName, xPath);
|
242 | };
|
243 |
|
244 | configured.uploadDocumentToTicket = _.partial(api.uploadDocumentToTicket, psaServerHostName, psaCompanyName, integrationLoginId, integrationPassword);
|
245 | configured.connectWiseUrl = _.partial(api.connectWiseUrl, psaServerHostName);
|
246 | configured.ticketUrl = _.partial(api.ticketUrl, psaServerHostName, psaCompanyName);
|
247 | configured.activityUrl = _.partial(api.activityUrl, psaServerHostName);
|
248 |
|
249 | return configured;
|
250 | }
|
251 | };
|
252 |
|
253 | module.exports = api; |
\ | No newline at end of file |