...

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

Documentation: github.com/udacity/graphb

     1  // graphb is a Graph QL client query builder.
     2  // public.go contains public functions (not struct methods) to construct Query(s) and Field(s).
     3  
     4  package graphb
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  // StringFromChan builds a string from a channel, assuming the channel has been closed.
    13  func StringFromChan(c <-chan string) string {
    14  	var strs []string
    15  	for str := range c {
    16  		strs = append(strs, str)
    17  	}
    18  	return strings.Join(strs, "")
    19  }
    20  
    21  ///////////////////
    22  // Field Factory //
    23  ///////////////////
    24  
    25  // NewField uses functional options to construct a new Field and returns the pointer to it.
    26  // On error, the pointer is nil.
    27  // To know more about this design pattern, see https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
    28  func NewField(name string, options ...FieldOptionInterface) *Field {
    29  	f := &Field{Name: name}
    30  	for _, op := range options {
    31  		if err := op.runFieldOption(f); err != nil {
    32  			f.E = errors.WithStack(err)
    33  			return f
    34  		}
    35  	}
    36  	return f
    37  }
    38  
    39  // FieldOptionInterface implements functional options for NewField().
    40  type FieldOptionInterface interface {
    41  	runFieldOption(f *Field) error
    42  }
    43  
    44  // FieldOption implements FieldOptionInterface
    45  type FieldOption func(field *Field) error
    46  
    47  func (fco FieldOption) runFieldOption(f *Field) error {
    48  	return fco(f)
    49  }
    50  
    51  // OfFields returns a FieldOption which sets a list of sub fields of given names of the targeting field.
    52  // All the sub fields only have one level which is their names. That is, no sub fields have sub fields.
    53  func OfFields(name ...string) FieldOption {
    54  	return func(f *Field) error {
    55  		f.setFields(Fields(name...))
    56  		return nil
    57  	}
    58  }
    59  
    60  func OfAlias(alias string) FieldOption {
    61  	return func(f *Field) error {
    62  		f.Alias = alias
    63  		return errors.WithStack(f.checkAlias())
    64  	}
    65  }
    66  
    67  // OfArguments returns a FieldOption which sets the arguments of the targeting field.
    68  func OfArguments(arguments ...Argument) FieldOption {
    69  	return func(f *Field) error {
    70  		f.Arguments = arguments
    71  		return nil
    72  	}
    73  }
    74  
    75  ///////////////////
    76  // Query Factory //
    77  ///////////////////
    78  
    79  // NewQuery uses functional options to construct a new Query and returns the pointer to it.
    80  // On error, the pointer is nil.
    81  // Type is required.
    82  // Other options such as operation name and alias are optional.
    83  func NewQuery(Type operationType, options ...QueryOptionInterface) *Query {
    84  	// todo: change to new style error handling
    85  	q := &Query{Type: Type}
    86  
    87  	for _, op := range options {
    88  		if err := op.runQueryOption(q); err != nil {
    89  			q.E = errors.WithStack(err)
    90  			return q
    91  		}
    92  	}
    93  	return q
    94  }
    95  
    96  // QueryOptionInterface implements functional options for NewQuery().
    97  type QueryOptionInterface interface {
    98  	runQueryOption(q *Query) error
    99  }
   100  
   101  // QueryOption implements QueryOptionInterface
   102  type QueryOption func(query *Query) error
   103  
   104  func (qo QueryOption) runQueryOption(query *Query) error {
   105  	return qo(query)
   106  }
   107  
   108  // OfName returns a QueryOption which validates and sets the operation name of a query.
   109  func OfName(name string) QueryOption {
   110  	return func(query *Query) error {
   111  		query.Name = name
   112  		if err := query.checkName(); err != nil {
   113  			return errors.WithStack(err)
   114  		}
   115  		return nil
   116  	}
   117  }
   118  
   119  ////////////////////////////
   120  // fieldContainer Factory //
   121  ////////////////////////////
   122  type fieldContainer interface {
   123  	getFields() []*Field
   124  	setFields([]*Field)
   125  }
   126  
   127  // FieldContainerOption implements FieldOptionInterface and QueryOptionInterface,
   128  // which means, it can be used as the functional option for both NewQuery() and NewField().
   129  // FieldContainerOption is a function which takes in a fieldContainer and config it.
   130  // Both Query and Field are fieldContainer.
   131  type FieldContainerOption func(fc fieldContainer) error
   132  
   133  func (fco FieldContainerOption) runQueryOption(q *Query) error {
   134  	return fco(q)
   135  }
   136  
   137  func (fco FieldContainerOption) runFieldOption(f *Field) error {
   138  	return fco(f)
   139  }
   140  
   141  // OfField returns a FieldContainerOption and has the same parameter signature of
   142  // NewField(name string, options ...FieldOptionInterface) (*Field, error)
   143  func OfField(name string, options ...FieldOptionInterface) FieldContainerOption {
   144  	return func(fc fieldContainer) error {
   145  		f := NewField(name, options...)
   146  		if f.E != nil {
   147  			return errors.WithStack(f.E)
   148  		}
   149  		fc.setFields(append(fc.getFields(), f))
   150  		return nil
   151  	}
   152  }
   153  
   154  // Fields takes a list of strings and make them a slice of *Field.
   155  // This is useful when you want fields with no sub fields.
   156  // For example:
   157  //	query { courses { id, key } }
   158  // can be written as:
   159  // 	Query{
   160  //		Type: "query",
   161  //		Fields: []*Field{
   162  //			{
   163  //				Name:      "courses",
   164  //				Fields:    Fields("id", "key"),
   165  //			},
   166  //		},
   167  //	}
   168  func Fields(args ...string) []*Field {
   169  	fields := make([]*Field, len(args))
   170  	for i, name := range args {
   171  		fields[i] = &Field{
   172  			Name: name,
   173  		}
   174  	}
   175  	return fields
   176  }
   177  

View as plain text