1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-07-22 17:18:37 +00:00

Add OAuth2 authentication

This commit is contained in:
Frédéric Guillot 2017-11-22 22:22:33 -08:00
parent 9877051f12
commit cc6d272eb7
351 changed files with 81664 additions and 55 deletions

209
vendor/google.golang.org/appengine/search/doc.go generated vendored Normal file
View file

@ -0,0 +1,209 @@
// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package search provides a client for App Engine's search service.
Basic Operations
Indexes contain documents. Each index is identified by its name: a
human-readable ASCII string.
Within an index, documents are associated with an ID, which is also
a human-readable ASCII string. A document's contents are a mapping from
case-sensitive field names to values. Valid types for field values are:
- string,
- search.Atom,
- search.HTML,
- time.Time (stored with millisecond precision),
- float64 (value between -2,147,483,647 and 2,147,483,647 inclusive),
- appengine.GeoPoint.
The Get and Put methods on an Index load and save a document.
A document's contents are typically represented by a struct pointer.
Example code:
type Doc struct {
Author string
Comment string
Creation time.Time
}
index, err := search.Open("comments")
if err != nil {
return err
}
newID, err := index.Put(ctx, "", &Doc{
Author: "gopher",
Comment: "the truth of the matter",
Creation: time.Now(),
})
if err != nil {
return err
}
A single document can be retrieved by its ID. Pass a destination struct
to Get to hold the resulting document.
var doc Doc
err := index.Get(ctx, id, &doc)
if err != nil {
return err
}
Search and Listing Documents
Indexes have two methods for retrieving multiple documents at once: Search and
List.
Searching an index for a query will result in an iterator. As with an iterator
from package datastore, pass a destination struct to Next to decode the next
result. Next will return Done when the iterator is exhausted.
for t := index.Search(ctx, "Comment:truth", nil); ; {
var doc Doc
id, err := t.Next(&doc)
if err == search.Done {
break
}
if err != nil {
return err
}
fmt.Fprintf(w, "%s -> %#v\n", id, doc)
}
Search takes a string query to determine which documents to return. The query
can be simple, such as a single word to match, or complex. The query
language is described at
https://cloud.google.com/appengine/docs/go/search/query_strings
Search also takes an optional SearchOptions struct which gives much more
control over how results are calculated and returned.
Call List to iterate over all documents in an index.
for t := index.List(ctx, nil); ; {
var doc Doc
id, err := t.Next(&doc)
if err == search.Done {
break
}
if err != nil {
return err
}
fmt.Fprintf(w, "%s -> %#v\n", id, doc)
}
Fields and Facets
A document's contents can be represented by a variety of types. These are
typically struct pointers, but they can also be represented by any type
implementing the FieldLoadSaver interface. The FieldLoadSaver allows metadata
to be set for the document with the DocumentMetadata type. Struct pointers are
more strongly typed and are easier to use; FieldLoadSavers are more flexible.
A document's contents can be expressed in two ways: fields and facets.
Fields are the most common way of providing content for documents. Fields can
store data in multiple types and can be matched in searches using query
strings.
Facets provide a way to attach categorical information to a document. The only
valid types for facets are search.Atom and float64. Facets allow search
results to contain summaries of the categories matched in a search, and to
restrict searches to only match against specific categories.
By default, for struct pointers, all of the struct fields are used as document
fields, and the field name used is the same as on the struct (and hence must
start with an upper case letter). Struct fields may have a
`search:"name,options"` tag. The name must start with a letter and be
composed only of word characters. A "-" tag name means that the field will be
ignored. If options is "facet" then the struct field will be used as a
document facet. If options is "" then the comma may be omitted. There are no
other recognized options.
Example code:
// A and B are renamed to a and b.
// A, C and I are facets.
// D's tag is equivalent to having no tag at all (E).
// F and G are ignored entirely by the search package.
// I has tag information for both the search and json packages.
type TaggedStruct struct {
A float64 `search:"a,facet"`
B float64 `search:"b"`
C float64 `search:",facet"`
D float64 `search:""`
E float64
F float64 `search:"-"`
G float64 `search:"-,facet"`
I float64 `search:",facet" json:"i"`
}
The FieldLoadSaver Interface
A document's contents can also be represented by any type that implements the
FieldLoadSaver interface. This type may be a struct pointer, but it
does not have to be. The search package will call Load when loading the
document's contents, and Save when saving them. In addition to a slice of
Fields, the Load and Save methods also use the DocumentMetadata type to
provide additional information about a document (such as its Rank, or set of
Facets). Possible uses for this interface include deriving non-stored fields,
verifying fields or setting specific languages for string and HTML fields.
Example code:
type CustomFieldsExample struct {
// Item's title and which language it is in.
Title string
Lang string
// Mass, in grams.
Mass int
}
func (x *CustomFieldsExample) Load(fields []search.Field, meta *search.DocumentMetadata) error {
// Load the title field, failing if any other field is found.
for _, f := range fields {
if f.Name != "title" {
return fmt.Errorf("unknown field %q", f.Name)
}
s, ok := f.Value.(string)
if !ok {
return fmt.Errorf("unsupported type %T for field %q", f.Value, f.Name)
}
x.Title = s
x.Lang = f.Language
}
// Load the mass facet, failing if any other facet is found.
for _, f := range meta.Facets {
if f.Name != "mass" {
return fmt.Errorf("unknown facet %q", f.Name)
}
m, ok := f.Value.(float64)
if !ok {
return fmt.Errorf("unsupported type %T for facet %q", f.Value, f.Name)
}
x.Mass = int(m)
}
return nil
}
func (x *CustomFieldsExample) Save() ([]search.Field, *search.DocumentMetadata, error) {
fields := []search.Field{
{Name: "title", Value: x.Title, Language: x.Lang},
}
meta := &search.DocumentMetadata{
Facets: {
{Name: "mass", Value: float64(x.Mass)},
},
}
return fields, meta, nil
}
*/
package search

82
vendor/google.golang.org/appengine/search/field.go generated vendored Normal file
View file

@ -0,0 +1,82 @@
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package search
// Field is a name/value pair. A search index's document can be loaded and
// saved as a sequence of Fields.
type Field struct {
// Name is the field name. A valid field name matches /[A-Za-z][A-Za-z0-9_]*/.
Name string
// Value is the field value. The valid types are:
// - string,
// - search.Atom,
// - search.HTML,
// - time.Time (stored with millisecond precision),
// - float64,
// - GeoPoint.
Value interface{}
// Language is a two-letter ISO 639-1 code for the field's language,
// defaulting to "en" if nothing is specified. It may only be specified for
// fields of type string and search.HTML.
Language string
// Derived marks fields that were calculated as a result of a
// FieldExpression provided to Search. This field is ignored when saving a
// document.
Derived bool
}
// Facet is a name/value pair which is used to add categorical information to a
// document.
type Facet struct {
// Name is the facet name. A valid facet name matches /[A-Za-z][A-Za-z0-9_]*/.
// A facet name cannot be longer than 500 characters.
Name string
// Value is the facet value.
//
// When being used in documents (for example, in
// DocumentMetadata.Facets), the valid types are:
// - search.Atom,
// - float64.
//
// When being used in SearchOptions.Refinements or being returned
// in FacetResult, the valid types are:
// - search.Atom,
// - search.Range.
Value interface{}
}
// DocumentMetadata is a struct containing information describing a given document.
type DocumentMetadata struct {
// Rank is an integer specifying the order the document will be returned in
// search results. If zero, the rank will be set to the number of seconds since
// 2011-01-01 00:00:00 UTC when being Put into an index.
Rank int
// Facets is the set of facets for this document.
Facets []Facet
}
// FieldLoadSaver can be converted from and to a slice of Fields
// with additional document metadata.
type FieldLoadSaver interface {
Load([]Field, *DocumentMetadata) error
Save() ([]Field, *DocumentMetadata, error)
}
// FieldList converts a []Field to implement FieldLoadSaver.
type FieldList []Field
// Load loads all of the provided fields into l.
// It does not first reset *l to an empty slice.
func (l *FieldList) Load(f []Field, _ *DocumentMetadata) error {
*l = append(*l, f...)
return nil
}
// Save returns all of l's fields as a slice of Fields.
func (l *FieldList) Save() ([]Field, *DocumentMetadata, error) {
return *l, nil, nil
}
var _ FieldLoadSaver = (*FieldList)(nil)

1121
vendor/google.golang.org/appengine/search/search.go generated vendored Normal file

File diff suppressed because it is too large Load diff

1000
vendor/google.golang.org/appengine/search/search_test.go generated vendored Normal file

File diff suppressed because it is too large Load diff

251
vendor/google.golang.org/appengine/search/struct.go generated vendored Normal file
View file

@ -0,0 +1,251 @@
// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package search
import (
"fmt"
"reflect"
"strings"
"sync"
)
// ErrFieldMismatch is returned when a field is to be loaded into a different
// than the one it was stored from, or when a field is missing or unexported in
// the destination struct.
type ErrFieldMismatch struct {
FieldName string
Reason string
}
func (e *ErrFieldMismatch) Error() string {
return fmt.Sprintf("search: cannot load field %q: %s", e.FieldName, e.Reason)
}
// ErrFacetMismatch is returned when a facet is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct. StructType is the type of the struct
// pointed to by the destination argument passed to Iterator.Next.
type ErrFacetMismatch struct {
StructType reflect.Type
FacetName string
Reason string
}
func (e *ErrFacetMismatch) Error() string {
return fmt.Sprintf("search: cannot load facet %q into a %q: %s", e.FacetName, e.StructType, e.Reason)
}
// structCodec defines how to convert a given struct to/from a search document.
type structCodec struct {
// byIndex returns the struct tag for the i'th struct field.
byIndex []structTag
// fieldByName returns the index of the struct field for the given field name.
fieldByName map[string]int
// facetByName returns the index of the struct field for the given facet name,
facetByName map[string]int
}
// structTag holds a structured version of each struct field's parsed tag.
type structTag struct {
name string
facet bool
ignore bool
}
var (
codecsMu sync.RWMutex
codecs = map[reflect.Type]*structCodec{}
)
func loadCodec(t reflect.Type) (*structCodec, error) {
codecsMu.RLock()
codec, ok := codecs[t]
codecsMu.RUnlock()
if ok {
return codec, nil
}
codecsMu.Lock()
defer codecsMu.Unlock()
if codec, ok := codecs[t]; ok {
return codec, nil
}
codec = &structCodec{
fieldByName: make(map[string]int),
facetByName: make(map[string]int),
}
for i, I := 0, t.NumField(); i < I; i++ {
f := t.Field(i)
name, opts := f.Tag.Get("search"), ""
if i := strings.Index(name, ","); i != -1 {
name, opts = name[:i], name[i+1:]
}
ignore := false
if name == "-" {
ignore = true
} else if name == "" {
name = f.Name
} else if !validFieldName(name) {
return nil, fmt.Errorf("search: struct tag has invalid field name: %q", name)
}
facet := opts == "facet"
codec.byIndex = append(codec.byIndex, structTag{name: name, facet: facet, ignore: ignore})
if facet {
codec.facetByName[name] = i
} else {
codec.fieldByName[name] = i
}
}
codecs[t] = codec
return codec, nil
}
// structFLS adapts a struct to be a FieldLoadSaver.
type structFLS struct {
v reflect.Value
codec *structCodec
}
func (s structFLS) Load(fields []Field, meta *DocumentMetadata) error {
var err error
for _, field := range fields {
i, ok := s.codec.fieldByName[field.Name]
if !ok {
// Note the error, but keep going.
err = &ErrFieldMismatch{
FieldName: field.Name,
Reason: "no such struct field",
}
continue
}
f := s.v.Field(i)
if !f.CanSet() {
// Note the error, but keep going.
err = &ErrFieldMismatch{
FieldName: field.Name,
Reason: "cannot set struct field",
}
continue
}
v := reflect.ValueOf(field.Value)
if ft, vt := f.Type(), v.Type(); ft != vt {
err = &ErrFieldMismatch{
FieldName: field.Name,
Reason: fmt.Sprintf("type mismatch: %v for %v data", ft, vt),
}
continue
}
f.Set(v)
}
if meta == nil {
return err
}
for _, facet := range meta.Facets {
i, ok := s.codec.facetByName[facet.Name]
if !ok {
// Note the error, but keep going.
if err == nil {
err = &ErrFacetMismatch{
StructType: s.v.Type(),
FacetName: facet.Name,
Reason: "no matching field found",
}
}
continue
}
f := s.v.Field(i)
if !f.CanSet() {
// Note the error, but keep going.
if err == nil {
err = &ErrFacetMismatch{
StructType: s.v.Type(),
FacetName: facet.Name,
Reason: "unable to set unexported field of struct",
}
}
continue
}
v := reflect.ValueOf(facet.Value)
if ft, vt := f.Type(), v.Type(); ft != vt {
if err == nil {
err = &ErrFacetMismatch{
StructType: s.v.Type(),
FacetName: facet.Name,
Reason: fmt.Sprintf("type mismatch: %v for %d data", ft, vt),
}
continue
}
}
f.Set(v)
}
return err
}
func (s structFLS) Save() ([]Field, *DocumentMetadata, error) {
fields := make([]Field, 0, len(s.codec.fieldByName))
var facets []Facet
for i, tag := range s.codec.byIndex {
if tag.ignore {
continue
}
f := s.v.Field(i)
if !f.CanSet() {
continue
}
if tag.facet {
facets = append(facets, Facet{Name: tag.name, Value: f.Interface()})
} else {
fields = append(fields, Field{Name: tag.name, Value: f.Interface()})
}
}
return fields, &DocumentMetadata{Facets: facets}, nil
}
// newStructFLS returns a FieldLoadSaver for the struct pointer p.
func newStructFLS(p interface{}) (FieldLoadSaver, error) {
v := reflect.ValueOf(p)
if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
return nil, ErrInvalidDocumentType
}
codec, err := loadCodec(v.Elem().Type())
if err != nil {
return nil, err
}
return structFLS{v.Elem(), codec}, nil
}
func loadStructWithMeta(dst interface{}, f []Field, meta *DocumentMetadata) error {
x, err := newStructFLS(dst)
if err != nil {
return err
}
return x.Load(f, meta)
}
func saveStructWithMeta(src interface{}) ([]Field, *DocumentMetadata, error) {
x, err := newStructFLS(src)
if err != nil {
return nil, nil, err
}
return x.Save()
}
// LoadStruct loads the fields from f to dst. dst must be a struct pointer.
func LoadStruct(dst interface{}, f []Field) error {
return loadStructWithMeta(dst, f, nil)
}
// SaveStruct returns the fields from src as a slice of Field.
// src must be a struct pointer.
func SaveStruct(src interface{}) ([]Field, error) {
f, _, err := saveStructWithMeta(src)
return f, err
}

View file

@ -0,0 +1,213 @@
// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package search
import (
"reflect"
"testing"
)
func TestLoadingStruct(t *testing.T) {
testCases := []struct {
desc string
fields []Field
meta *DocumentMetadata
want interface{}
wantErr bool
}{
{
desc: "Basic struct",
fields: []Field{
{Name: "Name", Value: "Gopher"},
{Name: "Legs", Value: float64(4)},
},
want: &struct {
Name string
Legs float64
}{"Gopher", 4},
},
{
desc: "Struct with tags",
fields: []Field{
{Name: "Name", Value: "Gopher"},
{Name: "about", Value: "Likes slide rules."},
},
meta: &DocumentMetadata{Facets: []Facet{
{Name: "Legs", Value: float64(4)},
{Name: "Fur", Value: Atom("furry")},
}},
want: &struct {
Name string
Info string `search:"about"`
Legs float64 `search:",facet"`
Fuzz Atom `search:"Fur,facet"`
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
},
{
desc: "Bad field from tag",
want: &struct {
AlphaBeta string `search:"αβ"`
}{},
wantErr: true,
},
{
desc: "Ignore missing field",
fields: []Field{
{Name: "Meaning", Value: float64(42)},
},
want: &struct{}{},
wantErr: true,
},
{
desc: "Ignore unsettable field",
fields: []Field{
{Name: "meaning", Value: float64(42)},
},
want: &struct{ meaning float64 }{}, // field not populated.
wantErr: true,
},
{
desc: "Error on missing facet",
meta: &DocumentMetadata{Facets: []Facet{
{Name: "Set", Value: Atom("yes")},
{Name: "Missing", Value: Atom("no")},
}},
want: &struct {
Set Atom `search:",facet"`
}{Atom("yes")},
wantErr: true,
},
{
desc: "Error on unsettable facet",
meta: &DocumentMetadata{Facets: []Facet{
{Name: "Set", Value: Atom("yes")},
{Name: "unset", Value: Atom("no")},
}},
want: &struct {
Set Atom `search:",facet"`
}{Atom("yes")},
wantErr: true,
},
{
desc: "Error setting ignored field",
fields: []Field{
{Name: "Set", Value: "yes"},
{Name: "Ignored", Value: "no"},
},
want: &struct {
Set string
Ignored string `search:"-"`
}{Set: "yes"},
wantErr: true,
},
{
desc: "Error setting ignored facet",
meta: &DocumentMetadata{Facets: []Facet{
{Name: "Set", Value: Atom("yes")},
{Name: "Ignored", Value: Atom("no")},
}},
want: &struct {
Set Atom `search:",facet"`
Ignored Atom `search:"-,facet"`
}{Set: Atom("yes")},
wantErr: true,
},
}
for _, tt := range testCases {
// Make a pointer to an empty version of what want points to.
dst := reflect.New(reflect.TypeOf(tt.want).Elem()).Interface()
err := loadStructWithMeta(dst, tt.fields, tt.meta)
if err != nil != tt.wantErr {
t.Errorf("%s: got err %v; want err %t", tt.desc, err, tt.wantErr)
continue
}
if !reflect.DeepEqual(dst, tt.want) {
t.Errorf("%s: doesn't match\ngot: %v\nwant: %v", tt.desc, dst, tt.want)
}
}
}
func TestSavingStruct(t *testing.T) {
testCases := []struct {
desc string
doc interface{}
wantFields []Field
wantFacets []Facet
}{
{
desc: "Basic struct",
doc: &struct {
Name string
Legs float64
}{"Gopher", 4},
wantFields: []Field{
{Name: "Name", Value: "Gopher"},
{Name: "Legs", Value: float64(4)},
},
},
{
desc: "Struct with tags",
doc: &struct {
Name string
Info string `search:"about"`
Legs float64 `search:",facet"`
Fuzz Atom `search:"Fur,facet"`
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
wantFields: []Field{
{Name: "Name", Value: "Gopher"},
{Name: "about", Value: "Likes slide rules."},
},
wantFacets: []Facet{
{Name: "Legs", Value: float64(4)},
{Name: "Fur", Value: Atom("furry")},
},
},
{
desc: "Ignore unexported struct fields",
doc: &struct {
Name string
info string
Legs float64 `search:",facet"`
fuzz Atom `search:",facet"`
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
wantFields: []Field{
{Name: "Name", Value: "Gopher"},
},
wantFacets: []Facet{
{Name: "Legs", Value: float64(4)},
},
},
{
desc: "Ignore fields marked -",
doc: &struct {
Name string
Info string `search:"-"`
Legs float64 `search:",facet"`
Fuzz Atom `search:"-,facet"`
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
wantFields: []Field{
{Name: "Name", Value: "Gopher"},
},
wantFacets: []Facet{
{Name: "Legs", Value: float64(4)},
},
},
}
for _, tt := range testCases {
fields, meta, err := saveStructWithMeta(tt.doc)
if err != nil {
t.Errorf("%s: got err %v; want nil", tt.desc, err)
continue
}
if !reflect.DeepEqual(fields, tt.wantFields) {
t.Errorf("%s: fields don't match\ngot: %v\nwant: %v", tt.desc, fields, tt.wantFields)
}
if facets := meta.Facets; !reflect.DeepEqual(facets, tt.wantFacets) {
t.Errorf("%s: facets don't match\ngot: %v\nwant: %v", tt.desc, facets, tt.wantFacets)
}
}
}