1// Copyright 2012-present Oliver Eilhard. All rights reserved.
2// Use of this source code is governed by a MIT-license.
3// See http://olivere.mit-license.org/license.txt for details.
4
5package elastic
6
7import (
8	"context"
9	"fmt"
10	"net/http"
11	"net/url"
12	"strings"
13
14	"gopkg.in/olivere/elastic.v5/uritemplates"
15)
16
17// IndicesExistsService checks if an index or indices exist or not.
18//
19// See https://www.elastic.co/guide/en/elasticsearch/reference/5.2/indices-exists.html
20// for details.
21type IndicesExistsService struct {
22	client            *Client
23	pretty            bool
24	index             []string
25	ignoreUnavailable *bool
26	allowNoIndices    *bool
27	expandWildcards   string
28	local             *bool
29}
30
31// NewIndicesExistsService creates and initializes a new IndicesExistsService.
32func NewIndicesExistsService(client *Client) *IndicesExistsService {
33	return &IndicesExistsService{
34		client: client,
35		index:  make([]string, 0),
36	}
37}
38
39// Index is a list of one or more indices to check.
40func (s *IndicesExistsService) Index(index []string) *IndicesExistsService {
41	s.index = index
42	return s
43}
44
45// AllowNoIndices indicates whether to ignore if a wildcard indices expression
46// resolves into no concrete indices. (This includes `_all` string or
47// when no indices have been specified).
48func (s *IndicesExistsService) AllowNoIndices(allowNoIndices bool) *IndicesExistsService {
49	s.allowNoIndices = &allowNoIndices
50	return s
51}
52
53// ExpandWildcards indicates whether to expand wildcard expression to
54// concrete indices that are open, closed or both.
55func (s *IndicesExistsService) ExpandWildcards(expandWildcards string) *IndicesExistsService {
56	s.expandWildcards = expandWildcards
57	return s
58}
59
60// Local, when set, returns local information and does not retrieve the state
61// from master node (default: false).
62func (s *IndicesExistsService) Local(local bool) *IndicesExistsService {
63	s.local = &local
64	return s
65}
66
67// IgnoreUnavailable indicates whether specified concrete indices should be
68// ignored when unavailable (missing or closed).
69func (s *IndicesExistsService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesExistsService {
70	s.ignoreUnavailable = &ignoreUnavailable
71	return s
72}
73
74// Pretty indicates that the JSON response be indented and human readable.
75func (s *IndicesExistsService) Pretty(pretty bool) *IndicesExistsService {
76	s.pretty = pretty
77	return s
78}
79
80// buildURL builds the URL for the operation.
81func (s *IndicesExistsService) buildURL() (string, url.Values, error) {
82	// Build URL
83	path, err := uritemplates.Expand("/{index}", map[string]string{
84		"index": strings.Join(s.index, ","),
85	})
86	if err != nil {
87		return "", url.Values{}, err
88	}
89
90	// Add query string parameters
91	params := url.Values{}
92	if s.pretty {
93		params.Set("pretty", "1")
94	}
95	if s.local != nil {
96		params.Set("local", fmt.Sprintf("%v", *s.local))
97	}
98	if s.ignoreUnavailable != nil {
99		params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
100	}
101	if s.allowNoIndices != nil {
102		params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
103	}
104	if s.expandWildcards != "" {
105		params.Set("expand_wildcards", s.expandWildcards)
106	}
107	return path, params, nil
108}
109
110// Validate checks if the operation is valid.
111func (s *IndicesExistsService) Validate() error {
112	var invalid []string
113	if len(s.index) == 0 {
114		invalid = append(invalid, "Index")
115	}
116	if len(invalid) > 0 {
117		return fmt.Errorf("missing required fields: %v", invalid)
118	}
119	return nil
120}
121
122// Do executes the operation.
123func (s *IndicesExistsService) Do(ctx context.Context) (bool, error) {
124	// Check pre-conditions
125	if err := s.Validate(); err != nil {
126		return false, err
127	}
128
129	// Get URL for request
130	path, params, err := s.buildURL()
131	if err != nil {
132		return false, err
133	}
134
135	// Get HTTP response
136	res, err := s.client.PerformRequest(ctx, "HEAD", path, params, nil, 404)
137	if err != nil {
138		return false, err
139	}
140
141	// Return operation response
142	switch res.StatusCode {
143	case http.StatusOK:
144		return true, nil
145	case http.StatusNotFound:
146		return false, nil
147	default:
148		return false, fmt.Errorf("elastic: got HTTP code %d when it should have been either 200 or 404", res.StatusCode)
149	}
150}
151