1/*
2Copyright 2014 The Perkeep Authors
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package main
18
19import (
20	"encoding/json"
21	"flag"
22	"fmt"
23	"io/ioutil"
24	"os"
25
26	"perkeep.org/internal/osutil"
27	"perkeep.org/pkg/blob"
28	"perkeep.org/pkg/client"
29	"perkeep.org/pkg/cmdmain"
30	"perkeep.org/pkg/schema"
31	"perkeep.org/pkg/search"
32)
33
34type searchNamesGetCmd struct{}
35type searchNamesSetCmd struct{}
36
37func init() {
38	osutil.AddSecretRingFlag()
39	cmdmain.RegisterMode("named-search-get", func(flags *flag.FlagSet) cmdmain.CommandRunner {
40		return new(searchNamesGetCmd)
41	})
42	cmdmain.RegisterMode("named-search-set", func(flags *flag.FlagSet) cmdmain.CommandRunner {
43		return new(searchNamesSetCmd)
44	})
45}
46
47func (c *searchNamesGetCmd) Describe() string { return "Get a named search's current value" }
48func (c *searchNamesSetCmd) Describe() string { return "Create or update a named search" }
49
50func (c *searchNamesGetCmd) Usage() {
51	fmt.Fprintln(os.Stderr, "pk named-search-get <name>")
52}
53func (c *searchNamesSetCmd) Usage() {
54	fmt.Fprintln(os.Stderr, "pk named-search-set <name> <new-search-expression>")
55}
56
57func (c *searchNamesGetCmd) RunCommand(args []string) error {
58	if len(args) != 1 {
59		return cmdmain.UsageError("expected 1 argument")
60	}
61	named := args[0]
62	gnr, err := getNamedSearch(named)
63	if err != nil {
64		return err
65	}
66	out, err := json.MarshalIndent(gnr, "  ", "")
67	if err != nil {
68		return err
69	}
70	fmt.Fprintln(cmdmain.Stdout, string(out))
71	return nil
72}
73
74func (c *searchNamesSetCmd) RunCommand(args []string) error {
75	if len(args) != 2 {
76		return cmdmain.UsageError("expected 2 arguments")
77	}
78	named, substitute := args[0], args[1]
79
80	cc := newClient("")
81	uh := client.NewUploadHandleFromString(substitute)
82	substpr, err := cc.Upload(ctxbg, uh)
83	if err != nil {
84		return err
85	}
86	var pn blob.Ref
87	claims := []*schema.Builder{}
88	gr, err := getNamedSearch(named)
89	if err == nil {
90		pn = gr.PermaRef
91	} else {
92		pnpr, err := cc.UploadAndSignBlob(ctxbg, schema.NewUnsignedPermanode())
93		if err != nil {
94			return err
95		}
96		pn = pnpr.BlobRef
97
98		claims = append(claims, schema.NewSetAttributeClaim(pn, "camliNamedSearch", named))
99		claims = append(claims, schema.NewSetAttributeClaim(pn, "title", fmt.Sprintf("named:%s", named)))
100	}
101	claims = append(claims, schema.NewSetAttributeClaim(pn, "camliContent", substpr.BlobRef.String()))
102	for _, claimBuilder := range claims {
103		_, err := cc.UploadAndSignBlob(ctxbg, claimBuilder)
104		if err != nil {
105			return err
106		}
107	}
108	snr := setNamedResponse{PermaRef: pn, SubstRef: substpr.BlobRef}
109	out, err := json.MarshalIndent(snr, "  ", "")
110	if err != nil {
111		return err
112	}
113	fmt.Fprintln(cmdmain.Stdout, string(out))
114	return nil
115}
116
117type getNamedResponse struct {
118	Named      string   `json:"named"`
119	Substitute string   `json:"substitute"`
120	PermaRef   blob.Ref `json:"permaRef"`
121	SubstRef   blob.Ref `json:"substRef"`
122}
123
124type setNamedResponse struct {
125	PermaRef blob.Ref `json:"permaRef"`
126	SubstRef blob.Ref `json:"substRef"`
127}
128
129func getNamedSearch(named string) (getNamedResponse, error) {
130	cc := newClient("")
131	var gnr getNamedResponse
132	gnr.Named = named
133	sr, err := cc.Query(ctxbg, search.NamedSearch(named))
134	if err != nil {
135		return gnr, err
136	}
137	if len(sr.Blobs) < 1 {
138		return gnr, fmt.Errorf("No named search found for: %s", named)
139	}
140	gnr.PermaRef = sr.Blobs[0].Blob
141	substRefS := sr.Describe.Meta.Get(gnr.PermaRef).Permanode.Attr.Get("camliContent")
142	br, ok := blob.Parse(substRefS)
143	if !ok {
144		return gnr, fmt.Errorf("Invalid blob ref: %s", substRefS)
145	}
146	reader, _, err := cc.Fetch(ctxbg, br)
147	if err != nil {
148		return gnr, err
149	}
150	result, err := ioutil.ReadAll(reader)
151	if err != nil {
152		return gnr, err
153	}
154	gnr.Substitute = string(result)
155	return gnr, nil
156}
157