github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/remote/registry/regclient/search.go (about)

     1  package regclient
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"strings"
     9  
    10  	"github.com/qri-io/dataset"
    11  	"github.com/qri-io/qri/remote/registry"
    12  )
    13  
    14  // SearchFilter stores various types of filters that may be applied
    15  // to a search
    16  type SearchFilter struct {
    17  	// Type denotes the ype of search filter
    18  	Type string
    19  	// Relation indicates the relation between the key and value
    20  	// supported options include ["eq"|"neq"|"gt"|"gte"|"lt"|"lte"]
    21  	Relation string
    22  	// Key corresponds to the name of the index mapping that we wish to
    23  	// apply the filter to
    24  	Key string
    25  	// Value is the predicate of the subject-relation-predicate triple
    26  	// eg. [key=timestamp] [gte] [value=[today]]
    27  	Value interface{}
    28  }
    29  
    30  // SearchParams contains the parameters that are passed to a
    31  // Client.Search method
    32  type SearchParams struct {
    33  	QueryString string
    34  	Filters     []SearchFilter
    35  	Limit       int
    36  	Offset      int
    37  }
    38  
    39  // Search makes a registry search request
    40  func (c Client) Search(p *SearchParams) ([]*dataset.Dataset, error) {
    41  	params := &registry.SearchParams{
    42  		Q: p.QueryString,
    43  		//Filters: p.Filters,
    44  		Limit:  p.Limit,
    45  		Offset: p.Offset,
    46  	}
    47  	results, err := c.doJSONSearchReq("GET", params)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	return results, nil
    52  }
    53  
    54  func (c Client) prepPostReq(s *registry.SearchParams) (*http.Request, error) {
    55  	data, err := json.Marshal(s)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	req, err := http.NewRequest("POST", fmt.Sprintf("%s/registry/search", c.cfg.Location), bytes.NewReader(data))
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	req.Header.Set("Content-Type", "application/json")
    64  	return req, nil
    65  }
    66  
    67  func (c Client) prepGetReq(s *registry.SearchParams) (*http.Request, error) {
    68  	req, err := http.NewRequest("GET", fmt.Sprintf("%s/registry/search", c.cfg.Location), nil)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	q := req.URL.Query()
    73  	q.Add("q", s.Q)
    74  	if s.Limit > 0 {
    75  		q.Add("limit", fmt.Sprintf("%d", s.Limit))
    76  	}
    77  	if s.Offset > -1 {
    78  		q.Add("offset", fmt.Sprintf("%d", s.Offset))
    79  	}
    80  	req.URL.RawQuery = q.Encode()
    81  	return req, nil
    82  }
    83  
    84  func (c Client) doJSONSearchReq(method string, s *registry.SearchParams) (results []*dataset.Dataset, err error) {
    85  	if c.cfg.Location == "" {
    86  		return nil, ErrNoRegistry
    87  	}
    88  	// req := &http.Request{}
    89  	var req *http.Request
    90  	switch method {
    91  	case "POST":
    92  		req, err = c.prepPostReq(s)
    93  	case "GET":
    94  		req, err = c.prepGetReq(s)
    95  	}
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	res, err := c.httpClient.Do(req)
   101  	if err != nil {
   102  		if strings.Contains(err.Error(), "no such host") {
   103  			return nil, ErrNoRegistry
   104  		}
   105  		return nil, err
   106  	}
   107  	// add response to an envelope
   108  	env := struct {
   109  		Data []*dataset.Dataset
   110  		Meta struct {
   111  			Error  string
   112  			Status string
   113  			Code   int
   114  		}
   115  	}{}
   116  
   117  	if err := json.NewDecoder(res.Body).Decode(&env); err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	if res.StatusCode != http.StatusOK {
   122  		return nil, fmt.Errorf("error %d: %s", res.StatusCode, env.Meta.Error)
   123  	}
   124  	return env.Data, nil
   125  }