...

Source file src/github.com/udacity/graphb/query.go

Documentation: github.com/udacity/graphb

     1  package graphb
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/pkg/errors"
     8  )
     9  
    10  // Query represents a GraphQL query.
    11  // Though all fields (Go struct field, not GraphQL field) of this struct is public,
    12  // the author recommends you to use functions in public.go.
    13  type Query struct {
    14  	Type   operationType // The operation type is either query, mutation, or subscription.
    15  	Name   string        // The operation name is a meaningful and explicit name for your operation.
    16  	Fields []*Field
    17  	E      error
    18  }
    19  
    20  // implements fieldContainer
    21  func (q *Query) getFields() []*Field {
    22  	return q.Fields
    23  }
    24  
    25  func (q *Query) setFields(fs []*Field) {
    26  	q.Fields = fs
    27  }
    28  
    29  // StringChan returns a string channel and an error.
    30  // When error is not nil, the channel is nil.
    31  // When error is nil, the channel is guaranteed to be closed.
    32  // Warning: One should never receive from a nil channel for eternity awaits by a nil channel.
    33  func (q *Query) StringChan() (<-chan string, error) {
    34  	ch := make(chan string)
    35  
    36  	if err := q.check(); err != nil {
    37  		close(ch)
    38  		return ch, errors.WithStack(err)
    39  	}
    40  
    41  	for _, f := range q.Fields {
    42  		if f == nil {
    43  			close(ch)
    44  			return ch, errors.WithStack(NilFieldErr{})
    45  		}
    46  		if err := f.check(); err != nil {
    47  			close(ch)
    48  			return ch, errors.WithStack(err)
    49  		}
    50  	}
    51  	return q.stringChan(), nil
    52  }
    53  
    54  // StringChan returns a read only channel which is guaranteed to be closed in the future.
    55  func (q *Query) stringChan() <-chan string {
    56  	tokenChan := make(chan string)
    57  	go func() {
    58  		tokenChan <- strings.ToLower(string(q.Type))
    59  		// emit operation name
    60  		if q.Name != "" {
    61  			tokenChan <- tokenSpace
    62  			tokenChan <- q.Name
    63  		}
    64  		// emit fields
    65  		tokenChan <- tokenLB
    66  		for i, field := range q.Fields {
    67  			if i != 0 {
    68  				tokenChan <- tokenComma
    69  			}
    70  			strs := field.stringChan()
    71  			for str := range strs {
    72  				tokenChan <- str
    73  			}
    74  		}
    75  		tokenChan <- tokenRB
    76  		close(tokenChan)
    77  	}()
    78  	return tokenChan
    79  }
    80  
    81  func (q *Query) check() error {
    82  	// check query
    83  	if !isValidOperationType(q.Type) {
    84  		return errors.WithStack(InvalidOperationTypeErr{q.Type})
    85  	}
    86  	if err := q.checkName(); err != nil {
    87  		return errors.WithStack(err)
    88  	}
    89  	return nil
    90  }
    91  
    92  func (q *Query) checkName() error {
    93  	if q.Name != "" && !validName.MatchString(q.Name) {
    94  		return errors.WithStack(InvalidNameErr{operationName, q.Name})
    95  	}
    96  	return nil
    97  }
    98  
    99  ////////////////
   100  // Public API //
   101  ////////////////
   102  
   103  // MakeQuery constructs a Query of the given type and returns a pointer of it.
   104  func MakeQuery(Type operationType) *Query {
   105  	return &Query{Type: Type}
   106  }
   107  
   108  // JSON returns a json string with "query" field.
   109  func (q *Query) JSON() (string, error) {
   110  	strCh, err := q.StringChan()
   111  	if err != nil {
   112  		return "", errors.WithStack(err)
   113  	}
   114  	s := StringFromChan(strCh)
   115  	return fmt.Sprintf(`{"query":"%s"}`, strings.Replace(s, `"`, `\"`, -1)), nil
   116  }
   117  
   118  // SetName sets the Name field of this Query.
   119  func (q *Query) SetName(name string) *Query {
   120  	q.Name = name
   121  	return q
   122  }
   123  
   124  // GetField return the field identified by the name. Nil if not exist.
   125  func (q *Query) GetField(name string) *Field {
   126  	for _, f := range q.Fields {
   127  		if f.Name == name {
   128  			return f
   129  		}
   130  	}
   131  	return nil
   132  }
   133  
   134  // SetFields sets the Fields field of this Query.
   135  // If q.Fields already contains data, they will be replaced.
   136  func (q *Query) SetFields(fields ...*Field) *Query {
   137  	q.Fields = fields
   138  	return q
   139  }
   140  
   141  // AddFields adds to the Fields field of this Query.
   142  func (q *Query) AddFields(fields ...*Field) *Query {
   143  	q.Fields = append(q.Fields, fields...)
   144  	return q
   145  }
   146  

View as plain text