soql/formatter.go (315 lines of code) (raw):
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
}