1.5.9 • Published 1 year ago

rule-filter-validator v1.5.9

Weekly downloads
-
License
ISC
Repository
github
Last release
1 year ago

The idea

Given a specific scope what is the easiest way to write various rules that be tested against the scope for various reasons

Useful for testing & validation Business Logic stored as json.

Getting started

npm install rule-filter-validator

Usage

// Initial state/scope/payload
const SCOPE = {
    person: {
        id: 1,
        dob: "1998-02-18",
        age: 23,
        active: true,
        gender: 'F'
    }
}
// Validate and return errors
const rule: Filter = {
    "person": {
        "age": {
            "_gt": 18,
            "_lt": 25
        }
    }
}

let errors = validatePayload(rule, SCOPE);
return ! errors.length // true
// Simply validate
// Using field alter functions
isValidPayload({ person: { 'year(dob)': { _eq: 1998 } } }, SCOPE) // true


// Using $NOW
isValidPayload({ person: { 'dob': { _gt: '$NOW(-6 years)' } } }, SCOPE) // false

View all methods and functions

By default tests are case and type insensitive, meaning:

RuleFnScopeResult
1_eq'1'true
'ABC'_eq'abc'true
'zxc3'_contains3true
'zxc3'_contains'ZXC'true

Calling validatePayload(filter, payload, [strict = false]) with strict = true will make the validator to be case and type sensitive.

RuleFnScopeResult
1_eq'1'false
'ABC'_eq'abc'false
'zxc3'_contains3true
'zxc3'_contains'ZXC'false

_contains always compares as strings and is therefore not type sensitive

Advance Usage

const prices = [
    {
        label: 'Child price'
        price: 100,
        logic: {
            "person": {
                "age": {
                    "_lt": 18
                }
            }
        }
    },
    {
        label: 'Adult price'
        price: 200,
        logic: {
            "person": {
                "age": {
                    "_gte": 18
                }
            }
        }
    }
]

const scope = getUserScope(); // You implement this.

const priceToPay = prices.find(({ price, logic }) => {
    let e = validatePayload(logic, scope)
    return ! e.length
})
const filter: Filter = {
    _or: [{
        permissions: {
            _$: {
                action: {
                    _eq: 'update',
                },
                collection: {
                    _eq: 'membership',
                },
                fields: {
                    _contains: 'status',
                },
            },
        },
    },
    {
        role: {
            _eq: 'admin',
        },
    }],
};

const scope = {
    role: 'author',
    permissions: [
        {
            action: 'create',
            collection: 'membership',
            fields: ['person', 'status'],
        },
        {
            action: 'read',
            collection: 'membership',
            fields: ['id', 'person', 'status'],
        },
        {action: 'update', collection: 'membership', fields: ['status']},
    ],
};

const canAccess = validatePayload(filter, scope, strict?).length === 0

// passes (IF the year is currently 2021) validatePayload( { person: {'year(dob)': {_eq: '$NOW(-12 years).year'}} }, { person: { dob: '2009-02-19' } } );

</details>
<br/>
<hr/>
<br/>

# Methods

- `isValidPayload(Filter, Payload, strict?)`

    This is a simple function that returns true if the payload is valid against the filter, and false otherwise.

- `validatePayload(Filter, Payload, strict?)`

    This is the main function that validates the payload against the filter. It returns an array of errors, if any.

- `invertFilter(Filter)`
 
    Inverts the filter, so that the filter will return the opposite of what it would have returned before.

- `extractFieldFromFilter(Filter, Field, Path?)`

    This extracts the given field from the passed Filter and returns a new Filter object that only contains only the given field and its children, if any.

- `adjustDate(date, adjustment)`

    The function applies adjustments to the Date using built-in methods such as setUTCMonth, setUTCHours, etc., based on the type of adjustment requested. If no supported adjustment type is supplied, it returns undefined.
    
    eg. `-1 month` will return the date 1 month before the given date.
    
    Supports years, months, days, hours, minutes, seconds, milliseconds, and weeks.

---

# Dynamic values

Currently only supporting one dynamic value, which is `$NOW`.

Examples:
 - `$NOW(-1 month)` will test the payload with the date 1 month before the current date.
 - `$NOW(-12 years).year` will test the payload with the year 12 years before the current date.

Supports all [field functions](#Field-Functions) that can be applied to a date.

<br/>

# Operands

## Special Operands

| Fn | Description | Accepted Types |
| ---- | ----- | ----- |
| _and | All of the specified filters must be true for the expression to be true | array of filters
| _or | At least one of the specified filters must be true for the expression to be true | array of filters
| _$ | Used as an index for array of objects, whereby at least one item must pass the filter for the expression to be true.  | object

## Field Functions

Used on fields to perform operations on them.
eg. `year(dob)` will test with the year of the date of birth.

| Fn | Description | Accepted Types |
| ---- | ----- | ----- |
| year() | the year of the date | date, isoString
| month() | the month of the date | date, isoString
| week() | the week of the date | date, isoString
| day() | the day of the date | date, isoString
| hour() | the hour of the date | date, isoString
| minute() | the minute of the date | date, isoString
| second() | the second of the date | date, isoString
| count() | the number of items in the array | array


## All Operands (Functions)

| Fn | Description | Accepted Types |
| ---- | ----- | ----- |
| _eq | equal to | string, number, boolean
| _neq | not equal to | string, number, boolean
| _contains | string/array contains | string, number
| _ncontains | string/array does not contain | string, number
| _starts_with | starts with | string, number
| _nstarts_with | does not start with | string, number
| _ends_with | ends with | string, number
| _nends_with | does not end with | string, number
| _in | in | string, Array
| _nin | not in | string, Array
| _between | between | string, number, Date
| _nbetween | not between | string, number, Date
| _gt | greater than | string, number, Date
| _gte | greater than or equal to | string, number, Date
| _lt | less than | string, number, Date
| _lte | less than or equal to | string, number, Date
| _null | null = | string, number, boolean, Date, Array
| _nnull | not null = | string, number, boolean, Date, Array
| _empty | empty = | string, Array
| _nempty | not empty = | string, Array
| _submitted | submitted = | string, number, boolean, Date, Array
| _regex | matching regex | string, number, boolean, Date


### _$

<details>
<summary>Examples of using `_$`</summary>

Given the following data record:
```ts
const data = {
    colors: [{
        name: 'red',
        hex: '#ff0000',
    }, {
        name: 'green',
        hex: '#00ff00',
    }, {
        name: 'blue',
        hex: '#0000ff',
    }]
};

The following filter will pass:

{
    colors: {
        _$: {
            name: {
                _eq: 'red',
            },
        },
    },
}

And the following will fail:

{
    colors: {
        _$: {
            name: {
                _eq: 'yellow',
            },
        },
    },
}

You could also have multiple properties that have to match

// Will pass
{
    colors: {
        _$: {
            name: {
                _eq: 'red',
            },
            hex: {
                _eq: '#ff0000',
            },
        },
    },
}

// Will fail
{
    colors: {
        _$: {
            name: {
                _eq: 'red',
            },
            hex: {
                _eq: '#ff4444',
            },
        },
    },
};
1.5.9

1 year ago

1.5.8

1 year ago

1.5.7

1 year ago

1.5.6

1 year ago

1.5.5

2 years ago

1.5.4

2 years ago

1.5.3

2 years ago

1.5.2

2 years ago

1.5.1

2 years ago

1.5.0

2 years ago

1.4.3

2 years ago

1.4.2

2 years ago

1.4.1

2 years ago

1.2.3

2 years ago

1.4.0

2 years ago

1.3.1

2 years ago

1.2.2

2 years ago

1.3.0

2 years ago

1.2.0

3 years ago

1.2.1

3 years ago

1.1.3

3 years ago

1.1.2

3 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago