UNPKG

5.61 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2018, Kinvey, Inc. All rights reserved.
3 *
4 * This software is licensed to you under the Kinvey terms of service located at
5 * http://www.kinvey.com/terms-of-use. By downloading, accessing and/or using this
6 * software, you hereby accept such terms of service (and any agreement referenced
7 * therein) and agree that you have read, understand and agree to be bound by such
8 * terms of service and are of legal age to agree to such terms with Kinvey.
9 *
10 * This software contains valuable confidential and proprietary information of
11 * KINVEY, INC and is subject to applicable licensing agreements.
12 * Unauthorized reproduction, transmission or distribution of this file and its
13 * contents is a violation of applicable laws.
14 */
15
16const EOL = require('os').EOL;
17
18const request = require('request');
19
20const { Errors, HTTPMethod } = require('./Constants');
21const KinveyError = require('./KinveyError');
22const Utils = require('./Utils');
23
24/**
25 * Responsible for making HTTP requests.
26 */
27class Request {
28 /**
29 * Create an instance.
30 * @param {Object} [user]
31 * @param {String} user.email
32 * @param {String} user.token
33 * @param {String} user.host
34 * @param {Object} options
35 * @param {String} options.cliVersion
36 * @param {Object} [options.headers]
37 * @params {String} [options.method] HTTP method
38 * @param {String} [options.endpoint] Endpoint, relative to the base URL (e.g. 'v2/apps')
39 * @param {String} [options.query] Query string that will be appended to the endpoint
40 * @param {Number} [options.timeout]
41 * @param [options.data] Set as request body
42 * @param [options.formData]
43 * @param {Boolean} [options.skipAuth] If true, doesn't send authorization header.
44 * @param {Boolean} [options.isBaas] If true, options.baasHost will be used (if set) or the host will be modified.
45 * @param {String} [options.baasHost] BAAS host that will be used if options.isBaas is true.
46 */
47 constructor(user, options) {
48 this.options = {};
49 this._init(user, options);
50 }
51
52 _init(user, options) {
53 this._buildHeaders(user, options);
54 this._buildURL(user, options);
55
56 this.options.method = options.method || HTTPMethod.GET;
57 if (options.timeout) {
58 this.options.timeout = options.timeout;
59 }
60 this.options.json = true;
61
62 if (options.data) {
63 this.options.body = options.data;
64 }
65
66 if (options.formData) {
67 this.options.formData = options.formData;
68 }
69 }
70
71 _buildHeaders(user, options) {
72 this.options.headers = {};
73 if (!Utils.isNullOrUndefined(options.headers)) {
74 this.options.headers = options.headers;
75 }
76
77 this.options.headers['X-Kinvey-Device-Information'] = Utils.getDeviceInformation(options.cliVersion);
78 this._buildAuthHeader(user, options);
79 }
80
81 _buildAuthHeader(user, options) {
82 if (options.skipAuth) {
83 return;
84 }
85
86 if (!Utils.isNullOrUndefined(this.options.headers.Authorization)) {
87 return;
88 }
89
90 if (!Utils.isEmpty(user)) {
91 this.options.headers.Authorization = `Kinvey ${user.token}`;
92 }
93 }
94
95 _buildURL(user, options) {
96 let host;
97 if (user) {
98 host = user.host;
99 } else {
100 host = options.host;
101 }
102
103 if (options.isBaas) {
104 host = options.baasHost || host.replace('manage', 'baas');
105 }
106
107 if (!host.endsWith('/')) {
108 host = `${host}/`;
109 }
110
111 this.options.url = `${host}${options.endpoint}`;
112
113 if (options.query) {
114 this.options.qs = options.query;
115 }
116 }
117
118 static _getDebugMessage(debug) {
119 if (Utils.isNullOrUndefined(debug)) {
120 return '';
121 }
122
123 let debugMsg = '';
124 if (debug.message) {
125 debugMsg = debug.message;
126 } else if (typeof debug === 'string') {
127 debugMsg = debug;
128 } else if (typeof debug === 'object') {
129 debugMsg = JSON.stringify(debug);
130 }
131
132 return debugMsg;
133 }
134
135 static _getProcessedError(err, response) {
136 const isSuccess = !err && response && response.statusCode >= 200 && response.statusCode < 300;
137 if (isSuccess) {
138 return null;
139 }
140
141 if (err) {
142 return Utils.getErrorFromRequestError(err);
143 }
144
145 // status is not 2xx
146 if (response.body) {
147 let errMsg = response.body.description || '';
148 const debugMsg = Request._getDebugMessage(response.body.debug);
149 if (debugMsg) {
150 errMsg += ` ${debugMsg}`;
151 }
152
153 const errors = response.body.errors;
154 if (!Utils.isEmpty(errors) && Array.isArray(errors)) {
155 errors.forEach((x) => {
156 const field = x.field ? `Field: ${x.field}.` : '';
157 errMsg += `${EOL}\t${field} ${x.message}`;
158 });
159 }
160
161 return new KinveyError(response.body.code, errMsg);
162 }
163
164 let errMsg = response.statusCode;
165 if (response.statusMessage) {
166 errMsg += ` ${response.statusMessage}`;
167 }
168
169 return new KinveyError(Errors.RequestError.NAME, errMsg);
170 }
171
172 _send(done) {
173 return request(this.options, done);
174 }
175
176 /**
177 * Sends request. Callback receives error if one has occurred or status code is different than 2xx.
178 * @param done
179 * @returns {*} Request instance that could be used to abort the request.
180 */
181 send(done) {
182 return this._send((err, response) => {
183 const errResult = Request._getProcessedError(err, response);
184 if (errResult) {
185 return done(errResult, response || {});
186 }
187
188 done(null, response);
189 });
190 }
191}
192
193module.exports = Request;