Skip to content

Commit

Permalink
Fix some typings and lint errors in LogicalIterator
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeffrey Yan committed Jun 30, 2020
1 parent 5cd00f4 commit 28db729
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 36 deletions.
55 changes: 34 additions & 21 deletions src/lib/LogicalIterator.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
const OR = '$or';
const AND = '$and';
import { isArray, toPairs, uniq} from 'lodash';
import { isArray, toPairs, uniq } from 'lodash';
import { Model } from 'objection';
import { debug } from '../config';

// Types
import { Model } from 'objection';
import {
Expression,
ExpressionValue,
PropertyOmissionPredicate,
ExpressionObject,
LogicalExpressionIteratorOptions
LogicalExpressionIteratorOptions,
StringFormatter,
LogicalIterator
} from './types';

const OR = '$or';
const AND = '$and';

// Typescript type predicate check during runtime
// https://stackoverflow.com/questions/12789231/class-type-check-in-typescript
function _isArray<T>(objectOrArray: Object | T[]): objectOrArray is T[] {
function _isArray<T>(
objectOrArray: Record<string, unknown> | T[]
): objectOrArray is T[] {
return isArray(objectOrArray);
}

Expand All @@ -26,15 +31,21 @@ function _isArray<T>(objectOrArray: Object | T[]): objectOrArray is T[] {
* @param {Object|Array} objectOrArray
* @returns {Array<Object>}
*/
function arrayize<Item>(objectOrArray: Object | Item[]): Item[] {
function arrayize<T extends Expression>(
objectOrArray: Record<string, unknown> | T[]
): T[] {
if (_isArray(objectOrArray)) return objectOrArray;
const tuples = toPairs(objectOrArray)
const objectArray = tuples.map(item => ({ [item[0]]: item[1] }));
return objectArray as Item[];
};
const tuples = toPairs(objectOrArray);
const objectArray = tuples.map((item) => ({ [item[0]]: item[1] }));
return objectArray as T[];
}

// Helper function to confirm the rhs Expression is an object of SubExpressions
function hasSubExpression(lhs: string, rhs: ExpressionValue): rhs is ExpressionObject {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function hasSubExpression(
lhs: string,
rhs: ExpressionValue
): rhs is ExpressionObject {
return [OR, AND].includes(lhs);
}

Expand All @@ -54,7 +65,9 @@ export function getPropertiesFromExpression(

if (hasSubExpression(lhs, rhs)) {
for (const subExpression of arrayize(rhs)) {
properties = properties.concat(getPropertiesFromExpression(subExpression, test));
properties = properties.concat(
getPropertiesFromExpression(subExpression, test)
);
}
} else {
if (test(lhs)) properties.push(lhs);
Expand All @@ -63,7 +76,7 @@ export function getPropertiesFromExpression(
}

return uniq(properties);
};
}

/**
* Returns a function which iterates an object composed of $or/$and operators
Expand All @@ -84,24 +97,24 @@ export function getPropertiesFromExpression(
*/
export function iterateLogicalExpression<M extends Model>({
onExit, // onExit(propertyOrOperator, value, builder)
onLiteral, // onLiteral(value, builder)
}: LogicalExpressionIteratorOptions<M>) {
onLiteral // onLiteral(value, builder)
}: LogicalExpressionIteratorOptions<M>): LogicalIterator {
/**
*
* @param {Object} expression
* @param {ObjecQueryBuilder} builder
* @param {Boolean} or
* @param {Function} propertyTransform A preOnExit transform for the property name
*/
const iterator = function<M>(
const iterator = function <M>(
expression: Expression,
builder: M,
or = false,
propertyTransform = p => p
propertyTransform: StringFormatter = (p) => p
) {
debug('Iterating through', expression);

builder[or ? 'orWhere' : 'where'](subQueryBuilder => {
builder[or ? 'orWhere' : 'where']((subQueryBuilder) => {
// Assume equality if the target expression is a primitive
if (typeof expression !== 'object') {
return onLiteral(expression, subQueryBuilder);
Expand All @@ -113,7 +126,7 @@ export function iterateLogicalExpression<M extends Model>({

if (hasSubExpression(lhs, rhs)) {
// Wrap nested conditions in their own scope
subQueryBuilder.where(innerBuilder => {
subQueryBuilder.where((innerBuilder) => {
for (const subExpression of arrayize(rhs)) {
iterator(
subExpression,
Expand All @@ -133,4 +146,4 @@ export function iterateLogicalExpression<M extends Model>({
};

return iterator;
};
}
38 changes: 23 additions & 15 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import {
QueryBuilder,
Model
} from 'objection';
import { QueryBuilder, Model } from 'objection';

// Shared types
export interface Relation {
propertyName: string;
relationName: string;
fullyQualifiedProperty: string
fullyQualifiedProperty: string;
}

export type Primitive = number | string | null;
Expand All @@ -25,7 +22,7 @@ type OperationHandler<M extends Model> = (

export type Operators<M extends Model> = {
[f: string]: OperationHandler<M>;
}
};

export type AggregationCallback = <M extends Model, K extends typeof Model>(
RelatedModelClass: K
Expand All @@ -46,13 +43,15 @@ export type ExpressionValue = Expression | string | number;
export type ExpressionObject = {
[key: string]: ExpressionValue;
};
export type Expression = ExpressionObject | ExpressionObject[] | string | number;
export type PropertyOmissionPredicate = (
propertyName?: string
) => boolean;
export type Expression =
| ExpressionObject
| ExpressionObject[]
| string
| number;
export type PropertyOmissionPredicate = (propertyName?: string) => boolean;

export type Item = {
[x: string]: any;
[x: string]: unknown;
};

export type LogicalIteratorExitFunction<M extends Model> = (
Expand Down Expand Up @@ -97,8 +96,17 @@ export interface AggregationConfig {

// Filter definition
export type EagerExpression = {
$where?: Expression,
$aggregations?: AggregationConfig[]
}
$where?: Expression;
$aggregations?: AggregationConfig[];
};

export type RequireExpression = Expression;

export type RequireExpression = Expression;
export type StringFormatter = (s: string) => string;

export type LogicalIterator = <M extends Model>(
expression: Expression,
builder: QueryBuilder<M>,
or?: boolean,
propertyTransform?: StringFormatter
) => void;

0 comments on commit 28db729

Please sign in to comment.