• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pustovitDmytro / rest-chronicle / c6537982-3bc1-4c94-8379-7a5bf80e3533

10 May 2024 12:15PM UTC coverage: 94.19% (-0.3%) from 94.462%
c6537982-3bc1-4c94-8379-7a5bf80e3533

push

circleci

pustovitDmytro
Merge branch 'master' of github.com:pustovitDmytro/rest-chronicle

185 of 212 branches covered (87.26%)

Branch coverage included in aggregate %.

431 of 442 relevant lines covered (97.51%)

93.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.3
/src/modules/Action.js
1
import { URL } from 'url';
2
import { v4 as uuid } from 'uuid';
3
import { isEmpty, toArray } from 'myrmidon';
4
import { HTTP_STATUS_CODES, DEFAULT_STATUS_CODE } from '../constants';
5

6
function getQuery(searchParams) {
7
    const query = {};
116✔
8

9
    for (const [ name, value ] of searchParams.entries()) {
116✔
10
        if (query[name]) {
29!
11
            query[name] = [ ...toArray(query[name]), value ];
×
12
        } else {
13
            query[name] = value;
29✔
14
        }
15
    }
16

17
    return query;
116✔
18
}
19

20
export default class Action {
21
    constructor({ chronicle, id, _id, _chronicle, ...values }) {
22
        this._context = {};
72✔
23
        this._response = {};
72✔
24
        this._request = {};
72✔
25
        this._chronicle = chronicle || _chronicle;
72!
26
        this.set(values);
72✔
27
        this._id = id || _id || uuid();
72✔
28
        this._chronicle._actions.push(this);
72✔
29
    }
30

31
    copy(chronicle) {
32
        return new Action({
5✔
33
            context  : this.context,
34
            request  : this.request,
35
            response : this.response,
36
            chronicle
37
        });
38
    }
39

40
    static sanitizeHeaders(headers, config) {
41
        if (!config || !headers) return headers || null;
189✔
42
        const sanitized = {};
120✔
43

44
        if (typeof config.sanitize === 'function') {
120!
45
            return config.sanitize(headers);
×
46
        }
47

48
        for (const key of Object.keys(headers)) {
120✔
49
            const value = headers[key];
624✔
50
            const sanitizer = config.sanitize?.[key];
624✔
51

52
            if (config.include && !config.include.includes(key)) continue;
624✔
53
            if (config.exclude && config.exclude.includes(key)) continue;
65!
54

55
            if (typeof sanitizer === 'function') {
65✔
56
                sanitized[key] = sanitizer(value, headers);
55✔
57
            } else {
58
                sanitized[key] = value;
10✔
59
            }
60
        }
61

62
        return isEmpty(sanitized) ? null : sanitized;
120✔
63
    }
64

65
    set(values = {}) {
×
66
        const filtered = Object.entries(values)
186✔
67
            .filter(([ , value ]) => value !== undefined);
631✔
68

69
        for (const [ key, value ] of filtered) { // TODO: check for setter
186✔
70
            this[key] = value;
606✔
71
        }
72
    }
73

74
    set context(context) {
75
        const { urlParams, rawUrl } = context;
72✔
76

77
        this._context = this._chronicle.contextBuilder(context);
72✔
78
        if (urlParams) this._context.urlParams = urlParams;
72✔
79
        if (rawUrl) this._context.rawUrl = rawUrl;
72✔
80
    }
81

82
    set request({ headers, body, ...values }) {
83
        this.set(values);
38✔
84
        this.set({
38✔
85
            reqHeaders : headers,
86
            reqBody    : body
87
        });
88
    }
89

90
    set response({ headers, body, http, status, charset, type }) {
91
        this.set({
38✔
92
            http,
93
            status,
94
            info : {
95
                charset,
96
                type
97
            },
98
            resHeaders : headers,
99
            resBody    : body
100
        });
101
    }
102

103
    set url(value) {
104
        const { rawUrl } = this._context;
67✔
105
        const url = rawUrl || value;
67✔
106

107
        this._request.url = new URL(url);
67✔
108
    }
109

110
    set method(value) {
111
        this._request._method = value.toUpperCase();
71✔
112
    }
113

114
    set reqHeaders(headers) {
115
        this._request.headers = headers;
64✔
116
    }
117

118
    set reqBody(body) {
119
        this._request.body = body;
51✔
120
    }
121

122
    set resHeaders(values) {
123
        this._response.headers = values;
54✔
124
    }
125

126
    set resBody(values) {
127
        this._response.body = values;
71✔
128
    }
129

130
    set httpVersion(version) {
131
        this._response.httpVersion = version;
×
132
    }
133

134
    set status({ code }) {
135
        this._response.code = code;
37✔
136
    }
137

138
    get context() {
139
        return this._context;
246✔
140
    }
141

142
    get title() {
143
        return this._context.title;
×
144
    }
145

146
    get group() {
147
        return this._context.group;
5✔
148
    }
149

150
    get url() {
151
        if (!this._request.url) return null;
116!
152

153
        return {
116✔
154
            href     : this._request.url.href,
155
            origin   : this._request.url.origin,
156
            protocol : this._request.url.protocol,
157
            hostname : this._request.url.hostname,
158
            port     : this._request.url.port,
159
            path     : this._request.url.pathname,
160
            query    : getQuery(this._request.url.searchParams)
161
        };
162
    }
163

164
    get method() {
165
        return this._request._method || 'GET';
96✔
166
    }
167

168
    get reqHeaders() {
169
        return Action.sanitizeHeaders(this._request.headers, this._chronicle.config.headers?.request);
96✔
170
    }
171

172
    get reqBody() {
173
        return this._request.body;
96✔
174
    }
175

176
    get request() {
177
        const url = this.url;
96✔
178

179
        if (!url) return null;
96!
180
        const request = {
96✔
181
            ...url,
182
            method  : this.method,
183
            headers : this.reqHeaders
184
        };
185

186
        const reqBody = this.reqBody;
96✔
187

188
        if (reqBody) {
96✔
189
            request.body = reqBody;
36✔
190
        }
191

192
        return request;
96✔
193
    }
194

195
    get resBody() {
196
        if (Buffer.isBuffer(this._response.body)) return Buffer.from('BINARY DATA');
95✔
197

198
        return this._response.body;
91✔
199
    }
200

201
    get status() {
202
        const statusCode = this._response.code || DEFAULT_STATUS_CODE;
93✔
203

204
        return {
93✔
205
            code    : statusCode,
206
            message : HTTP_STATUS_CODES[statusCode]
207
        };
208
    }
209

210
    get resHeaders() {
211
        return Action.sanitizeHeaders(this._response.headers, this._chronicle.config.headers?.response);
93✔
212
    }
213

214
    get resContentInfo() {
215
        return {
93✔
216
            type    : this.info?.type || 'application/json',
121✔
217
            // eslint-disable-next-line unicorn/text-encoding-identifier-case
218
            charset : this.info?.charset || 'utf-8'
121✔
219
        };
220
    }
221

222
    get httpVersion() {
223
        return this._response.httpVersion || '1.1';
93✔
224
    }
225

226
    get response() {
227
        return {
93✔
228
            status  : this.status,
229
            body    : this.resBody,
230
            headers : this.resHeaders,
231
            info    : this.resContentInfo,
232
            http    : {
233
                version : this.httpVersion
234
            }
235
        };
236
    }
237

238
    get data() {
239
        const request = this.request;
60✔
240
        const response = this.response;
60✔
241
        const context = this.context;
60✔
242

243
        if (!request || !response || !context) return null;
60!
244

245
        return {
60✔
246
            id : this._id,
247
            context,
248
            request,
249
            response
250
        };
251
    }
252

253
    toJSON() {
254
        return this.data;
1✔
255
    }
256
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc