@@ -2,6 +2,7 @@ package dbmodel
2
2
3
3
import (
4
4
"context"
5
+ "iter"
5
6
"slices"
6
7
"strings"
7
8
"time"
@@ -20,6 +21,59 @@ const (
20
21
ZoneRelationLocalZonesApp = "LocalZones.Daemon.App"
21
22
)
22
23
24
+ type ZoneType string
25
+
26
+ const (
27
+ ZoneTypeBuiltin ZoneType = "builtin"
28
+ ZoneTypeDelegationOnly ZoneType = "delegation-only"
29
+ ZoneTypeForward ZoneType = "forward"
30
+ ZoneTypeHint ZoneType = "hint"
31
+ ZoneTypeMirror ZoneType = "mirror"
32
+ ZoneTypePrimary ZoneType = "primary"
33
+ ZoneTypeRedirect ZoneType = "redirect"
34
+ ZoneTypeSecondary ZoneType = "secondary"
35
+ ZoneTypeStaticStub ZoneType = "static-stub"
36
+ ZoneTypeStub ZoneType = "stub"
37
+ )
38
+
39
+ // Holds a set of zone types by which the zones should be filtered.
40
+ // If there are no types specified, all zones are returned.
41
+ // Otherwise, the zones matching the enabled filters are returned.
42
+ type GetZonesFilterZoneTypes struct {
43
+ types map [ZoneType ]bool
44
+ }
45
+
46
+ // Instantiates the zone types filter.
47
+ func NewGetZonesFilterZoneTypes () * GetZonesFilterZoneTypes {
48
+ return & GetZonesFilterZoneTypes {
49
+ types : make (map [ZoneType ]bool ),
50
+ }
51
+ }
52
+
53
+ // Enables a filter for a specific zone type. The zones of the matching
54
+ // type are returned.
55
+ func (f * GetZonesFilterZoneTypes ) Enable (zoneType ZoneType ) {
56
+ f .types [zoneType ] = true
57
+ }
58
+
59
+ // Returns true if any filter is specified (enabled or disabled).
60
+ func (f * GetZonesFilterZoneTypes ) IsAnySpecified () bool {
61
+ return len (f .types ) > 0
62
+ }
63
+
64
+ // Returns an iterator over the enabled zone types.
65
+ func (f * GetZonesFilterZoneTypes ) GetEnabled () iter.Seq [ZoneType ] {
66
+ return func (yield func (ZoneType ) bool ) {
67
+ for zoneType , enabled := range f .types {
68
+ if enabled {
69
+ if ! yield (zoneType ) {
70
+ return
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+
23
77
// Filter used in the GetZones function for complex filtering of
24
78
// the zones returned from the database.
25
79
type GetZonesFilter struct {
@@ -38,11 +92,19 @@ type GetZonesFilter struct {
38
92
// Filter by partial or exact zone serial.
39
93
Serial * string
40
94
// Filter by zone type (e.g., primary or secondary).
41
- Type * string
95
+ Types * GetZonesFilterZoneTypes
42
96
// Filter by partial zone name, app name or view.
43
97
Text * string
44
98
}
45
99
100
+ // Convenience function to enable a zone type filter.
101
+ func (f * GetZonesFilter ) EnableZoneType (zoneType ZoneType ) {
102
+ if f .Types == nil {
103
+ f .Types = NewGetZonesFilterZoneTypes ()
104
+ }
105
+ f .Types .Enable (zoneType )
106
+ }
107
+
46
108
// Represents a zone in a database. The same zone can be shared between
47
109
// many DNS servers. Associations with different servers is are created
48
110
// by adding LocalZone instances to the zone.
@@ -123,7 +185,7 @@ func AddZones(dbi pg.DBI, zones ...*Zone) error {
123
185
// would be negligible.
124
186
func GetZones (db pg.DBI , filter * GetZonesFilter , relations ... ZoneRelation ) ([]* Zone , int , error ) {
125
187
var zones []* Zone
126
- q := db .Model (& zones )
188
+ q := db .Model (& zones ). Distinct ()
127
189
// Add relations.
128
190
for _ , relation := range relations {
129
191
q = q .Relation (string (relation ))
@@ -132,62 +194,72 @@ func GetZones(db pg.DBI, filter *GetZonesFilter, relations ...ZoneRelation) ([]*
132
194
q = q .OrderExpr ("rname ASC" )
133
195
134
196
// Filtering is optional.
135
- if filter ! = nil {
136
- // Limit the number of zones returned.
137
- if filter . Limit != nil {
138
- q = q . Limit ( * filter . Limit )
197
+ if filter = = nil {
198
+ count , err := q . SelectAndCount ()
199
+ if err != nil {
200
+ return nil , count , errors . Wrapf ( err , "failed to select unfiltered zones from the database" )
139
201
}
140
- // Paging from the last returned zone name.
141
- if filter .LowerBound != nil {
142
- labels := dns .SplitDomainName (* filter .LowerBound )
143
- slices .Reverse (labels )
144
- lowerBound := strings .Join (labels , "." )
145
- q = q .Where ("rname > ?" , lowerBound )
146
- }
147
- // Paging from offset.
148
- if filter .Offset != nil {
149
- q = q .Offset (* filter .Offset )
150
- }
151
- // Join relations required for filtering.
152
- if filter .Serial != nil || filter .Class != nil || filter .Type != nil || filter .AppID != nil || filter .AppType != nil || filter .Text != nil {
153
- q = q .Join ("JOIN local_zone AS lz" ).JoinOn ("lz.zone_id = zone.id" )
154
- if filter .AppID != nil || filter .AppType != nil || filter .Text != nil {
155
- q = q .Join ("JOIN daemon AS d" ).JoinOn ("d.id = lz.daemon_id" ).
156
- Join ("JOIN app AS a" ).JoinOn ("a.id = d.app_id" )
157
- }
158
- }
159
- // Filter by serial.
160
- if filter .Serial != nil {
161
- q = q .Where ("lz.serial::text ILIKE ?" , "%" + * filter .Serial + "%" )
162
- }
163
- // Filter by class.
164
- if filter .Class != nil {
165
- q = q .Where ("lz.class = ?" , * filter .Class )
166
- }
167
- // Filter by type.
168
- if filter .Type != nil {
169
- q = q .Where ("lz.type = ?" , * filter .Type )
170
- }
171
- // Filter by app ID.
172
- if filter .AppID != nil {
173
- q = q .Where ("a.id = ?" , * filter .AppID )
174
- }
175
- // Filter by app type.
176
- if filter .AppType != nil {
177
- q = q .Where ("a.type = ?" , * filter .AppType )
202
+ return zones , count , nil
203
+ }
204
+
205
+ // Limit the number of zones returned.
206
+ if filter .Limit != nil {
207
+ q = q .Limit (* filter .Limit )
208
+ }
209
+ // Paging from the last returned zone name.
210
+ if filter .LowerBound != nil {
211
+ labels := dns .SplitDomainName (* filter .LowerBound )
212
+ slices .Reverse (labels )
213
+ lowerBound := strings .Join (labels , "." )
214
+ q = q .Where ("rname > ?" , lowerBound )
215
+ }
216
+ // Paging from offset.
217
+ if filter .Offset != nil {
218
+ q = q .Offset (* filter .Offset )
219
+ }
220
+ // Join relations required for filtering.
221
+ if filter .Serial != nil || filter .Class != nil || filter .Types != nil && filter .Types .IsAnySpecified () || filter .AppID != nil || filter .AppType != nil || filter .Text != nil {
222
+ q = q .Join ("JOIN local_zone AS lz" ).JoinOn ("lz.zone_id = zone.id" )
223
+ if filter .AppID != nil || filter .AppType != nil || filter .Text != nil {
224
+ q = q .Join ("JOIN daemon AS d" ).JoinOn ("d.id = lz.daemon_id" ).
225
+ Join ("JOIN app AS a" ).JoinOn ("a.id = d.app_id" )
178
226
}
179
- // Filter by zone name, app name or local zone view using partial matching.
180
- if filter .Text != nil {
181
- q = q .WhereGroup (func (q * pg.Query ) (* pg.Query , error ) {
182
- return q .WhereOr ("zone.name ILIKE ?" , "%" + * filter .Text + "%" ).
183
- WhereOr ("a.name ILIKE ?" , "%" + * filter .Text + "%" ).
184
- WhereOr ("lz.view ILIKE ?" , "%" + * filter .Text + "%" ), nil
185
- })
227
+ }
228
+ // Filter by serial.
229
+ if filter .Serial != nil {
230
+ q = q .Where ("lz.serial::text ILIKE ?" , "%" + * filter .Serial + "%" )
231
+ }
232
+ // Filter by class.
233
+ if filter .Class != nil {
234
+ q = q .Where ("lz.class = ?" , * filter .Class )
235
+ }
236
+ // Filter by zone types.
237
+ if filter .Types != nil {
238
+ types := slices .Collect (filter .Types .GetEnabled ())
239
+ if len (types ) > 0 {
240
+ q = q .WhereIn ("lz.type IN (?)" , types )
186
241
}
187
242
}
243
+ // Filter by app ID.
244
+ if filter .AppID != nil {
245
+ q = q .Where ("a.id = ?" , * filter .AppID )
246
+ }
247
+ // Filter by app type.
248
+ if filter .AppType != nil {
249
+ q = q .Where ("a.type = ?" , * filter .AppType )
250
+ }
251
+ // Filter by zone name, app name or local zone view using partial matching.
252
+ if filter .Text != nil {
253
+ q = q .WhereGroup (func (q * pg.Query ) (* pg.Query , error ) {
254
+ return q .WhereOr ("zone.name ILIKE ?" , "%" + * filter .Text + "%" ).
255
+ WhereOr ("a.name ILIKE ?" , "%" + * filter .Text + "%" ).
256
+ WhereOr ("lz.view ILIKE ?" , "%" + * filter .Text + "%" ), nil
257
+ })
258
+ }
259
+ // Select and count the zones.
188
260
count , err := q .SelectAndCount ()
189
261
if err != nil {
190
- return nil , count , errors .Wrapf (err , "failed to select zones from the database" )
262
+ return nil , count , errors .Wrapf (err , "failed to select filtered zones from the database" )
191
263
}
192
264
return zones , count , nil
193
265
}
0 commit comments