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

MrSwitch / dare / #72

06 Jun 2026 07:58PM UTC coverage: 69.143% (-30.9%) from 100.0%
#72

push

web-flow
feat(import-engine): move engines out to their own import path, BREAKING CHANGE (#454)

* feat(postgres): move postgres out to it's own endpoint, BREAKING CHANGE

* style(prettier): fix

* style(ts): fix

* test: fixes

* docs: update README

* docs: update README

* fix(import): migrate LIKE keyword to configurable prop

* feat(import): define mysql5.7 (incl 5.6) legacy import paths, #455

* fix(import): test using wrong dependency

* feat: pluggable json formatter

* fix: issue

* chore: address comments

* fix: issue

* fix: address comments

* fix: address comments, tidy ts

* feat(import): abstract json perfix, operator and formatting

* chore: tidy

* fix(import): abstract DML changes

* feat(import): abstract applyAliasesOnUpdate

* feat(import): abstract sql_fulltext_wildcard

* feat(import): abstract sql_fulltext_wildcard

* feat(import): abstract fulltext sign operators

* chore(postgres): migrate to postgres16 for future proofing

* docs(README): add info about engine specific import paths

* Update README.md

* docs(README): update

* fix: address comments

* fix: prettier

339 of 499 branches covered (67.94%)

Branch coverage included in aggregate %.

487 of 516 new or added lines in 6 files covered. (94.38%)

1497 existing lines in 23 files now uncovered.

3439 of 4965 relevant lines covered (69.26%)

4.28 hits per line

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

47.51
/src/format/join_handler.js
1
import getFieldAttributes from '../utils/field_attributes.js';
9✔
2

9✔
3
/**
9✔
4
 * Join Handler
9✔
5
 * Obtain the table join conditions which says how two tables reference one another
9✔
6
 * @param {object} join_object - The object being joined
9✔
7
 * @param {object} root_object - The root object, for which we want the join_object too attach
9✔
8
 * @param {object} dareInstance - Dare Instance
9✔
9
 * @returns {object} An updated join_object with new join_conditions attached
9✔
10
 */
9✔
11
export default function joinHandler(join_object, root_object, dareInstance) {
9✔
12
        const {models, infer_intermediate_models} = dareInstance.options;
2✔
13

2✔
14
        const {table: rootModel} = root_object;
2✔
15
        const {table: joinModel} = join_object;
2✔
16

2✔
17
        /*
2✔
18
         * The preference is to match in order:
2✔
19
         * joinTable to rootTable
2✔
20
         * rootTable to joinTable (inverted)
2✔
21
         * Looks at the schema of both tables to find one which has a reference field to the other.
2✔
22
         */
2✔
23

2✔
24
        const join_conditions =
2✔
25
                links(models[joinModel]?.schema, rootModel, dareInstance) ||
2✔
26
                invert_links(models[rootModel]?.schema, joinModel, dareInstance);
2✔
27

2✔
28
        // Yes, no, Yeah!
2✔
29
        if (join_conditions) {
2✔
30
                return Object.assign(join_object, join_conditions);
2✔
31
        }
2✔
UNCOV
32

×
UNCOV
33
        /**
×
UNCOV
34
         * Shortcut?
×
UNCOV
35
         * now check whether this alias is defined as a Shortcut
×
UNCOV
36
         * Shortcut's are defined in the model of the root table
×
UNCOV
37
         * And desrcibes the path (model joins) needed for the given nested model
×
UNCOV
38
         */
×
UNCOV
39

×
40
        const shortcut = models[rootModel]?.shortcut_map?.[joinModel];
2✔
41

2✔
42
        // The model alias is now present so we know we're on the right track
2✔
43
        if (shortcut) {
2!
UNCOV
44
                // Decode the modelAlias and construct the joins
×
UNCOV
45
                const [linkTable, joinModel] = shortcut.split('.');
×
UNCOV
46

×
UNCOV
47
                // Update the underlying table
×
UNCOV
48
                join_object.table = joinModel;
×
UNCOV
49

×
UNCOV
50
                // Construct intermediate join
×
UNCOV
51
                const intermediateJoin = findIntermediateJoin({
×
UNCOV
52
                        rootModel,
×
UNCOV
53
                        linkTable,
×
UNCOV
54
                        joinModel,
×
UNCOV
55
                        models,
×
UNCOV
56
                        join_object,
×
UNCOV
57
                        dareInstance,
×
UNCOV
58
                });
×
UNCOV
59

×
UNCOV
60
                // Return, errors are thrown in format_request if this is undefined
×
UNCOV
61
                return intermediateJoin;
×
UNCOV
62
        }
×
UNCOV
63

×
UNCOV
64
        /*
×
UNCOV
65
         * Is the infer_intermediate_models option is set to false?
×
UNCOV
66
         * --> can't guess which table to use, return null
×
UNCOV
67
         */
×
UNCOV
68
        if (infer_intermediate_models === false) {
×
UNCOV
69
                return null;
×
UNCOV
70
        }
×
UNCOV
71

×
UNCOV
72
        // Crawl the schema for an intermediate table which is linked to both tables. link table, ... we're only going for a single Kevin Bacon. More than that and the process will deem this operation too hard.
×
UNCOV
73
        for (const linkTable in models) {
×
UNCOV
74
                // Construct intermediate join
×
UNCOV
75
                const intermediateJoin = findIntermediateJoin({
×
UNCOV
76
                        rootModel,
×
UNCOV
77
                        linkTable,
×
UNCOV
78
                        joinModel,
×
UNCOV
79
                        models,
×
UNCOV
80
                        join_object,
×
UNCOV
81
                        dareInstance,
×
UNCOV
82
                });
×
UNCOV
83

×
UNCOV
84
                if (intermediateJoin) {
×
UNCOV
85
                        return intermediateJoin;
×
UNCOV
86
                }
×
UNCOV
87
        }
×
UNCOV
88

×
UNCOV
89
        // Return a falsy value
×
UNCOV
90
        return null;
×
91
}
2✔
92

9✔
93
/**
9✔
94
 * Find Intermediate joins
9✔
95
 * Given root, link and target (join) names
9✔
96
 * Find and constructs the join definitions (fields to connect)
9✔
97
 * @param {object} object - Object
9✔
98
 * @param {string} object.rootModel - Root model name
9✔
99
 * @param {string} object.linkTable - Link model name
9✔
100
 * @param {string} object.joinModel - Target/join model name
9✔
101
 * @param {object} object.models - Model Options
9✔
102
 * @param {object} object.join_object - Passthrough original join options
9✔
103
 * @param {object} object.dareInstance - Dare Instance
9✔
104
 * @returns {object|undefined} Join definition to return
9✔
105
 */
9✔
UNCOV
106
function findIntermediateJoin({
×
UNCOV
107
        rootModel,
×
UNCOV
108
        linkTable,
×
UNCOV
109
        joinModel,
×
UNCOV
110
        models,
×
UNCOV
111
        join_object,
×
UNCOV
112
        dareInstance,
×
UNCOV
113
}) {
×
UNCOV
114
        // Well, ignore models of the same name
×
UNCOV
115
        if (linkTable === joinModel || linkTable === rootModel) {
×
UNCOV
116
                return;
×
UNCOV
117
        }
×
UNCOV
118

×
UNCOV
119
        // LinkTable <> joinTable?
×
UNCOV
120
        const join_conditions =
×
UNCOV
121
                links(models[joinModel]?.schema, linkTable, dareInstance) ||
×
UNCOV
122
                invert_links(models[linkTable]?.schema, joinModel, dareInstance);
×
UNCOV
123

×
UNCOV
124
        if (!join_conditions) {
×
UNCOV
125
                return;
×
UNCOV
126
        }
×
UNCOV
127

×
UNCOV
128
        // RootTable <> linkTable
×
UNCOV
129
        const root_conditions =
×
UNCOV
130
                links(models[linkTable]?.schema, rootModel, dareInstance) ||
×
UNCOV
131
                invert_links(models[rootModel]?.schema, linkTable, dareInstance);
×
UNCOV
132

×
UNCOV
133
        if (!root_conditions) {
×
UNCOV
134
                return;
×
UNCOV
135
        }
×
UNCOV
136

×
UNCOV
137
        /*
×
UNCOV
138
         * If both the root and join table are pointing to the same value in the linkTable
×
UNCOV
139
         * -> Abort
×
UNCOV
140
         * e.g.
×
UNCOV
141
         *        root_conditions: { join_conditions: { id: 'commentcountry_id' }, many: false },
×
UNCOV
142
         *        join_conditions: { join_conditions: { personcountry_id: 'id' }, many: true }
×
UNCOV
143
         */
×
UNCOV
144
        if (
×
UNCOV
145
                Object.keys(root_conditions.join_conditions).at(0) ===
×
UNCOV
146
                Object.values(join_conditions.join_conditions).at(0)
×
UNCOV
147
        ) {
×
UNCOV
148
                return;
×
UNCOV
149
        }
×
UNCOV
150

×
UNCOV
151
        /*
×
UNCOV
152
         * Awesome, this table (tbl) is the link table and can be used to join up both these tables.
×
UNCOV
153
         * Also give this link table a unique Alias
×
UNCOV
154
         */
×
UNCOV
155
        return {
×
UNCOV
156
                table: linkTable,
×
UNCOV
157
                joins: [Object.assign(join_object, join_conditions)],
×
UNCOV
158
                ...root_conditions,
×
UNCOV
159
        };
×
UNCOV
160
}
×
161

9✔
162
function links(tableSchema, joinTable, dareInstance, flipped = false) {
3✔
163
        const map = {};
3✔
164

3✔
165
        // Loop through the table fields
3✔
166
        for (const field in tableSchema) {
3✔
167
                const {references} = getFieldAttributes(
2✔
168
                        field,
2✔
169
                        tableSchema,
2✔
170
                        dareInstance
2✔
171
                );
2✔
172

2✔
173
                let ref = references || [];
2!
174

2✔
175
                if (!Array.isArray(ref)) {
2!
UNCOV
176
                        ref = [ref];
×
UNCOV
177
                }
×
178

2✔
179
                ref.forEach(ref => {
2✔
180
                        const a = ref.split('.');
2✔
181
                        if (a[0] === joinTable) {
2✔
182
                                map[field] = a[1];
2✔
183
                        }
2✔
184
                });
2✔
185
        }
2✔
186

2✔
187
        return Object.keys(map).length
2✔
188
                ? {
2✔
189
                                join_conditions: flipped ? invert(map) : map,
2✔
190
                                many: !flipped,
2✔
191
                        }
2✔
192
                : null;
3✔
193
}
3✔
194

9✔
195
function invert_links(...args) {
1✔
196
        return links(...args, true);
1✔
197
}
1✔
198

9✔
199
function invert(o) {
1✔
200
        const r = {};
1✔
201
        for (const x in o) {
1✔
202
                r[o[x]] = x;
1✔
203
        }
1✔
204
        return r;
1✔
205
}
1✔
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