lib/http/rest.js   A
last analyzed

Complexity

Total Complexity 24
Complexity/F 1.6

Size

Lines of Code 199
Function Count 15

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 0
nc 1
dl 0
loc 199
rs 10
c 2
b 0
f 0
wmc 24
mnd 1
bc 19
fnc 15
bpm 1.2666
cpm 1.6
noi 0

3 Functions

Rating   Name   Duplication   Size   Complexity  
A rest.js ➔ getDefaults 0 13 1
A rest.js ➔ JsonSerializer 0 4 1
B rest.js ➔ RestClient 0 104 4
1
/**
2
 * @module http/rest
3
 */
4
5
var C = require('./_common')
6
var M = C.Method
7
var Client = require('./basic').Client
8
var Slf4j = require('../logger').Slf4j
9
10
/**
11
 * @interface Serializer
12
 */
13
14
/**
15
 * @function Serializer.serialize
16
 *
17
 * @param {object}
18
 *
19
 * @return {string}
20
 */
21
22
/**
23
 * @function Serializer.deserialize
24
 *
25
 * @param {string}
26
 *
27
 * @return {object}
28
 */
29
30
/**
31
 * @class
32
 *
33
 * @implements Serializer
34
 */
35
function JsonSerializer () {
36
  this.serialize = JSON.stringify
37
  this.deserialize = JSON.parse
38
}
39
40
/**
41
 * @class RestClientSettings
42
 *
43
 * @property {string|undefined} url String to prepend to every request route.
44
 * @property {Serializer|undefined} serializer Serializer to encode/decode
45
 *   messages, default: JsonSerializer
46
 * @property {number|undefined} retries Maximum number of retries for each
47
 *   request, default: `4`
48
 * @property {string|undefined} methodOverrideHeader Name of header that should
49
 *   conceal real request HTTP method,
50
 *   look up `X-HTTP-Method-Override` in nearest search engine. Default: none
51
 * @property {boolean|undefined} retryOnNetworkError Whether request should be
52
 *   retried on connection error,
53
 *   default: `true`
54
 * @property {boolean|undefined} retryOnServerError Whether request should be
55
 *   retried on server error (5xx),
56
 *   default: `true`
57
 * @property {HeaderBag|undefined} headers Object containing headers for every
58
 *   emitted request, default: `{}`
59
 * @property {int|undefined} timeout Default request timeout in milliseconds,
60
 *   may be overriden on per-request level. Falsey values will use no timeout
61
 *   at all.
62
 * @property {LoggerOptions} logger Logger options.
63
 * @property {IHttpClient} client Underlying http client
64
 */
65
66
// noinspection JSClosureCompilerSyntax
67
/**
68
 * @class
69
 *
70
 * @implements IRestClient
71
 *
72
 * @param {RestClientSettings|Object} [settings]
73
 * @param {netHttpRequestAsync} [transport]
74
 */
75
function RestClient (settings, transport) {
76
  var self = this
77
  var opts = getDefaults()
78
  var client
79
  var logger
80
  var counter = 0
81
82
  if (settings instanceof Function) {
83
    transport = settings
84
    settings = {}
85
  }
86
  settings = settings || {}
87
  if (typeof settings === 'string' || settings instanceof String) {
88
    settings = {url: settings}
89
  }
90
  Object.keys(opts).forEach(function (key) {
91
    if (key in settings) {
92
      opts[key] = settings[key]
93
    }
94
  })
95
  client = settings.client || new Client(opts, transport)
96
  logger = Slf4j.factory(opts.logger, 'ama-team.voxengine-sdk.http.rest')
97
98
  function execute (request) {
99
    var id = ++counter
100
    request.id = request.id || id
101
    var basicRequest = {
102
      url: request.resource || request.route, // 0.2.0 compatibility
103
      method: request.method,
104
      query: request.query,
105
      headers: request.headers,
106
      timeout: typeof request.timeout === 'number' ? request.timeout : settings.timeout
107
    }
108
    logger.debug('Executing request #{} `{} {}`', request.id, request.method, request.resource)
109
    basicRequest.payload = request.payload ? opts.serializer.serialize(request.payload) : null
110
    return client
111
      .execute(basicRequest)
112
      .then(function (response) {
113
        logger.debug('Request #{} `{} {}` received response with code {}',
114
          request.id, request.method, request.resource, response.code)
115
        return {
116
          code: response.code,
117
          payload: response.payload ? opts.serializer.deserialize(response.payload) : null,
118
          headers: response.headers,
119
          request: request
120
        }
121
      }, function (e) {
122
        logger.debug('Request #{} `{} {}` has finished with error {}',
123
          request.id, request.method, request.resource, e.name)
124
        throw e
125
      })
126
  }
127
128
  /**
129
   * @inheritDoc
130
   */
131
  function request (method, resource, payload, query, headers, timeout) {
132
    return execute({resource: resource, method: method, query: query, payload: payload, headers: headers, timeout: timeout})
133
  }
134
135
  /**
136
   * @inheritDoc
137
   */
138
  this.execute = execute
139
140
  /**
141
   * @inheritDoc
142
   */
143
  this.request = request
144
145
  /**
146
   * @inheritDoc
147
   */
148
  this.exists = function (resource, query, headers, timeout) {
149
    return request(M.Head, resource, null, query, headers, timeout)
150
      .then(function (response) {
151
        return response.code !== 404
152
      })
153
  }
154
155
  /**
156
   * @inheritDoc
157
   */
158
  this.get = function (resource, query, headers, timeout) {
159
    return request(M.Get, resource, null, query, headers, timeout)
160
      .then(function (response) {
161
        return response.code === 404 ? null : response.payload
162
      })
163
  }
164
165
  var methods = {create: M.Post, set: M.Put, modify: M.Patch, delete: M.Delete}
166
  Object.keys(methods).forEach(function (method) {
167
    self[method] = function (resource, payload, headers, query, timeout) {
168
      return request(methods[method], resource, payload, query, headers, timeout)
169
        .then(function (response) {
170
          if (response.code === 404) {
171
            var message = 'Modification request has returned 404 status code'
172
            throw new C.NotFoundException(message, response.request, response)
173
          }
174
          return response.payload
175
        })
176
    }
177
  })
178
}
179
180
/**
181
 * @return {RestClientSettings}
182
 */
183
function getDefaults () {
184
  return {
185
    url: '',
186
    client: null,
187
    serializer: new JsonSerializer(),
188
    retries: 4,
189
    methodOverrideHeader: null,
190
    retryOnServerError: true,
191
    retryOnNetworkError: true,
192
    headers: {},
193
    logger: {}
194
  }
195
}
196
197
RestClient.getDefaults = getDefaults
198
199
module.exports = {
200
  Client: RestClient,
201
  /** @deprecated */
202
  getDefaults: getDefaults
203
}
204