@featurevisor/sdk
Version:
Featurevisor SDK for Node.js and the browser
128 lines (110 loc) • 4.15 kB
text/typescript
import { compareVersions } from "compare-versions";
import { Context, Condition, PlainCondition } from "@featurevisor/types";
import { Logger } from "./logger";
export function conditionIsMatched(condition: PlainCondition, context: Context): boolean {
const { attribute, operator, value } = condition;
if (operator === "equals") {
return context[attribute] === value;
} else if (operator === "notEquals") {
return context[attribute] !== value;
} else if (operator === "before" || operator === "after") {
// date comparisons
const valueInContext = context[attribute] as string | Date;
const dateInContext =
valueInContext instanceof Date ? valueInContext : new Date(valueInContext);
const dateInCondition = value instanceof Date ? value : new Date(value as string);
return operator === "before"
? dateInContext < dateInCondition
: dateInContext > dateInCondition;
} else if (
Array.isArray(value) &&
(["string", "number"].indexOf(typeof context[attribute]) !== -1 || context[attribute] === null)
) {
// array
const valueInContext = context[attribute] as string;
if (operator === "in") {
return value.indexOf(valueInContext) !== -1;
} else if (operator === "notIn") {
return value.indexOf(valueInContext) === -1;
}
} else if (typeof context[attribute] === "string" && typeof value === "string") {
// string
const valueInContext = context[attribute] as string;
if (operator === "contains") {
return valueInContext.indexOf(value) !== -1;
} else if (operator === "notContains") {
return valueInContext.indexOf(value) === -1;
} else if (operator === "startsWith") {
return valueInContext.startsWith(value);
} else if (operator === "endsWith") {
return valueInContext.endsWith(value);
} else if (operator === "semverEquals") {
return compareVersions(valueInContext, value) === 0;
} else if (operator === "semverNotEquals") {
return compareVersions(valueInContext, value) !== 0;
} else if (operator === "semverGreaterThan") {
return compareVersions(valueInContext, value) === 1;
} else if (operator === "semverGreaterThanOrEquals") {
return compareVersions(valueInContext, value) >= 0;
} else if (operator === "semverLessThan") {
return compareVersions(valueInContext, value) === -1;
} else if (operator === "semverLessThanOrEquals") {
return compareVersions(valueInContext, value) <= 0;
}
} else if (typeof context[attribute] === "number" && typeof value === "number") {
// numeric
const valueInContext = context[attribute] as number;
if (operator === "greaterThan") {
return valueInContext > value;
} else if (operator === "greaterThanOrEquals") {
return valueInContext >= value;
} else if (operator === "lessThan") {
return valueInContext < value;
} else if (operator === "lessThanOrEquals") {
return valueInContext <= value;
}
}
return false;
}
export function allConditionsAreMatched(
conditions: Condition[] | Condition,
context: Context,
logger: Logger,
): boolean {
if ("attribute" in conditions) {
try {
return conditionIsMatched(conditions, context);
} catch (e) {
logger.warn(e.message, {
error: e,
details: {
condition: conditions,
context,
},
});
return false;
}
}
if ("and" in conditions && Array.isArray(conditions.and)) {
return conditions.and.every((c) => allConditionsAreMatched(c, context, logger));
}
if ("or" in conditions && Array.isArray(conditions.or)) {
return conditions.or.some((c) => allConditionsAreMatched(c, context, logger));
}
if ("not" in conditions && Array.isArray(conditions.not)) {
return conditions.not.every(
() =>
allConditionsAreMatched(
{
and: conditions.not,
},
context,
logger,
) === false,
);
}
if (Array.isArray(conditions)) {
return conditions.every((c) => allConditionsAreMatched(c, context, logger));
}
return false;
}