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

moleculerjs / database / #178

12 Nov 2023 04:51PM UTC coverage: 38.137% (-56.5%) from 94.594%
#178

push

icebob
Fix #53

690 of 1512 branches covered (0.0%)

Branch coverage included in aggregate %.

44 of 45 new or added lines in 2 files covered. (97.78%)

2662 existing lines in 19 files now uncovered.

1517 of 4275 relevant lines covered (35.49%)

50.35 hits per line

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

49.64
/src/transform.js
1
/*
2
 * @moleculer/database
3
 * Copyright (c) 2022 MoleculerJS (https://github.com/moleculerjs/database)
4
 * MIT Licensed
5
 */
6

7
"use strict";
8

9
const { ServiceSchemaError, ValidationError } = require("moleculer").Errors;
1✔
10
const _ = require("lodash");
1✔
11

12
function deepResolve(values, resolvedObj) {
UNCOV
13
        if (!resolvedObj) return values;
×
14

UNCOV
15
        return values.map(v => {
×
UNCOV
16
                if (v != null) {
×
UNCOV
17
                        if (Array.isArray(v)) return deepResolve(v, resolvedObj);
×
18
                        else {
UNCOV
19
                                const res = resolvedObj[v];
×
20
                                // If not found, return the original value (which can be `null`)
UNCOV
21
                                return res != null ? res : v;
×
22
                        }
23
                }
24
                return v;
×
25
        });
26
}
27

28
module.exports = function (mixinOpts) {
1✔
29
        return {
85✔
30
                /**
31
                 * Transform the result rows.
32
                 *
33
                 * @param {Adapter?} adapter
34
                 * @param {Object|Array<Object>} docs
35
                 * @param {Object?} params
36
                 * @param {Context?} ctx
37
                 */
38
                async transformResult(adapter, docs, params, ctx) {
39
                        let isDoc = false;
434✔
40
                        if (!Array.isArray(docs)) {
434✔
41
                                if (_.isObject(docs)) {
196!
42
                                        isDoc = true;
196✔
43
                                        docs = [docs];
196✔
44
                                } else {
45
                                        // Any other primitive value
UNCOV
46
                                        return docs;
×
47
                                }
48
                        }
49

50
                        const span = this.startSpan(ctx, "Transforming result", { params });
434✔
51

52
                        if (!adapter) adapter = await this.getAdapter(ctx);
434!
53
                        docs = docs.map(doc => adapter.entityToJSON(doc));
1,056✔
54

55
                        if (this.$fields) {
434!
56
                                docs = await this._transformFields(adapter, docs, params, ctx);
434✔
57
                        }
58

59
                        this.finishSpan(ctx, span);
434✔
60

61
                        return isDoc ? docs[0] : docs;
434✔
62
                },
63

64
                /**
65
                 * Transform fields on documents.
66
                 *
67
                 * @param {Adapter} adapter
68
                 * @param {Array<Object>} docs
69
                 * @param {Object} params
70
                 * @param {Context?} ctx
71
                 */
72
                async _transformFields(adapter, docs, params, ctx) {
73
                        let customFieldList = false;
434✔
74
                        let selectedFields = this.$fields;
434✔
75
                        if (Array.isArray(params.fields)) {
434!
UNCOV
76
                                selectedFields = this.$fields.filter(f => params.fields.includes(f.name));
×
UNCOV
77
                                customFieldList = true;
×
78
                        }
79
                        const authorizedFields = await this._authorizeFields(selectedFields, ctx, params, {
434✔
80
                                isWrite: false
81
                        });
82

83
                        const res = Array.from(docs).map(() => ({}));
1,056✔
84

85
                        let needPopulates = this.settings.defaultPopulates;
434✔
86
                        if (params.populate === false) needPopulates = null;
434!
87
                        else if (_.isString(params.populate)) needPopulates = [params.populate];
434!
88
                        else if (Array.isArray(params.populate)) needPopulates = params.populate;
434!
89

90
                        await Promise.all(
434✔
91
                                authorizedFields.map(async field => {
92
                                        if (field.hidden === true) return;
2,023✔
93
                                        else if (field.hidden == "byDefault" && !customFieldList) return;
1,970!
94

95
                                        // Field values
96
                                        let values = docs.map(doc => _.get(doc, field.columnName));
6,216✔
97

98
                                        if (!adapter.hasNestedFieldSupport) {
1,970✔
99
                                                if (field.type == "array" || field.type == "object") {
1,295!
UNCOV
100
                                                        values = values.map(v => {
×
UNCOV
101
                                                                if (typeof v === "string") {
×
UNCOV
102
                                                                        try {
×
UNCOV
103
                                                                                return JSON.parse(v);
×
104
                                                                        } catch (e) {
105
                                                                                this.logger.warn("Unable to parse the JSON value", v);
×
106
                                                                        }
107
                                                                }
UNCOV
108
                                                                return v;
×
109
                                                        });
110
                                                }
111
                                        }
112

113
                                        // Populating
114
                                        if (
1,970!
115
                                                field.populate &&
1,970!
116
                                                needPopulates != null &&
117
                                                needPopulates.includes(field.name)
118
                                        ) {
UNCOV
119
                                                if (field.populate.keyField) {
×
120
                                                        // Using different field values as key values
UNCOV
121
                                                        const keyField = this.$fields.find(
×
UNCOV
122
                                                                f => f.name == field.populate.keyField
×
123
                                                        );
UNCOV
124
                                                        if (!keyField) {
×
125
                                                                throw new ServiceSchemaError(
×
126
                                                                        `The 'keyField' is not exist in populate definition of '${field.name}' field.`,
127
                                                                        { field }
128
                                                                );
129
                                                        }
130

UNCOV
131
                                                        values = docs.map(doc => _.get(doc, keyField.columnName));
×
132
                                                }
133

UNCOV
134
                                                const resolvedObj = await this._populateValues(field, values, docs, ctx);
×
135
                                                // Received the values from custom populate
UNCOV
136
                                                if (Array.isArray(resolvedObj)) values = resolvedObj;
×
137
                                                // Received the values from action resolving
UNCOV
138
                                                else values = deepResolve(values, resolvedObj);
×
139
                                        }
140

141
                                        // Virtual or formatted field
142
                                        if (_.isFunction(field.get)) {
1,970✔
143
                                                values = await Promise.all(
439✔
144
                                                        values.map(async (value, i) =>
145
                                                                this._callCustomFunction(field.get, [
1,188✔
146
                                                                        { value, entity: docs[i], field, ctx }
147
                                                                ])
148
                                                        )
149
                                                );
150
                                        }
151

152
                                        // Secure ID field
153
                                        if (field.secure) {
1,970✔
154
                                                values = values.map(v => this.encodeID(v));
329✔
155
                                        }
156

157
                                        // Set values to result
158
                                        res.map((doc, i) => {
1,970✔
159
                                                if (values[i] !== undefined) _.set(doc, field.name, values[i]);
6,216✔
160
                                        });
161
                                })
162
                        );
163

164
                        return res;
434✔
165
                },
166

167
                /**
168
                 * Populate values.
169
                 *
170
                 * @param {Object} field
171
                 * @param {Array<any>} values
172
                 * @param {Array<Object>} docs
173
                 * @param {Context?} ctx
174
                 * @returns {Object}
175
                 */
176
                async _populateValues(field, values, docs, ctx) {
UNCOV
177
                        values = _.uniq(_.compact(_.flattenDeep(values)));
×
178

UNCOV
179
                        const rule = field.populate;
×
UNCOV
180
                        if (rule.handler) {
×
UNCOV
181
                                return await rule.handler.call(this, ctx, values, docs, field);
×
182
                        }
183

UNCOV
184
                        if (values.length == 0) return {};
×
185

UNCOV
186
                        const params = {
×
187
                                ...(rule.params || {}),
×
188
                                id: values,
189
                                mapping: true,
190
                                throwIfNotExist: false
191
                        };
192

UNCOV
193
                        return await (ctx || this.broker).call(rule.action, params, rule.callOptions);
×
194
                }
195
        };
196
};
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