• 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

78.03
/src/format_request.js
1
import SQL, {empty, join, raw} from 'sql-template-tag';
9✔
2
import DareError from './utils/error.js';
9✔
3
import fieldReducer from './format/field_reducer.js';
9✔
4
import groupbyReducer from './format/groupby_reducer.js';
9✔
5
import orderbyReducer from './format/orderby_reducer.js';
9✔
6
import reduceConditions from './format/reducer_conditions.js';
9✔
7
import limitClause from './format/limit_clause.js';
9✔
8
import joinHandler from './format/join_handler.js';
9✔
9
import getFieldAttributes from './utils/field_attributes.js';
9✔
10
import extend from './utils/extend.js';
9✔
11
import buildQuery, {generateSQLSelect} from './get.js';
9✔
12
import toArray from './utils/toArray.js';
9✔
13

9✔
14
/**
9✔
15
 * @import {Sql} from 'sql-template-tag'
9✔
16
 * @import Dare, {QueryOptions} from './index.js'
9✔
17
 */
9✔
18

9✔
19
/**
9✔
20
 * Format Request initiation
9✔
21
 *
9✔
22
 * @param {object} options - Options object
9✔
23
 * @returns {object} Formatted options
9✔
24
 */
9✔
25
export default function (options) {
9✔
26
        return format_request(options, this);
6✔
27
}
6✔
28

9✔
29
/**
9✔
30
 * @typedef {object} SimpleNode
9✔
31
 * @property {string} alias - Alias
9✔
32
 * @property {string} field_alias_path - Field alias path
9✔
33
 * @property {string} table - Table name
9✔
34
 */
9✔
35

9✔
36
/**
9✔
37
 * Format Request
9✔
38
 *
9✔
39
 * @param {QueryOptions} options - Current iteration
9✔
40
 * @param {Dare} dareInstance - Instance of Dare
9✔
41
 * @returns {Promise<QueryOptions>} formatted object with all the joins
9✔
42
 */
9✔
43
async function format_request(options, dareInstance) {
8✔
44
        if (!options) {
8!
UNCOV
45
                throw new DareError(
×
UNCOV
46
                        DareError.INVALID_REQUEST,
×
UNCOV
47
                        `Invalid options '${options}'`
×
UNCOV
48
                );
×
UNCOV
49
        }
×
50

8✔
51
        // Use the alias to find the real table name
8✔
52
        if (!options.alias) {
8✔
53
                const alias = options.table;
6✔
54
                options.alias = alias;
6✔
55
        }
6✔
56

8✔
57
        /*
8✔
58
         * Reject when the table is not provided
8✔
59
         */
8✔
60
        if (!options.table) {
8!
UNCOV
61
                throw new DareError(
×
UNCOV
62
                        DareError.INVALID_REQUEST,
×
UNCOV
63
                        '`table` option is undefined'
×
UNCOV
64
                );
×
UNCOV
65
        }
×
66

8✔
67
        /*
8✔
68
         * Get option settings
8✔
69
         */
8✔
70
        const {conditional_operators_in_value, method, models, state} =
8✔
71
                dareInstance.options;
8✔
72

8✔
73
        /**
8✔
74
         * Assign the state to the options if it is not already defined
8✔
75
         */
8✔
76
        options.state ??= state;
8✔
77

8✔
78
        /*
8✔
79
         * Options name defines the model name
8✔
80
         */
8✔
81
        options.name = dareInstance.table_alias_handler(options.table);
8✔
82

8✔
83
        /*
8✔
84
         * Retrieve the model based upon the model name (alias)
8✔
85
         */
8✔
86
        const model = models?.[options.name] || {};
8✔
87

8✔
88
        /*
8✔
89
         * Set the SQL Table, If the model redefines the table name otherwise use the model Name
8✔
90
         */
8✔
91
        options.sql_table = model.table || options.name;
8✔
92

8✔
93
        /**
8✔
94
         * Hack
8✔
95
         * To resolve mysql bug with aliasing in DELETE operations with a LIMIT https://bugs.mysql.com/bug.php?id=89410
8✔
96
         * Preset the sql_table based upon the actual table name conditions with the same alias will work.
8✔
97
         * TODO [MySQL-5.6/5.7] remove when only supporting MySQL-8
8✔
98
         * TODO [mysql#89410]: Remove the conditional assignment when we're not presetting sql_alias in DELETE operation
8✔
99
         */
8✔
100
        if (options.method === 'del' && !options.parent) {
8✔
101
                options.sql_alias = options.sql_table;
1✔
102
        } else {
7✔
103
                /** EOF Hack */
7✔
104
                options.sql_alias = dareInstance.get_unique_alias();
7✔
105
        }
7✔
106

8✔
107
        const {sql_alias} = options;
8✔
108

8✔
109
        /*
8✔
110
         * Call bespoke table handler
8✔
111
         * This may modify the incoming options object, ammend after handler, etc...
8✔
112
         */
8✔
113
        {
8✔
114
                /*
8✔
115
                 * Mutation operations only exist on the root level table
8✔
116
                 * Nested tables should use the get() handler as they only filter
8✔
117
                 */
8✔
118
                const derivedMethod = options.parent ? 'get' : method;
8✔
119

8✔
120
                // If the model does not define the method
8✔
121
                const handler =
8✔
122
                        derivedMethod in model
8✔
123
                                ? model[derivedMethod]
8!
124
                                : // Or use the default model
8✔
125
                                        models?.default?.[derivedMethod];
8!
126

8✔
127
                if (handler) {
8!
UNCOV
128
                        // Trigger the handler which alters the options...
×
UNCOV
129
                        await handler.call(dareInstance, options, dareInstance);
×
UNCOV
130
                }
×
131
        }
8✔
132

8✔
133
        // Get the schema
8✔
134
        const {schema: table_schema = {}} = model;
8✔
135

8✔
136
        /*
8✔
137
         * Apply defaultValues to join
8✔
138
         */
8✔
139
        {
8✔
140
                Object.keys(table_schema).forEach(key => {
8✔
141
                        const {defaultValue} = getFieldAttributes(
2✔
142
                                key,
2✔
143
                                table_schema,
2✔
144
                                dareInstance
2✔
145
                        );
2✔
146

2✔
147
                        /*
2✔
148
                         * Check the defaultValue for the method has been assigned
2✔
149
                         * -> That there is no definition for the value in the filter and join options
2✔
150
                         * -> That we're trying to get their original field names
2✔
151
                         */
2✔
152
                        if (defaultValue !== undefined) {
2!
UNCOV
153
                                // Does the fields exist?
×
UNCOV
154
                                const filterHasKey = Object.keys({
×
UNCOV
155
                                        ...options.filter,
×
UNCOV
156
                                        ...options.join,
×
UNCOV
157
                                })
×
UNCOV
158
                                        .map(
×
UNCOV
159
                                                filterKey =>
×
UNCOV
160
                                                        dareInstance.getFieldKey(filterKey, table_schema) ||
×
UNCOV
161
                                                        filterKey
×
UNCOV
162
                                        )
×
UNCOV
163
                                        .includes(key);
×
UNCOV
164

×
UNCOV
165
                                // If there is no match
×
UNCOV
166
                                if (!filterHasKey) {
×
UNCOV
167
                                        // Extend the join object with the default value
×
UNCOV
168
                                        extend(options, {join: {[key]: defaultValue}});
×
UNCOV
169
                                }
×
UNCOV
170
                        }
×
171
                });
8✔
172
        }
8✔
173

8✔
174
        // Set the prefix if not already
8✔
175
        options.field_alias_path = options.field_alias_path || '';
8✔
176

8✔
177
        const {field_alias_path} = options;
8✔
178

8✔
179
        // Current Path
8✔
180
        const current_path = field_alias_path || `${options.alias}.`;
8✔
181

8✔
182
        // Create a shared object to provide nested objects
8✔
183
        const joined = {};
8✔
184

8✔
185
        /**
8✔
186
         * Extract nested Handler
8✔
187
         * @param {string} propName - Type of item
8✔
188
         * @param {boolean} isArray - Is array, otherwise expect object
8✔
189
         * @param {string} key - Key to extract
8✔
190
         * @param {*} value - Value to extract
8✔
191
         * @returns {void} - Nothing
8✔
192
         */
8✔
193
        function extractJoined(propName, isArray, key, value) {
8✔
194
                if (!joined[key]) {
2✔
195
                        joined[key] = {};
2✔
196
                }
2✔
197

2✔
198
                // Set default...
2✔
199
                joined[key][propName] = joined[key][propName] || (isArray ? [] : {});
2✔
200

2✔
201
                // Handle differently
2✔
202
                if (isArray) {
2✔
203
                        joined[key][propName].push(...value);
1✔
204
                } else {
1✔
205
                        joined[key][propName] = {...joined[key][propName], ...value};
1✔
206
                }
1✔
207
        }
2✔
208

8✔
209
        /** @type {Array<Sql>} */
8✔
210
        const sql_filters = [];
8✔
211

8✔
212
        // Format filters
8✔
213
        if (options.filter) {
8✔
214
                // Filter must be an object with key=>values
3✔
215
                if (typeof options.filter !== 'object') {
3!
UNCOV
216
                        throw new DareError(
×
UNCOV
217
                                DareError.INVALID_REFERENCE,
×
UNCOV
218
                                `The filter property value '${options.filter}' is invalid. Expected a JS object`
×
UNCOV
219
                        );
×
UNCOV
220
                }
×
221

3✔
222
                // Extract nested filters handler
3✔
223
                const extract = extractJoined.bind(null, 'filter', false);
3✔
224

3✔
225
                // Return array of immediate props
3✔
226
                const arr = reduceConditions(options.filter, {
3✔
227
                        extract,
3✔
228
                        sql_alias,
3✔
229
                        table_schema,
3✔
230
                        conditional_operators_in_value,
3✔
231
                        dareInstance,
3✔
232
                });
3✔
233

3✔
234
                // Add to filters
3✔
235
                sql_filters.push(...arr);
3✔
236
        }
3✔
237

8✔
238
        // Format fields
8✔
239
        if (options.fields) {
8✔
240
                // Fields must be an array, or a dictionary (aka object)
2✔
241
                if (typeof options.fields !== 'object') {
2!
UNCOV
242
                        throw new DareError(
×
UNCOV
243
                                DareError.INVALID_REFERENCE,
×
UNCOV
244
                                `The field definition '${options.fields}' is invalid.`
×
UNCOV
245
                        );
×
UNCOV
246
                }
×
247

2✔
248
                // Extract nested fields handler
2✔
249
                const extract = extractJoined.bind(null, 'fields', true);
2✔
250

2✔
251
                // Set reducer options
2✔
252
                const reducer = fieldReducer({
2✔
253
                        field_alias_path,
2✔
254
                        extract,
2✔
255
                        table_schema,
2✔
256
                        dareInstance,
2✔
257
                });
2✔
258

2✔
259
                // Return array of immediate props
2✔
260
                options.fields = toArray(options.fields).reduce(reducer, []);
2✔
261
        }
2✔
262

8✔
263
        /** @type {Array<Sql>} */
8✔
264
        const sql_join_condition = [];
8✔
265

8✔
266
        // Format conditional joins
8✔
267
        if (options.join) {
8!
UNCOV
268
                // Filter must be an object with key=>values
×
UNCOV
269
                if (typeof options.join !== 'object') {
×
UNCOV
270
                        throw new DareError(
×
UNCOV
271
                                DareError.INVALID_REFERENCE,
×
UNCOV
272
                                `The join property value '${options.join}' is invalid, expected an JS object.`
×
UNCOV
273
                        );
×
UNCOV
274
                }
×
UNCOV
275

×
UNCOV
276
                // Is a required join?
×
UNCOV
277
                if ('_required' in options.join) {
×
UNCOV
278
                        // Has _required join?
×
UNCOV
279
                        options.required_join = options.join._required;
×
UNCOV
280

×
UNCOV
281
                        // Filter out _required
×
UNCOV
282
                        delete options.join._required;
×
UNCOV
283
                }
×
UNCOV
284

×
UNCOV
285
                // Extract nested joins handler
×
UNCOV
286
                const extract = extractJoined.bind(null, 'join', false);
×
UNCOV
287

×
UNCOV
288
                // Return array of immediate props
×
UNCOV
289
                const arrJoins = reduceConditions(options.join, {
×
UNCOV
290
                        extract,
×
UNCOV
291
                        sql_alias,
×
UNCOV
292
                        table_schema,
×
UNCOV
293
                        conditional_operators_in_value,
×
UNCOV
294
                        dareInstance,
×
UNCOV
295
                });
×
UNCOV
296

×
UNCOV
297
                /*
×
UNCOV
298
                 * Convert root joins to filters...
×
UNCOV
299
                 */
×
UNCOV
300
                if (arrJoins.length && !options.parent) {
×
UNCOV
301
                        sql_filters.push(...arrJoins);
×
UNCOV
302
                } else {
×
UNCOV
303
                        sql_join_condition.push(...arrJoins);
×
UNCOV
304
                }
×
UNCOV
305
        }
×
306

8✔
307
        /**
8✔
308
         * Can we stop here?
8✔
309
         */
8✔
310
        if (
8✔
311
                options.parent &&
8✔
312
                !options.required_join &&
8✔
313
                !options.has_fields &&
8✔
314
                !options.has_filter
1✔
315
        ) {
8!
UNCOV
316
                // Prevent this join from being included.
×
UNCOV
317
                return;
×
UNCOV
318
        }
×
319

8✔
320
        /*
8✔
321
         * Groupby
8✔
322
         * If the content is grouped
8✔
323
         */
8✔
324
        if (options.groupby) {
8!
UNCOV
325
                // Extract nested groupby handler
×
UNCOV
326
                const extract = extractJoined.bind(null, 'groupby', true);
×
UNCOV
327

×
UNCOV
328
                // Set reducer options
×
UNCOV
329
                const reducer = groupbyReducer({current_path, extract});
×
UNCOV
330

×
UNCOV
331
                // Return array of immediate props
×
UNCOV
332
                options.groupby = toArray(options.groupby).reduce(reducer, []);
×
UNCOV
333
        }
×
334

8✔
335
        /*
8✔
336
         * Orderby
8✔
337
         * If the content is ordered
8✔
338
         */
8✔
339
        if (options.orderby) {
8!
UNCOV
340
                // Extract nested orderby handler
×
UNCOV
341
                const extract = extractJoined.bind(null, 'orderby', true);
×
UNCOV
342

×
UNCOV
343
                // Set reducer options
×
UNCOV
344
                const reducer = orderbyReducer({
×
UNCOV
345
                        current_path,
×
UNCOV
346
                        extract,
×
UNCOV
347
                        table_schema,
×
UNCOV
348
                        dareInstance,
×
UNCOV
349
                });
×
UNCOV
350

×
UNCOV
351
                // Return array of immediate props
×
UNCOV
352
                options.orderby = toArray(options.orderby).reduce(reducer, []);
×
UNCOV
353
        }
×
354

8✔
355
        // Set default limit
8✔
356
        {
8✔
357
                const limits = limitClause(options, dareInstance.MAX_LIMIT);
8✔
358
                Object.assign(options, limits);
8✔
359
        }
8✔
360

8✔
361
        // Joins
8✔
362
        {
8✔
363
                /** @type {Array<QueryOptions>} */
8✔
364
                const joins = options.joins || [];
8✔
365

8✔
366
                // Add additional joins which have been derived from nested fields and filters...
8✔
367
                for (const alias in joined) {
8✔
368
                        // Furnish the join table a little more...
2✔
369
                        const join_object = Object.assign(joined[alias], {
2✔
370
                                alias,
2✔
371
                                field_alias_path: `${options.field_alias_path}${alias}.`,
2✔
372
                                table: dareInstance.table_alias_handler(alias),
2✔
373
                        });
2✔
374

2✔
375
                        /*
2✔
376
                         * Join referrencing
2✔
377
                         * Create the join_conditions which link two tables together
2✔
378
                         */
2✔
379
                        const new_join_object = joinHandler(
2✔
380
                                join_object,
2✔
381
                                options,
2✔
382
                                dareInstance
2✔
383
                        );
2✔
384

2✔
385
                        // Reject if the join handler returned a falsy value
2✔
386
                        if (!new_join_object) {
2!
UNCOV
387
                                throw new DareError(
×
UNCOV
388
                                        DareError.INVALID_REFERENCE,
×
UNCOV
389
                                        `Could not understand field '${alias}'`
×
UNCOV
390
                                );
×
UNCOV
391
                        }
×
392

2✔
393
                        // Mark the join object to negate
2✔
394
                        new_join_object.negate = alias.startsWith('-');
2✔
395

2✔
396
                        // Help the GET parser
2✔
397

2✔
398
                        // Does this contain a nested filter, orderby or groupby?
2✔
399
                        join_object.has_filter = new_join_object.has_filter = Boolean(
2✔
400
                                join_object.filter || join_object.orderby || join_object.groupby
2✔
401
                        );
2✔
402

2✔
403
                        // Does this contain nested fields
2✔
404
                        join_object.has_fields = new_join_object.has_fields =
2✔
405
                                !!(Array.isArray(join_object.fields)
2✔
406
                                        ? join_object.fields.length
2✔
407
                                        : join_object.fields);
2✔
408

2✔
409
                        // Update the request with this table join
2✔
410
                        joins.push(new_join_object);
2✔
411
                }
2✔
412

8✔
413
                // Loop through the joins array
8✔
414
                if (joins.length) {
8✔
415
                        // Loop through the joins and pass through the formatter
2✔
416
                        const a = joins.map(async join_object => {
2✔
417
                                // Set the parent
2✔
418
                                join_object.parent = options;
2✔
419

2✔
420
                                // Format join...
2✔
421
                                const formatedObject = await format_request(
2✔
422
                                        join_object,
2✔
423
                                        dareInstance
2✔
424
                                );
2✔
425

2✔
426
                                // If this is present
2✔
427
                                if (formatedObject) {
2✔
428
                                        // The handler may have assigned filters when their previously wasn't any
2✔
429
                                        formatedObject.has_filter ||= Boolean(
2✔
430
                                                formatedObject.filter
2✔
431
                                        );
2✔
432
                                }
2✔
433

2✔
434
                                return formatedObject;
2✔
435
                        });
2✔
436

2✔
437
                        // Add Joins
2✔
438
                        const arr = await Promise.all(a);
2✔
439

2✔
440
                        options._joins = arr.filter(Boolean);
2✔
441
                }
2✔
442
        }
8✔
443

8✔
444
        /*
8✔
445
         * Construct the SQL WHERE Condition
8✔
446
         */
8✔
447

8✔
448
        {
8✔
449
                // Place holder
8✔
450

8✔
451
                // Get nested filters
8✔
452
                if (options._joins) {
8✔
453
                        sql_filters.push(
2✔
454
                                ...options._joins.flatMap(
2✔
455
                                        ({sql_where_conditions}) => sql_where_conditions
2✔
456
                                )
2✔
457
                        );
2✔
458
                }
2✔
459

8✔
460
                // Assign
8✔
461
                /** @type {Array<Sql>} */
8✔
462
                options.sql_where_conditions = sql_filters.filter(Boolean);
8✔
463
        }
8✔
464

8✔
465
        // Initial SQL JOINS reference
8✔
466
        options.sql_joins = [];
8✔
467

8✔
468
        /**
8✔
469
         * Construct the join conditions
8✔
470
         * If this item has a parent, it'll require a join statement with conditions
8✔
471
         */
8✔
472
        if (options.parent) {
8✔
473
                // Join_conditions, defines how a node is linked to its parent
2✔
474
                for (const x in options.join_conditions) {
2✔
475
                        const val = options.join_conditions[x];
2✔
476
                        sql_join_condition.push(
2✔
477
                                raw(
2✔
478
                                        `${options.sql_alias}.${x} = ${options.parent.sql_alias}.${val}`
2✔
479
                                )
2✔
480
                        );
2✔
481
                }
2✔
482

2✔
483
                options.sql_join_condition = join(sql_join_condition, ' AND ');
2✔
484

2✔
485
                // Create the SQL JOIN conditions syntax
2✔
486
                options.sql_joins.push(
2✔
487
                        SQL`JOIN ${raw(options.sql_table)} ${raw(options.sql_alias)} ON (${
2✔
488
                                options.sql_join_condition
2✔
489
                        })`
2✔
490
                );
2✔
491
        }
2✔
492

8✔
493
        // Add nested joins
8✔
494
        if (Array.isArray(options._joins)) {
8✔
495
                // Update sql_joins
2✔
496
                options.sql_joins.push(
2✔
497
                        ...options._joins
2✔
498
                                .flatMap(({sql_joins}) => sql_joins)
2✔
499
                                .filter(Boolean)
2✔
500
                );
2✔
501
        }
2✔
502

8✔
503
        /**
8✔
504
         * Negate
8✔
505
         * NOT EXIST (SELECT 1 FROM alias WHERE join_conditions)
8✔
506
         */
8✔
507
        if (options.negate || options.parent?.forceSubquery) {
8✔
508
                // Mark as another subquery
1✔
509
                let sql_where_conditions = [];
1✔
510

1✔
511
                const sql_negate = options.negate ? raw('NOT') : empty;
1!
512

1✔
513
                if (method === 'get') {
1!
UNCOV
514
                        // Get queries can be much simpler, we're allowed to use the same table in an exist statement like...
×
UNCOV
515
                        options.is_subquery = true;
×
UNCOV
516

×
UNCOV
517
                        // Create sub_query
×
UNCOV
518
                        const sub_query = buildQuery(options, dareInstance);
×
UNCOV
519
                        // Create the SQL
×
UNCOV
520
                        const sql_sub_query = generateSQLSelect(sub_query);
×
UNCOV
521

×
UNCOV
522
                        sql_where_conditions = [
×
UNCOV
523
                                SQL`${sql_negate} EXISTS (${sql_sub_query})`,
×
UNCOV
524
                        ];
×
525
                } else {
1✔
526
                        /*
1✔
527
                         * Whilst patch and delete will throw an ER_UPDATE_TABLE_USED error
1✔
528
                         * The query must not reference the table, so we need to be quite sneaky
1✔
529
                         */
1✔
530

1✔
531
                        const parentReferences = Object.values(options.join_conditions).map(
1✔
532
                                val => `${options.parent.sql_alias}.${val}`
1✔
533
                        );
1✔
534

1✔
535
                        // Create sub_query
1✔
536
                        options.fields = Object.keys(options.join_conditions);
1✔
537
                        options.limit = null; // MySQL 5.6 doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
1✔
538
                        options.many = null; // Do not attempt to CONCAT the fields
1✔
539
                        options.field_alias_path = ''; // Do not prefix the fields labels
1✔
540
                        options.parent = null; // Do not add superfluous joins
1✔
541

1✔
542
                        const sub_query = buildQuery(options, dareInstance);
1✔
543
                        const sql_sub_query = generateSQLSelect(sub_query);
1✔
544

1✔
545
                        sql_where_conditions = [
1✔
546
                                SQL`${raw(parentReferences[0])}
1✔
547
                                ${sql_negate} IN (
1✔
548
                                        SELECT ${join(options.fields.map(field => raw(String(field))))} FROM (
1✔
549
                                                ${sql_sub_query}
1✔
550
                                        ) AS ${raw(options.sql_alias)}_tmp
1✔
551
                                )
1✔
552
                        `,
1✔
553
                        ];
1✔
554
                }
1✔
555

1✔
556
                // Update the filters
1✔
557
                return {
1✔
558
                        sql_where_conditions,
1✔
559
                };
1✔
560
        }
1✔
561

7✔
562
        return options;
7✔
563
}
8✔
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