0.2.6 • Published 4 months ago

@kinshipjs/adapter-tests v0.2.6

Weekly downloads
-
License
(MIT OR Apache-2....
Repository
github
Last release
4 months ago

Kinship Logo Title & Description

KinshipJS Adapter Tests

Testing suite for testing a custom adapter with Kinship.

Custom Adapter Development

Kinship is a database agnostic ORM thanks to the adapter support built in with it. While there are many adapters available, this suite guides you on how to build your own adapter as well as testing it so it is ready to be packaged and deployed.

Get Started

Install the necessary libraries

npm i @kinshipjs/core
npm i -D @kinshipjs/adapter-tests

Set up your database so it appears as the following:

image

NOTE: Avoid adding any constraints, as truncations of the tables occur, and if the SQL engine used prevents truncation for constraints, then the testAdapter function will never pass.

Serializing

forQuery()

The forQuery() function handles any sort of querying function in your database engine, such as a SELECT * FROM query.

The data you have access to is contained in one SerializationQueryHandlerData object.

SerializationQueryHandlerData

PropertyTypeDescription
from[MainTableFromClauseProperty, ...FromClauseProperty[]]Array of objects where each object represents a table to join on. The first object will represent the main table the context is connected to.
group_byGroupByClauseProperty[]|undefinedArray of objects where each object represents a column to group by. If undefined, then no GROUP BY clause was given.
limitnumber|undefinedNumber representing the number of records to grab. If undefined, then no LIMIT clause was given.
offsetnumber|undefinedNumber representing the number of records to skip before grabbing. If undefined, then no OFFSET clause was given.
order_bySortByClauseProperty[]|undefinedArray of objects where each object represents a column to order by. If undefined, then no ORDER BY clause was given.
selectColumn[]Array of objects where each object represents a column to select.
whereWhereClausePropertyArray|undefinedRecursively nested array of objects where each object represents a condition. If the element is an array, then that means the condition is nested with the last element from that array. If undefined, then no WHERE clause was given.

With the information given to the adapter using SerializationQueryHandlerData, you can construct a command for use within the engine that you are setting the adapter up.

Below are some examples and templates.

Template: createReductionFunction

Definition:

/**
 * Various tools to set up the reduction function used for serializing WHERE clauses.
 * @typedef {object} WhereClauseReduceTools
 * @prop {{[key: WhereChain]: string}=} chains
 * A map of each `WhereChain` (used in Kinship) to the actual string it should use in your SQL engine.  
 * (e.g., AND, OR, AND NOT, OR NOT, etc.)
 * @prop {{[key: WhereOperator]: (...args: string[]) => string}=} operators
 * A map of each `WhereOperator` (used in Kinship) to a function that gets the actual string (with the values) 
 * it should use in your SQL engine.
 * 
 * __NOTE: The values are already sanitized, so you will not have to worry about sanitizing them again.__
 * 
 * e.g.,, in MSSQL:
 * ```js
 * const reduceTools = {
 *   operators: {
 *     [WhereOperator.BETWEEN]: (x,y) => `BETWEEN ${x} AND ${y}`
 *   }
 * }
 * ```
 * @prop {((n: number) => string)=} sanitize
 * Sanitizes the argument, where `n` is the index of the argument it would pass into `for.execute()`
 * 
 * e.g., in MySQL:
 * ```js
 * const reduceTools = {
 *   sanitize: (n) => `?`
 * }
 * ```
 * @prop {number=} spacing
 * Specifies the spacing for if the command should prettified before getting sent back to Kinship.
 * (the default for this is 4 [spaces], and it should remain this way so users can use `onSuccess` or `onFail` to monitor their commands.)
 * If `0` or `undefined` is passed in, then newlines are replaced with regular spaces.
 */

/**
 * Create a reduction function for usage with your serialization of the WHERE clause.
 * @param {any[]} __argsReference
 * Reference to an array that will contain all of the arguments to be passed into the Engine upon completion of this function.
 * @param {WhereClauseReduceTools} tools
 * Various tools that allow you to change the behavior of the serialization of the WHERE clause at a given moment.
 */
function createReductionFunction(
    __argsReference=[], 
    tools={}
) {
    const { 
        chains, 
        operators, 
        sanitize = (n) => '?', 
        spacing = 4
    } = tools;
    /**
     * @param {string} command
     * @param {any} condition
     */
    const reduce = (command, condition, depth=1) => {
        const padding = (spacing ?? 0) * 2;
        const numberOfIndents = (depth * (spacing ?? 0)) + padding;
        const indentation = Array.from(Array(numberOfIndents).keys()).map(_ => " ").join('');
        const prettify = `${spacing ? "\n" : " "}${indentation}`;

        if(Array.isArray(condition)) {
            const chain = condition[0].chain;
            condition[0].chain = "";
            const reduced = condition.reduce((command, condition) => reduce(command, condition, depth + 1), `${chain} (`);
            return `${command}${reduced})${prettify}`;
        }
        let {
            chain,
            operator,
            property,
            table,
            value
        } = condition;
        
        if(Array.isArray(value)) {
            value = value.map((v,n) => {
                __argsReference.push(v);
                return sanitize(__argsReference.length + n);
            });
        } else {
            __argsReference.push(value);
        }

        chain = chains?.[chain] ?? chain;
        if(operators?.[operator]) {
            const values = /** @type {string[]} */ (Array.isArray(value) ? value : [value]);
            return `${command}${chain} ${escape(table, true)}.${escape(property)} ${operators[operator](...values)} ${prettify}`;
        }
        return `${command}${chain} ${escape(table, true)}.${escape(property)} ${operator} ${sanitize(__argsReference.length)}${prettify}`;
    }

    return reduce;
}

Example of usage (MSSQL):

let args = [];
const reduce = createReduce(args, {
    spacing: 4,
    operators: {
        [WhereOperator.BETWEEN]: (x,y) => `BETWEEN ${x} AND ${y}`,
        [WhereOperator.IN]: (...values) => `IN (${values.join(',')})`
    }
});

if(where) {
    const cmd = where.reduce((cmd, cond) => reduce(cmd, cond));
}
console.log(cmd);
// would print something like this for `.where(m => m.FirstName.equals("John").and(m => m.LastName.equals("Doe")))`
/**
 * SELECT [FirstName] AS [FirstName]
 *      [LastName] AS [LastName]
 *      FROM [MyTable] AS [MyTable]
 *      WHERE [FirstName] = ?
 *          AND [LastName] = ?
 */

Executing

Data Types

Column

FromClauseProperty

GroupByClauseProperty

MainTableFromClauseProperty

SortByClauseProperty

WhereClausePropertyArray

Testing your Custom Adapter

0.3.0-test10

4 months ago

0.3.0-test11

4 months ago

0.3.0-rc2

4 months ago

0.3.0-test8

4 months ago

0.3.0-test9

4 months ago

0.3.0-test4

4 months ago

0.3.0-test5

4 months ago

0.3.0-test6

4 months ago

0.3.0-test7

4 months ago

0.3.0-test1

4 months ago

0.3.0-test2

4 months ago

0.3.0-test3

4 months ago

0.3.0-rc1

4 months ago

0.3.0-alpha6

4 months ago

0.3.0-alpha4

4 months ago

0.3.0-alpha5

4 months ago

0.3.0-alpha2

4 months ago

0.3.0-alpha3

4 months ago

0.3.0-alpha1

4 months ago

0.2.6

4 months ago

0.2.5

4 months ago

0.0.14

5 months ago

0.2.1

5 months ago

0.0.15

5 months ago

0.0.16

5 months ago

0.0.17

5 months ago

0.2.3

5 months ago

0.2.2

5 months ago

0.2.4

5 months ago

0.0.3-development

5 months ago

0.0.10

5 months ago

0.0.11

5 months ago

0.0.12

5 months ago

0.0.13

5 months ago

0.0.4-development

5 months ago

0.0.9

5 months ago

0.0.8

5 months ago

0.0.5

5 months ago

0.0.7

5 months ago

0.0.6

5 months ago

0.0.2-development

8 months ago

0.0.1-development

8 months ago