/
formatter.go
412 lines (368 loc) · 10.2 KB
/
formatter.go
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
package soql
import (
"errors"
"fmt"
"strings"
"time"
)
// QueryInput is used to provide SOQL inputs.
//
// ObjectType is the Salesforce Object, like Account
//
// FieldList is the Salesforce Object's fields to query
//
// SubQuery is the inner query
//
// Where is the SOQL where cause
//
// Order is the SOQL ordering
//
// Limit is the SOQL record limit
//
// Offset is the SOQL record offset
type QueryInput struct {
FieldList []string
ObjectType string
SubQuery []QueryFormatter
Where WhereClauser
Order Orderer
Limit int
Offset int
}
// Query is the struture used to build a SOQL query.
type Query struct {
fieldList []string
objectType string
subQuery []QueryFormatter
where WhereClauser
order Orderer
limit int
offset int
}
// QueryFormatter is the interface to return the SOQL query.
//
// Format returns the SOQL query.
type QueryFormatter interface {
Format() (string, error)
}
// NewQuery creates a new builder. If the object is an
// empty string, then an error is returned.
func NewQuery(input QueryInput) (*Query, error) {
if input.ObjectType == "" {
return nil, errors.New("builder: object type can not be an empty string")
}
if len(input.FieldList) == 0 {
return nil, errors.New("builder: field list can not be empty")
}
return &Query{
objectType: input.ObjectType,
fieldList: input.FieldList,
subQuery: input.SubQuery,
where: input.Where,
order: input.Order,
limit: input.Limit,
offset: input.Offset,
}, nil
}
// Format will return the SOQL query. If the builder has an empty string or
// the field list is zero, an error is returned.
func (b *Query) Format() (string, error) {
if b.objectType == "" {
return "", errors.New("builder: object type can not be an empty string")
}
if len(b.fieldList) == 0 {
return "", errors.New("builder: field list must be have fields present")
}
soql := "SELECT " + strings.Join(b.fieldList, ",")
if b.subQuery != nil {
for _, query := range b.subQuery {
var sub string
var err error
if sub, err = query.Format(); err == nil {
soql += fmt.Sprintf(",(%s)", sub)
} else {
return "", err
}
}
}
soql += " FROM " + b.objectType
if b.where != nil {
soql += " " + b.where.Clause()
}
if b.order != nil {
order, err := b.order.Order()
if err == nil {
soql += " " + order
} else {
return "", err
}
}
if b.limit > 0 {
soql += fmt.Sprintf(" LIMIT %d", b.limit)
}
if b.offset > 0 {
soql += fmt.Sprintf(" OFFSET %d", b.offset)
}
return soql, nil
}
// WhereClause is the structure that will contain a SOQL where clause.
type WhereClause struct {
expression string
}
// WhereExpression is an interface to return the where cause's expression.
type WhereExpression interface {
Expression() string
}
// WhereClauser is an interface to return the where cause.
type WhereClauser interface {
Clause() string
}
// WhereLike will form the LIKE expression.
func WhereLike(field string, value string) (*WhereClause, error) {
if field == "" {
return nil, errors.New("soql where: field can not be empty")
}
if value == "" {
return nil, errors.New("soql where: value can not be empty")
}
return &WhereClause{
expression: fmt.Sprintf("%s LIKE '%s'", field, value),
}, nil
}
// WhereGreaterThan will form the greater or equal than expression. If the value is a
// string or boolean, an error is returned.
func WhereGreaterThan(field string, value interface{}, equals bool) (*WhereClause, error) {
if field == "" {
return nil, errors.New("soql where: field can not be empty")
}
if value == nil {
return nil, errors.New("soql where: value can not be nil")
}
var v string
switch value.(type) {
case string, bool:
return nil, errors.New("where greater than: value can not be a string or bool")
case time.Time:
date := value.(time.Time)
v = date.Format(time.RFC3339)
default:
v = fmt.Sprintf("%v", value)
}
operator := ">"
if equals {
operator += "="
}
return &WhereClause{
expression: fmt.Sprintf("%s %s %s", field, operator, v),
}, nil
}
// WhereLessThan will form the less or equal than expression. If the value is a
// string or boolean, an error is returned.
func WhereLessThan(field string, value interface{}, equals bool) (*WhereClause, error) {
if field == "" {
return nil, errors.New("soql where: field can not be empty")
}
if value == nil {
return nil, errors.New("soql where: value can not be nil")
}
var v string
switch value.(type) {
case string, bool:
return nil, errors.New("where less than: value can not be a string")
case time.Time:
date := value.(time.Time)
v = date.Format(time.RFC3339)
default:
v = fmt.Sprintf("%v", value)
}
operator := "<"
if equals {
operator += "="
}
return &WhereClause{
expression: fmt.Sprintf("%s %s %s", field, operator, v),
}, nil
}
// WhereEquals forms the equals where expression.
func WhereEquals(field string, value interface{}) (*WhereClause, error) {
if field == "" {
return nil, errors.New("soql where: field can not be empty")
}
var v string
if value != nil {
switch value.(type) {
case string:
v = fmt.Sprintf("'%s'", value.(string))
case time.Time:
date := value.(time.Time)
v = date.Format(time.RFC3339)
default:
v = fmt.Sprintf("%v", value)
}
} else {
v = "null"
}
return &WhereClause{
expression: fmt.Sprintf("%s = %s", field, v),
}, nil
}
// WhereNotEquals forms the not equals where expression.
func WhereNotEquals(field string, value interface{}) (*WhereClause, error) {
if field == "" {
return nil, errors.New("soql where: field can not be empty")
}
var v string
if value != nil {
switch value.(type) {
case string:
v = fmt.Sprintf("'%s'", value.(string))
case time.Time:
date := value.(time.Time)
v = date.Format(time.RFC3339)
default:
v = fmt.Sprintf("%v", value)
}
} else {
v = "null"
}
return &WhereClause{
expression: fmt.Sprintf("%s != %s", field, v),
}, nil
}
// WhereIn forms the field in a set expression.
func WhereIn(field string, values []interface{}) (*WhereClause, error) {
if field == "" {
return nil, errors.New("soql where: field can not be empty")
}
if values == nil {
return nil, errors.New("soql where: value array can not be nil")
}
set := make([]string, len(values))
for idx, value := range values {
switch value.(type) {
case string:
set[idx] = fmt.Sprintf("'%s'", value.(string))
case bool:
return nil, errors.New("where in: boolean is not a value set value")
case time.Time:
date := value.(time.Time)
set[idx] = date.Format(time.RFC3339)
default:
set[idx] = fmt.Sprintf("%v", value)
}
}
return &WhereClause{
expression: fmt.Sprintf("%s IN (%s)", field, strings.Join(set, ",")),
}, nil
}
// WhereNotIn forms the field is not in a set expression.
func WhereNotIn(field string, values []interface{}) (*WhereClause, error) {
if field == "" {
return nil, errors.New("soql where: field can not be empty")
}
if values == nil {
return nil, errors.New("soql where: value array can not be nil")
}
set := make([]string, len(values))
for idx, value := range values {
switch value.(type) {
case string:
set[idx] = fmt.Sprintf("'%s'", value.(string))
case bool:
return nil, errors.New("where not in: boolean is not a value set value")
case time.Time:
date := value.(time.Time)
set[idx] = date.Format(time.RFC3339)
default:
set[idx] = fmt.Sprintf("%v", value)
}
}
return &WhereClause{
expression: fmt.Sprintf("%s NOT IN (%s)", field, strings.Join(set, ",")),
}, nil
}
// Clause returns the where cluase.
func (wc *WhereClause) Clause() string {
return fmt.Sprintf("WHERE %s", wc.expression)
}
// Group will form a grouping around the expression.
func (wc *WhereClause) Group() {
wc.expression = fmt.Sprintf("(%s)", wc.expression)
}
// And will logical AND the expressions.
func (wc *WhereClause) And(where WhereExpression) {
wc.expression = fmt.Sprintf("%s AND %s", wc.expression, where.Expression())
}
// Or will logical OR the expressions.
func (wc *WhereClause) Or(where WhereExpression) {
wc.expression = fmt.Sprintf("%s OR %s", wc.expression, where.Expression())
}
// Expression will return the where expression.
func (wc *WhereClause) Expression() string {
return wc.expression
}
// OrderResult is the type of ordering of the query result.
type OrderResult string
const (
// OrderAsc will place the results in ascending order.
OrderAsc OrderResult = "ASC"
// OrderDesc will place the results in descending order.
OrderDesc OrderResult = "DESC"
)
// OrderNulls is where the null values are placed in the ordering.
type OrderNulls string
const (
// OrderNullsLast places the null values at the end of the ordering.
OrderNullsLast OrderNulls = "NULLS LAST"
// OrderNullsFirst places the null values at the start of the ordering.
OrderNullsFirst OrderNulls = "NULLS FIRST"
)
// OrderBy is the ordering structure of the SOQL query.
type OrderBy struct {
fieldOrder []string
result OrderResult
nulls OrderNulls
}
// Orderer is the interface for returning the SOQL ordering.
type Orderer interface {
Order() (string, error)
}
// NewOrderBy creates an OrderBy structure. If the order results is not ASC or DESC, an
// error will be returned.
func NewOrderBy(result OrderResult) (*OrderBy, error) {
switch result {
case OrderAsc, OrderDesc:
default:
return nil, fmt.Errorf("order by: %s is not a valid result ordering type", string(result))
}
return &OrderBy{
result: result,
}, nil
}
// FieldOrder is a list of fields in the ordering.
func (o *OrderBy) FieldOrder(fields ...string) {
o.fieldOrder = append(o.fieldOrder, fields...)
}
// NullOrdering sets the ordering, first or last, of the null values.
func (o *OrderBy) NullOrdering(nulls OrderNulls) error {
switch nulls {
case OrderNullsLast, OrderNullsFirst:
default:
return fmt.Errorf("order by: %s is not a valid null ordering type", string(nulls))
}
o.nulls = nulls
return nil
}
// Order returns the order by SOQL string.
func (o *OrderBy) Order() (string, error) {
switch o.result {
case OrderAsc, OrderDesc:
default:
return "", fmt.Errorf("order by: %s is not a valid result ordering type", string(o.result))
}
orderBy := "ORDER BY " + strings.Join(o.fieldOrder, ",") + " " + string(o.result)
if o.nulls != "" {
orderBy += " " + string(o.nulls)
}
return orderBy, nil
}