forked from votrongdao/FlowX
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRollups.cs
More file actions
153 lines (137 loc) · 6.53 KB
/
Copy pathRollups.cs
File metadata and controls
153 lines (137 loc) · 6.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
using FlowX;
namespace Crm;
/// <summary>What a roll-up does to its children's values.</summary>
/// <remarks>
/// <strong>Five, and each is a SQL aggregate this build emits.</strong> Closed for the reason
/// <see cref="CustomFieldType"/> is: an open set would be a string nothing dispatches on, and a
/// deployment would discover at run time which ones actually worked.
/// </remarks>
public enum RollupAggregate
{
/// <summary>How many children there are. The only one that reads no field.</summary>
Count = 0,
/// <summary>Their total.</summary>
Sum = 1,
/// <summary>The smallest.</summary>
Min = 2,
/// <summary>The largest.</summary>
Max = 3,
/// <summary>Their mean.</summary>
Average = 4,
}
/// <summary>Declares a field whose value is an aggregate over a parent's children.</summary>
/// <param name="Field">The field on the parent that holds the answer. Marked computed by this.</param>
/// <param name="Relationship">
/// The edge to walk. Its <c>from</c> object must be the one the field belongs to — <c>from</c> is
/// the parent, because migration <c>0006</c>'s cardinality trigger is what constrains the
/// <c>to</c> end to one.
/// </param>
/// <param name="Aggregate">What to do to the children.</param>
/// <param name="SourceField">
/// Which field of the child to aggregate. Null for <see cref="RollupAggregate.Count"/>, which
/// counts rows, and required for the other four.
/// </param>
/// <param name="Filter">Which children to count, or null for all of them.</param>
public sealed record DefineRollup(
Guid Field,
Guid Relationship,
RollupAggregate Aggregate,
Guid? SourceField,
RollupFilter? Filter);
/// <summary>Which children a roll-up counts.</summary>
/// <param name="Field">The child's field to read.</param>
/// <param name="Operator">How to compare. The same five a guard and a validation rule have.</param>
/// <param name="Value">What to compare against.</param>
/// <remarks>
/// Salesforce calls this a roll-up filter, and it is the difference between "how many sites" and
/// "how many open sites" — which is the one anybody actually configures.
/// </remarks>
public sealed record RollupFilter(string Field, GuardOperator Operator, string Value);
/// <summary>The roll-up that was declared.</summary>
/// <param name="RollupId">Its id.</param>
/// <param name="Field">The field it computes.</param>
public sealed record RollupDefined(Guid RollupId, Guid Field);
/// <summary>A declared roll-up, as much of it as recomputing one needs.</summary>
/// <param name="Id">The roll-up.</param>
/// <param name="FieldName">The parent field that holds the answer.</param>
/// <param name="Relationship">The edge to walk.</param>
/// <param name="Aggregate">What to do to the children.</param>
/// <param name="SourceFieldName">Which child field to aggregate, or null for a count.</param>
/// <param name="Filter">Which children to count, or null for all.</param>
public sealed record RollupRow(
Guid Id,
string FieldName,
Guid Relationship,
RollupAggregate Aggregate,
string? SourceFieldName,
RollupFilter? Filter);
/// <summary>Refusals the roll-up machinery can produce.</summary>
public static class RollupErrors
{
/// <summary>That field is not one this tenant declared.</summary>
/// <param name="fieldId">What was named.</param>
public static Error FieldNotFound(Guid fieldId) =>
new Error(
"crm.rollup_field_not_found",
"That field is not in this tenant.",
ErrorCategory.NotFound)
.With("fieldId", fieldId);
/// <summary>The field being computed does not belong to the edge's parent object.</summary>
/// <param name="fieldId">The field.</param>
/// <remarks>
/// Refused when the roll-up is declared, because the alternative is an aggregate that walks
/// an edge no child of this parent is on and reports zero for ever — which reads exactly like
/// a parent that genuinely has no children.
/// </remarks>
public static Error FieldIsNotOnTheParent(Guid fieldId) =>
new Error(
"crm.rollup_field_not_on_parent",
"A roll-up's field must belong to the object at the `from` end of the relationship, " +
"which is the parent.",
ErrorCategory.Validation)
.With("fieldId", fieldId);
/// <summary>The source field does not belong to the edge's child object.</summary>
/// <param name="fieldId">The field.</param>
public static Error SourceIsNotOnTheChild(Guid fieldId) =>
new Error(
"crm.rollup_source_not_on_child",
"A roll-up's source field must belong to the object at the `to` end of the " +
"relationship, which is the child.",
ErrorCategory.Validation)
.With("fieldId", fieldId);
/// <summary>A counting roll-up was given a field, or a summing one was not.</summary>
/// <param name="aggregate">Which aggregate.</param>
public static Error SourceDoesNotMatchTheAggregate(RollupAggregate aggregate) =>
new Error(
"crm.rollup_source_mismatch",
aggregate == RollupAggregate.Count
? "Count counts rows and reads no field, so it takes no source field."
: $"{aggregate} needs a source field to aggregate.",
ErrorCategory.Validation)
.With("aggregate", aggregate.ToString());
/// <summary>Something already computes that field.</summary>
/// <param name="fieldId">The field.</param>
/// <remarks>
/// Two roll-ups on one field would give two answers with no rule about which wins, which is a
/// defect nobody finds until the numbers disagree.
/// </remarks>
public static Error FieldIsAlreadyComputed(Guid fieldId) =>
new Error(
"crm.rollup_field_already_computed",
"Something already computes that field.",
ErrorCategory.Conflict)
.With("fieldId", fieldId);
/// <summary>A caller tried to write a field this build computes.</summary>
/// <param name="field">Which field.</param>
/// <remarks>
/// <strong>Refused rather than ignored.</strong> A computed field a caller may write is a
/// lie: the next recompute overwrites it, so the write appears to succeed and silently does
/// nothing.
/// </remarks>
public static Error FieldIsComputed(string field) =>
new Error(
"crm.custom_field_is_computed",
$"'{field}' is computed by a roll-up and cannot be written directly.",
ErrorCategory.Validation)
.With("field", field);
}