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