UNPKG

4.26 kBPlain TextView Raw
1import {
2 AllOperators,
3 ModelPredicate,
4 PersistentModel,
5 PredicateExpression,
6 PredicateGroups,
7 PredicatesGroup,
8 ProducerModelPredicate,
9 SchemaModel,
10} from '../types';
11import { exhaustiveCheck } from '../util';
12
13export { ModelSortPredicateCreator } from './sort';
14
15const predicatesAllSet = new WeakSet<ProducerModelPredicate<any>>();
16
17export function isPredicatesAll(
18 predicate: any
19): predicate is typeof PredicateAll {
20 return predicatesAllSet.has(predicate);
21}
22
23// This symbol is not used at runtime, only its type (unique symbol)
24export const PredicateAll = Symbol('A predicate that matches all records');
25
26export class Predicates {
27 public static get ALL(): typeof PredicateAll {
28 const predicate = <ProducerModelPredicate<any>>(c => c);
29
30 predicatesAllSet.add(predicate);
31
32 return <typeof PredicateAll>(<unknown>predicate);
33 }
34}
35
36export class ModelPredicateCreator {
37 private static predicateGroupsMap = new WeakMap<
38 ModelPredicate<any>,
39 PredicatesGroup<any>
40 >();
41
42 private static createPredicateBuilder<T extends PersistentModel>(
43 modelDefinition: SchemaModel
44 ) {
45 const { name: modelName } = modelDefinition;
46 const fieldNames = new Set<keyof T>(Object.keys(modelDefinition.fields));
47
48 let handler: ProxyHandler<ModelPredicate<T>>;
49 const predicate = new Proxy(
50 {} as ModelPredicate<T>,
51 (handler = {
52 get(
53 _target,
54 propertyKey,
55 receiver: ModelPredicate<T>
56 ): PredicateExpression<T, any> {
57 const groupType = propertyKey as keyof PredicateGroups<T>;
58
59 switch (groupType) {
60 case 'and':
61 case 'or':
62 case 'not':
63 const result: PredicateExpression<T, any> = (
64 newPredicate: (criteria: ModelPredicate<T>) => ModelPredicate<T>
65 ) => {
66 const group: PredicatesGroup<T> = {
67 type: groupType,
68 predicates: [],
69 };
70
71 // Create a new recorder
72 const tmpPredicateRecorder = new Proxy(
73 {} as ModelPredicate<T>,
74 handler
75 );
76
77 // Set the recorder group
78 ModelPredicateCreator.predicateGroupsMap.set(
79 tmpPredicateRecorder,
80 group
81 );
82
83 // Apply the predicates to the recorder (this is the step that records the changes)
84 newPredicate(tmpPredicateRecorder);
85
86 // Push the group to the top-level recorder
87 ModelPredicateCreator.predicateGroupsMap
88 .get(receiver)
89 .predicates.push(group);
90
91 return receiver;
92 };
93
94 return result;
95 default:
96 exhaustiveCheck(groupType, false);
97 }
98
99 const field = propertyKey as keyof T;
100
101 if (!fieldNames.has(field)) {
102 throw new Error(
103 `Invalid field for model. field: ${field}, model: ${modelName}`
104 );
105 }
106
107 const result: PredicateExpression<T, any> = (
108 operator: keyof AllOperators,
109 operand: any
110 ) => {
111 ModelPredicateCreator.predicateGroupsMap
112 .get(receiver)
113 .predicates.push({ field, operator, operand });
114 return receiver;
115 };
116 return result;
117 },
118 })
119 );
120
121 const group: PredicatesGroup<T> = {
122 type: 'and',
123 predicates: [],
124 };
125 ModelPredicateCreator.predicateGroupsMap.set(predicate, group);
126
127 return predicate;
128 }
129
130 static isValidPredicate<T extends PersistentModel>(
131 predicate: any
132 ): predicate is ModelPredicate<T> {
133 return ModelPredicateCreator.predicateGroupsMap.has(predicate);
134 }
135
136 static getPredicates<T extends PersistentModel>(
137 predicate: ModelPredicate<T>,
138 throwOnInvalid: boolean = true
139 ) {
140 if (throwOnInvalid && !ModelPredicateCreator.isValidPredicate(predicate)) {
141 throw new Error('The predicate is not valid');
142 }
143
144 return ModelPredicateCreator.predicateGroupsMap.get(predicate);
145 }
146
147 // transforms cb-style predicate into Proxy
148 static createFromExisting<T extends PersistentModel>(
149 modelDefinition: SchemaModel,
150 existing: ProducerModelPredicate<T>
151 ) {
152 if (!existing || !modelDefinition) {
153 return undefined;
154 }
155
156 return existing(
157 ModelPredicateCreator.createPredicateBuilder(modelDefinition)
158 );
159 }
160
161 static createForId<T extends PersistentModel>(
162 modelDefinition: SchemaModel,
163 id: string
164 ) {
165 return ModelPredicateCreator.createPredicateBuilder<T>(modelDefinition).id(
166 'eq',
167 <any>id
168 );
169 }
170}