UNPKG

@aws-amplify/datastore

Version:

AppSyncLocal support for aws-amplify

244 lines (241 loc) • 7.93 kB
import { extractPrimaryKeyFieldNames, extractPrimaryKeyValues } from '../util.mjs'; export { ModelSortPredicateCreator } from './sort.mjs'; const predicatesAllSet = new WeakSet(); function isPredicatesAll(predicate) { return predicatesAllSet.has(predicate); } /** * The valid logical grouping keys for a predicate group. */ const groupKeys = new Set(['and', 'or', 'not']); /** * Determines whether an object is a GraphQL style predicate "group", which must be an * object containing a single "group key", which then contains the child condition(s). * * E.g., * * ``` * { and: [ ... ] } * { not: { ... } } * ``` * * @param o The object to test. */ const isGroup = o => { const keys = [...Object.keys(o)]; return keys.length === 1 && groupKeys.has(keys[0]); }; /** * Determines whether an object specifies no conditions and should match everything, * as would be the case with `Predicates.ALL`. * * @param o The object to test. */ const isEmpty = o => { return !Array.isArray(o) && Object.keys(o).length === 0; }; /** * The valid comparison operators that can be used as keys in a predicate comparison object. */ const comparisonKeys = new Set([ 'eq', 'ne', 'gt', 'lt', 'ge', 'le', 'contains', 'notContains', 'beginsWith', 'between', 'in', 'notIn', ]); /** * Determines whether an object is a GraphQL style predicate comparison node, which must * be an object containing a single "comparison operator" key, which then contains the * operand or operands to compare against. * * @param o The object to test. */ const isComparison = o => { const keys = [...Object.keys(o)]; return !Array.isArray(o) && keys.length === 1 && comparisonKeys.has(keys[0]); }; /** * A light check to determine whether an object is a valid GraphQL Condition AST. * * @param o The object to test. */ const isValid = o => { if (Array.isArray(o)) { return o.every(v => isValid(v)); } else { return Object.keys(o).length <= 1; } }; // This symbol is not used at runtime, only its type (unique symbol) const PredicateAll = Symbol('A predicate that matches all records'); class Predicates { static get ALL() { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const predicate = (c => c); predicatesAllSet.add(predicate); return predicate; } } class ModelPredicateCreator { /** * Determines whether the given storage predicate (lookup key) is a predicate * key that DataStore recognizes. * * @param predicate The storage predicate (lookup key) to test. */ static isValidPredicate(predicate) { return ModelPredicateCreator.predicateGroupsMap.has(predicate); } /** * Looks for the storage predicate AST that corresponds to a given storage * predicate key. * * The key must have been created internally by a DataStore utility * method, such as `ModelPredicate.createFromAST()`. * * @param predicate The predicate reference to look up. * @param throwOnInvalid Whether to throw an exception if the predicate * isn't a valid DataStore predicate. */ static getPredicates(predicate, throwOnInvalid = true) { if (throwOnInvalid && !ModelPredicateCreator.isValidPredicate(predicate)) { throw new Error('The predicate is not valid'); } return ModelPredicateCreator.predicateGroupsMap.get(predicate); } /** * using the PK values from the given `model` (which can be a partial of T * Creates a predicate that matches an instance described by `modelDefinition` * that contains only PK field values.) * * @param modelDefinition The model definition to create a predicate for. * @param model The model instance to extract value equalities from. */ static createForPk(modelDefinition, model) { const keyFields = extractPrimaryKeyFieldNames(modelDefinition); const keyValues = extractPrimaryKeyValues(model, keyFields); const predicate = this.createFromAST(modelDefinition, { and: keyFields.map((field, idx) => { const operand = keyValues[idx]; return { [field]: { eq: operand } }; }), }); return predicate; } /** * Searches a `Model` table for records matching the given equalities object. * * This only matches against fields given in the equalities object. No other * fields are tested by the predicate. * * @param modelDefinition The model we need a predicate for. * @param flatEqualities An object holding field equalities to search for. */ static createFromFlatEqualities(modelDefinition, flatEqualities) { const ast = { and: Object.entries(flatEqualities).map(([k, v]) => ({ [k]: { eq: v } })), }; return this.createFromAST(modelDefinition, ast); } /** * Accepts a GraphQL style filter predicate tree and transforms it into an * AST that can be used for a storage adapter predicate. Example input: * * ```js * { * and: [ * { name: { eq: "Bob Jones" } }, * { age: { between: [32, 64] } }, * { not: { * or: [ * { favoriteFood: { eq: 'pizza' } }, * { favoriteFood: { eq: 'tacos' } }, * ] * }} * ] * } * ``` * * @param gql GraphQL style filter node. */ static transformGraphQLFilterNodeToPredicateAST(gql) { if (!isValid(gql)) { throw new Error('Invalid GraphQL Condition or subtree: ' + JSON.stringify(gql)); } if (isEmpty(gql)) { return { type: 'and', predicates: [], }; } else if (isGroup(gql)) { const groupkey = Object.keys(gql)[0]; const children = this.transformGraphQLFilterNodeToPredicateAST(gql[groupkey]); return { type: groupkey, predicates: Array.isArray(children) ? children : [children], }; } else if (isComparison(gql)) { const operatorKey = Object.keys(gql)[0]; return { operator: operatorKey, operand: gql[operatorKey], }; } else { if (Array.isArray(gql)) { return gql.map(o => this.transformGraphQLFilterNodeToPredicateAST(o)); } else { const fieldKey = Object.keys(gql)[0]; return { field: fieldKey, ...this.transformGraphQLFilterNodeToPredicateAST(gql[fieldKey]), }; } } } /** * Accepts a GraphQL style filter predicate tree and transforms it into a predicate * that storage adapters understand. Example input: * * ```js * { * and: [ * { name: { eq: "Bob Jones" } }, * { age: { between: [32, 64] } }, * { not: { * or: [ * { favoriteFood: { eq: 'pizza' } }, * { favoriteFood: { eq: 'tacos' } }, * ] * }} * ] * } * ``` * * @param modelDefinition The model that the AST/predicate must be compatible with. * @param ast The graphQL style AST that should specify conditions for `modelDefinition`. */ static createFromAST(modelDefinition, ast) { const key = {}; ModelPredicateCreator.predicateGroupsMap.set(key, this.transformGraphQLFilterNodeToPredicateAST(ast)); return key; } } /** * Map of storage predicates (key objects) to storage predicate AST's. */ ModelPredicateCreator.predicateGroupsMap = new WeakMap(); export { ModelPredicateCreator, PredicateAll, Predicates, comparisonKeys, isPredicatesAll }; //# sourceMappingURL=index.mjs.map