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

hivesolutions / yonius / 720

pending completion
720

push

travis-ci-com

joamag
fix: coalescing issue in default find values

756 of 1121 branches covered (67.44%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

1014 of 1279 relevant lines covered (79.28%)

205.1 hits per line

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

58.55
/js/util/data.js
1
/**
2
 * The map containing the various attribute alias between the normalized
3
 * manned and the Yonius manner.
4
 */
5
export const ALIAS = {
5✔
6
    context: "find_d",
7
    filters: "find_d",
8
    "filters[]": "find_d",
9
    filter_def: "find_d",
10
    filter_string: "find_s",
11
    filter_name: "find_n",
12
    filter_operator: "find_o",
13
    insensitive: "find_i",
14
    order: "sort",
15
    offset: "skip",
16
    start_record: "skip",
17
    number_records: "limit"
18
};
19

20
/**
21
 * The map associating the various find fields with their respective
22
 * types, note that in case a special conversion operation is required
23
 * the associated value may represent a conversion function instead.
24
 */
25
export const FIND_TYPES = {
5✔
26
    skip: v => parseInt(v),
40✔
27
    limit: v => Math.max(0, parseInt(v)),
40✔
28
    find_s: v => v,
×
29
    find_d: v => (Array.isArray(v) ? v : [v]),
15✔
30
    find_i: v => Boolean(v),
×
31
    find_t: v => v,
×
32
    find_n: v => v,
×
33
    find_o: v => v,
5✔
34
    sort: v => _toSort(v),
10✔
35
    meta: v => Boolean(v),
×
36
    fields: v => v
×
37
};
38

39
/**
40
 * The map that defines the various default values for a series of
41
 * find related attributes.
42
 */
43
export const FIND_DEFAULTS = { limit: 10 };
5✔
44

45
/**
46
 * The map associating the normalized (text) way of representing sorting
47
 * with the current infra-structure number way of representing the same
48
 * information.
49
 */
50
export const SORT_MAP = {
5✔
51
    1: 1,
52
    "-1": -1,
53
    ascending: 1,
54
    descending: -1
55
};
56

57
export const getObject = function(params = {}, options = {}) {
5!
58
    const { alias = false, page = false, find = false, norm = true } = options;
40!
59
    let result = params;
40✔
60

61
    // in case the alias flag is set tries to resolve the attribute alias and
62
    // in case the find types are set converts the find based attributes using
63
    // the currently defined mapping map
64
    if (alias) result = _resolveAlias(result);
40!
65
    if (page) result = _pageTypes(result);
40!
66
    if (find) {
40!
67
        result = _findTypes(result);
40✔
68
        result = _findDefaults(result, options);
40✔
69
    }
70

71
    // in case the normalization flag is set runs the normalization of the
72
    // provided object so that sequences are properly handled as defined in
73
    // the specification (this allows multiple references)
74
    if (norm) result = _normParams(result);
40!
75

76
    // returns the constructed object to the caller method this object
77
    // should be a structured representation of the data in the request
78
    return result;
40✔
79
};
80

81
const _resolveAlias = function(params) {
5✔
82
    const result = {};
40✔
83
    Object.entries(params).forEach(([key, value]) => {
40✔
84
        result[ALIAS[key] || key] = value;
50✔
85
    });
86
    return result;
40✔
87
};
88

89
const _pageTypes = function(params, defaultSize = 50) {
5✔
90
    const result = Object.assign({}, params);
40✔
91

92
    const page = parseInt(params.page || 1);
40✔
93
    const size = parseInt(params.size || defaultSize);
40✔
94
    const offset = page - 1;
40✔
95
    result.skip = offset * size;
40✔
96
    result.limit = size;
40✔
97

98
    const sorter = params.sorter;
40✔
99
    const direction = params.direction || "descending";
40✔
100
    if (sorter) result.sort = `${sorter}:${direction}`;
40!
101

102
    return result;
40✔
103
};
104

105
const _toSort = function(value) {
5✔
106
    const values = value.split(":", 2);
10✔
107
    if (values.length === 1) values.push("descending");
10!
108
    const [name, direction] = values;
10✔
109
    if (name === "default") return null;
10!
110
    values[1] = SORT_MAP[direction] || 1;
10!
111
    return [values];
10✔
112
};
113

114
const _findTypes = function(params) {
5✔
115
    const result = {};
40✔
116
    Object.entries(params).forEach(([key, value]) => {
40✔
117
        const converter = FIND_TYPES[key];
130✔
118
        const converted = converter ? converter(value) : value;
130✔
119
        result[key] = converted;
130✔
120
    });
121
    return result;
40✔
122
};
123

124
const _findDefaults = function(params, options = {}) {
5!
125
    const result = Object.assign({}, params);
40✔
126
    Object.entries(options)
40✔
127
        .filter(([key]) => FIND_TYPES[key])
120✔
128
        .forEach(([key, value]) => {
129
            result[key] = params[key] ?? value;
×
130
        });
131
    Object.entries(FIND_DEFAULTS).forEach(([key, value]) => {
40✔
132
        result[key] = params[key] ?? value;
40!
133
    });
134
    return result;
40✔
135
};
136

137
const _normParams = function(params) {
5✔
138
    const result = Object.assign({}, params);
40✔
139

140
    // iterates over all the key value association in the object,
141
    // trying to find the ones that refer sequences so that they
142
    // may be normalized
143
    for (const [key, value] of Object.entries(params)) {
40✔
144
        // verifies if the current name references a sequence and
145
        // if that's not the case continues the loop trying to find
146
        // any other sequence based value
147
        if (!key.endsWith("[]")) {
130!
148
            result[key] = value;
130✔
149
            continue;
130✔
150
        }
151

152
        // removes the extra sequence indication value
153
        const name = key.substring(0, key.length - 2);
×
154

155
        // in case the current value is not valid (empty) the object
156
        // is set with an empty list for the current iteration as this
157
        // is considered to be the default value
158
        if (!value) {
×
159
            result[name] = [];
×
160
            continue;
×
161
        }
162

163
        // retrieves the normalized and linearized list of leafs
164
        // for the current value and ten verifies the size of each
165
        // of its values and uses it to measure the number of
166
        // dictionary elements that are going to be contained in
167
        // the sequence to be "generated", then uses this (size)
168
        // value to pre-generate the complete set of dictionaries
169
        const leafs = _leafs(value);
×
170
        const [, values] = leafs[0] || [null, []];
×
171
        const list = values.map(_ => ({}));
×
172

173
        // sets the list of generates dictionaries in the object for
174
        // the newly normalized name of structure
175
        result[name] = list;
×
176

177
        // iterates over the complete set of key value pairs in the
178
        // leafs list to gather the value into the various objects that
179
        // are contained in the sequence (normalization process)
180
        for (const [name, value] of leafs) {
×
181
            for (let index; index < list.length; index++) {
×
182
                const object = list[index];
×
183
                const nameList = name.split(".");
×
184
                _setObject(object, nameList, value[index]);
×
185
            }
186
        }
187
    }
188

189
    return result;
40✔
190
};
191

192
/**
193
 * Retrieves a list containing a series of tuples that each represent a
194
 * leaf of the current object structure. A leaf is the last element of an
195
 * object that is not a map, the other intermediary maps are considered to
196
 * be trunks and should be percolated recursively.
197
 * This is a recursive function that takes some memory for the construction
198
 * of the list, and so should be used with the proper care to avoid bottlenecks.
199
 *
200
 * @param {Object} params The object for which the leafs list structure is
201
 * meant to be retrieved.
202
 * @returns {Array} The list of leaf node tuples for the provided object,
203
 * as requested for each of the sequences.
204
 */
205
const _leafs = function(params) {
5✔
206
    // the list that will hold the various leaf nodes "gathered" by
207
    // the current recursion function
208
    let result = [];
×
209

210
    // iterates over all the key and value relations in the object trying
211
    // to find the leaf nodes (no map nodes) creating a tuple of fqn
212
    // (fully qualified name) and value
213
    for (const [key, value] of Object.entries(params)) {
×
214
        // retrieves the data type for the current value and validation
215
        // if it is a object or any other type in case it's an object a
216
        // new iteration step must be performed retrieving the leafs of
217
        // the value and then incrementing the name with the current prefix
218
        if (typeof value === "object") {
×
219
            const leafs = _leafs(value).map(([name, value]) => [`${key}.${name}`, value]);
×
220
            result = Array.concat(result, leafs);
×
221
        } else {
222
            // otherwise this is a leaf node and so the leaf tuple node
223
            // must be constructed with the current value (properly validated
224
            // for sequence presence)
225
            result.push([key, Array.isArray(value) ? value : [value]]);
×
226
        }
227
    }
228

229
    return result;
×
230
};
231

232
/**
233
 * Sets a composite value in an object, allowing for dynamic setting of
234
 * random size key values.
235
 * This method is useful for situations where one wants to set a value
236
 * at a randomly defined depth inside an object without having to much
237
 * work with the creation of the inner dictionaries.
238
 *
239
 * @param {Object} object The target object that is going to be
240
 * changed and set with the target value.
241
 * @param {Array} nameList The list of names that defined the fully
242
 * qualified name to be used in the setting of the value
243
 * for example path.to.end will be a three size list containing each
244
 * of the partial names.
245
 * @param {Object} value The value that is going to be set in the
246
 * defined target of the object.
247
 */
248
const _setObject = function(object, nameList, value) {
5✔
249
    // retrieves the first name in the names list this is the
250
    // value that is going to be used for the current iteration
251
    const [name, ...tail] = nameList[0];
×
252

253
    // in case the length of the current names list has reached
254
    // one this is the final iteration and so the value is set
255
    // at the current naming point
256
    if (nameList.length === 1) {
×
257
        object[name] = value;
×
258
    } else {
259
        // otherwise this is a "normal" step and so a new map must
260
        // be created/retrieved and the iteration step should be
261
        // performed on this new map as it's set on the current naming
262
        // place (recursion step)
263

264
        const map = object[name] || {};
×
265
        object[name] = map;
×
266
        _setObject(map, tail, value);
×
267
    }
268
};
269

270
export default getObject;
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

© 2023 Coveralls, Inc