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/url"
11	"strings"
12
13	"github.com/olivere/elastic/uritemplates"
14)
15
16// AliasesService returns the aliases associated with one or more indices, or the
17// indices associated with one or more aliases, or a combination of those filters.
18// See http://www.elastic.co/guide/en/elasticsearch/reference/6.7/indices-aliases.html.
19type AliasesService struct {
20	client *Client
21	index  []string
22	alias  []string
23	pretty bool
24}
25
26// NewAliasesService instantiates a new AliasesService.
27func NewAliasesService(client *Client) *AliasesService {
28	builder := &AliasesService{
29		client: client,
30	}
31	return builder
32}
33
34// Pretty asks Elasticsearch to indent the returned JSON.
35func (s *AliasesService) Pretty(pretty bool) *AliasesService {
36	s.pretty = pretty
37	return s
38}
39
40// Index adds one or more indices.
41func (s *AliasesService) Index(index ...string) *AliasesService {
42	s.index = append(s.index, index...)
43	return s
44}
45
46// Alias adds one or more aliases.
47func (s *AliasesService) Alias(alias ...string) *AliasesService {
48	s.alias = append(s.alias, alias...)
49	return s
50}
51
52// buildURL builds the URL for the operation.
53func (s *AliasesService) buildURL() (string, url.Values, error) {
54	var err error
55	var path string
56
57	if len(s.index) > 0 {
58		path, err = uritemplates.Expand("/{index}/_alias/{alias}", map[string]string{
59			"index": strings.Join(s.index, ","),
60			"alias": strings.Join(s.alias, ","),
61		})
62	} else {
63		path, err = uritemplates.Expand("/_alias/{alias}", map[string]string{
64			"alias": strings.Join(s.alias, ","),
65		})
66	}
67	if err != nil {
68		return "", url.Values{}, err
69	}
70	path = strings.TrimSuffix(path, "/")
71
72	// Add query string parameters
73	params := url.Values{}
74	if s.pretty {
75		params.Set("pretty", fmt.Sprintf("%v", s.pretty))
76	}
77	return path, params, nil
78}
79
80func (s *AliasesService) Do(ctx context.Context) (*AliasesResult, error) {
81	path, params, err := s.buildURL()
82	if err != nil {
83		return nil, err
84	}
85
86	// Get response
87	res, err := s.client.PerformRequest(ctx, PerformRequestOptions{
88		Method: "GET",
89		Path:   path,
90		Params: params,
91	})
92	if err != nil {
93		return nil, err
94	}
95
96	// {
97	//   "indexName" : {
98	//     "aliases" : {
99	//       "alias1" : { },
100	//       "alias2" : { }
101	//     }
102	//   },
103	//   "indexName2" : {
104	//     ...
105	//   },
106	// }
107	indexMap := make(map[string]struct {
108		Aliases map[string]struct {
109			IsWriteIndex bool `json:"is_write_index"`
110		} `json:"aliases"`
111	})
112	if err := s.client.decoder.Decode(res.Body, &indexMap); err != nil {
113		return nil, err
114	}
115
116	// Each (indexName, _)
117	ret := &AliasesResult{
118		Indices: make(map[string]indexResult),
119	}
120	for indexName, indexData := range indexMap {
121		if indexData.Aliases == nil {
122			continue
123		}
124
125		indexOut, found := ret.Indices[indexName]
126		if !found {
127			indexOut = indexResult{Aliases: make([]aliasResult, 0)}
128		}
129
130		// { "aliases" : { ... } }
131		for aliasName, aliasData := range indexData.Aliases {
132			aliasRes := aliasResult{AliasName: aliasName, IsWriteIndex: aliasData.IsWriteIndex}
133			indexOut.Aliases = append(indexOut.Aliases, aliasRes)
134		}
135
136		ret.Indices[indexName] = indexOut
137	}
138
139	return ret, nil
140}
141
142// -- Result of an alias request.
143
144// AliasesResult is the outcome of calling AliasesService.Do.
145type AliasesResult struct {
146	Indices map[string]indexResult
147}
148
149type indexResult struct {
150	Aliases []aliasResult
151}
152
153type aliasResult struct {
154	AliasName    string
155	IsWriteIndex bool
156}
157
158// IndicesByAlias returns all indices given a specific alias name.
159func (ar AliasesResult) IndicesByAlias(aliasName string) []string {
160	var indices []string
161	for indexName, indexInfo := range ar.Indices {
162		for _, aliasInfo := range indexInfo.Aliases {
163			if aliasInfo.AliasName == aliasName {
164				indices = append(indices, indexName)
165			}
166		}
167	}
168	return indices
169}
170
171// HasAlias returns true if the index has a specific alias.
172func (ir indexResult) HasAlias(aliasName string) bool {
173	for _, alias := range ir.Aliases {
174		if alias.AliasName == aliasName {
175			return true
176		}
177	}
178	return false
179}
180