├── .gitignore ├── vendor └── github.com │ ├── zabawaba99 │ └── firego │ │ ├── sync │ │ ├── fixtures │ │ │ ├── scalars │ │ │ │ ├── decimal.json │ │ │ │ ├── number.json │ │ │ │ ├── boolean.json │ │ │ │ └── string.json │ │ │ ├── objects │ │ │ │ ├── simple.json │ │ │ │ ├── complex.json │ │ │ │ ├── with_arrays.json │ │ │ │ └── nested.json │ │ │ └── arrays │ │ │ │ ├── booleans.json │ │ │ │ ├── numbers.json │ │ │ │ ├── strings.json │ │ │ │ └── decimals.json │ │ ├── node_bench_test.go │ │ ├── database_test.go │ │ ├── database.go │ │ ├── node.go │ │ └── node_test.go │ │ ├── .gitignore │ │ ├── internal │ │ └── firetest │ │ │ ├── exmaple_test.go │ │ │ ├── README.md │ │ │ ├── notify_db_test.go │ │ │ ├── direct.go │ │ │ ├── direct_test.go │ │ │ ├── notify_db.go │ │ │ └── server.go │ │ ├── LICENSE │ │ ├── snapshot.go │ │ ├── snapshot_test.go │ │ ├── .travis.yml │ │ ├── transaction_test.go │ │ ├── transaction.go │ │ ├── example_test.go │ │ ├── README.md │ │ ├── firebase_test.go │ │ ├── watch.go │ │ ├── watch_test.go │ │ ├── query_test.go │ │ ├── query.go │ │ ├── event_callback.go │ │ └── firebase.go │ ├── gorilla │ ├── context │ │ ├── README.md │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── doc.go │ │ ├── context_test.go │ │ └── context.go │ └── mux │ │ ├── .travis.yml │ │ ├── context_native.go │ │ ├── context_gorilla.go │ │ ├── context_native_test.go │ │ ├── context_gorilla_test.go │ │ ├── LICENSE │ │ ├── bench_test.go │ │ └── doc.go │ └── urfave │ └── negroni │ ├── recovery_test.go │ ├── logger_test.go │ ├── doc.go │ ├── logger.go │ ├── CHANGELOG.md │ ├── recovery.go │ ├── LICENSE │ ├── static.go │ ├── negroni_test.go │ ├── static_test.go │ ├── response_writer.go │ ├── response_writer_test.go │ ├── negroni.go │ └── translations │ ├── README_zh_tw.md │ ├── README_zh_cn.md │ ├── README_pt_br.md │ └── README_de_de.md ├── helm ├── seanmeme │ ├── Chart.yaml │ ├── .helmignore │ ├── templates │ │ ├── _helpers.tpl │ │ ├── service.yaml │ │ ├── ingress.yaml │ │ ├── deployment.yaml │ │ └── NOTES.txt │ └── values.yaml └── helm-init.sh ├── Dockerfile ├── Makefile ├── Gopkg.toml ├── Gopkg.lock ├── main.go ├── Jenkinsfile └── static └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | gin-bin 2 | static/memes 3 | /seanmeme 4 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/scalars/decimal.json: -------------------------------------------------------------------------------- 1 | 2.2 -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/scalars/number.json: -------------------------------------------------------------------------------- 1 | 2 -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/scalars/boolean.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/scalars/string.json: -------------------------------------------------------------------------------- 1 | "foo" -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/objects/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar" 3 | } -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/arrays/booleans.json: -------------------------------------------------------------------------------- 1 | [ 2 | true, 3 | false 4 | ] -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/arrays/numbers.json: -------------------------------------------------------------------------------- 1 | [ 2 | 1, 3 | 2, 4 | 3 5 | ] -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/arrays/strings.json: -------------------------------------------------------------------------------- 1 | [ 2 | "foo", 3 | "bar" 4 | ] -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/arrays/decimals.json: -------------------------------------------------------------------------------- 1 | [ 2 | 1.1, 3 | 2.2, 4 | 3.3 5 | ] -------------------------------------------------------------------------------- /helm/seanmeme/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for Kubernetes 3 | name: seanmeme 4 | version: 0.1.0 5 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/objects/complex.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar", 3 | "foo1": 2, 4 | "foo2": true, 5 | "foo3": 3.42 6 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | RUN apk add -U ca-certificates 4 | 5 | COPY static/ /static/ 6 | COPY seanmeme / 7 | 8 | ENV PORT 8080 9 | 10 | CMD ["/seanmeme"] 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker run --rm -v "${PWD}":/go/src/seanmeme -w /go/src/seanmeme -e CGO_ENABLED=0 golang:1.8 go build 3 | 4 | docker-image: build 5 | docker build -t prydonius/seanmeme . 6 | -------------------------------------------------------------------------------- /helm/helm-init.sh: -------------------------------------------------------------------------------- 1 | HELM_URL=https://storage.googleapis.com/kubernetes-helm 2 | HELM_TARBALL=helm-v2.4.1-linux-amd64.tar.gz 3 | curl -O $HELM_URL/$HELM_TARBALL 4 | tar xzfv $HELM_TARBALL -C /home/jenkins && rm $HELM_TARBALL 5 | export PATH=/home/jenkins/linux-amd64/:$PATH 6 | helm init --client-only 7 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/objects/with_arrays.json: -------------------------------------------------------------------------------- 1 | { 2 | "regular": "item", 3 | "booleans": [ 4 | false, 5 | true 6 | ], 7 | "numbers": [ 8 | 1, 9 | 2 10 | ], 11 | "decimals": [ 12 | 1.1, 13 | 2.2 14 | ], 15 | "strings": [ 16 | "foo", 17 | "bar" 18 | ] 19 | } -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/README.md: -------------------------------------------------------------------------------- 1 | context 2 | ======= 3 | [![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) 4 | 5 | gorilla/context is a general purpose registry for global request variables. 6 | 7 | Read the full documentation here: http://www.gorillatoolkit.org/pkg/context 8 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - go: 1.3 7 | - go: 1.4 8 | - go: 1.5 9 | - go: 1.6 10 | - go: tip 11 | 12 | install: 13 | - go get golang.org/x/tools/cmd/vet 14 | 15 | script: 16 | - go get -t -v ./... 17 | - diff -u <(echo -n) <(gofmt -d .) 18 | - go tool vet . 19 | - go test -v -race ./... 20 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - go: 1.2 7 | - go: 1.3 8 | - go: 1.4 9 | - go: 1.5 10 | - go: 1.6 11 | - go: 1.7 12 | - go: 1.8 13 | - go: tip 14 | 15 | install: 16 | - # Skip 17 | 18 | script: 19 | - go get -t -v ./... 20 | - diff -u <(echo -n) <(gofmt -d .) 21 | - go tool vet . 22 | - go test -v -race ./... 23 | -------------------------------------------------------------------------------- /helm/seanmeme/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/context_native.go: -------------------------------------------------------------------------------- 1 | // +build go1.7 2 | 3 | package mux 4 | 5 | import ( 6 | "context" 7 | "net/http" 8 | ) 9 | 10 | func contextGet(r *http.Request, key interface{}) interface{} { 11 | return r.Context().Value(key) 12 | } 13 | 14 | func contextSet(r *http.Request, key, val interface{}) *http.Request { 15 | if val == nil { 16 | return r 17 | } 18 | 19 | return r.WithContext(context.WithValue(r.Context(), key, val)) 20 | } 21 | 22 | func contextClear(r *http.Request) { 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/context_gorilla.go: -------------------------------------------------------------------------------- 1 | // +build !go1.7 2 | 3 | package mux 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/gorilla/context" 9 | ) 10 | 11 | func contextGet(r *http.Request, key interface{}) interface{} { 12 | return context.Get(r, key) 13 | } 14 | 15 | func contextSet(r *http.Request, key, val interface{}) *http.Request { 16 | if val == nil { 17 | return r 18 | } 19 | 20 | context.Set(r, key, val) 21 | return r 22 | } 23 | 24 | func contextClear(r *http.Request) { 25 | context.Clear(r) 26 | } 27 | -------------------------------------------------------------------------------- /helm/seanmeme/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/fixtures/objects/nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "dinosaurs": { 3 | "bruhathkayosaurus": { 4 | "appeared": -70000000, 5 | "height": 25, 6 | "length": 44, 7 | "order": "saurischia", 8 | "vanished": -70000000, 9 | "weight": 135000 10 | }, 11 | "lambeosaurus": { 12 | "appeared": -76000000, 13 | "height": 2.1, 14 | "length": 12.5, 15 | "order": "ornithischia", 16 | "vanished": -75000000, 17 | "weight": 5000 18 | } 19 | }, 20 | "scores": { 21 | "bruhathkayosaurus": 55, 22 | "lambeosaurus": 21 23 | } 24 | } -------------------------------------------------------------------------------- /helm/seanmeme/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "fullname" . }} 5 | labels: 6 | app: {{ template "name" . }} 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | type: {{ .Values.service.type }} 12 | ports: 13 | - port: {{ .Values.service.externalPort }} 14 | targetPort: {{ .Values.service.internalPort }} 15 | protocol: TCP 16 | name: {{ .Values.service.name }} 17 | selector: 18 | app: {{ template "name" . }} 19 | release: {{ .Release.Name }} 20 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | 2 | # Gopkg.toml example 3 | # 4 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 5 | # for detailed Gopkg.toml documentation. 6 | # 7 | # required = ["github.com/user/thing/cmd/thing"] 8 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 9 | # 10 | # [[constraint]] 11 | # name = "github.com/user/project" 12 | # version = "1.0.0" 13 | # 14 | # [[constraint]] 15 | # name = "github.com/user/project2" 16 | # branch = "dev" 17 | # source = "github.com/myfork/project2" 18 | # 19 | # [[override]] 20 | # name = "github.com/x/y" 21 | # version = "2.4.0" 22 | 23 | 24 | [[constraint]] 25 | name = "github.com/urfave/negroni" 26 | version = "0.2.0" 27 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/internal/firetest/exmaple_test.go: -------------------------------------------------------------------------------- 1 | package firetest_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "net/http" 7 | "strings" 8 | 9 | "github.com/zabawaba99/firego/internal/firetest" 10 | ) 11 | 12 | func Example() { 13 | ft := firetest.New() 14 | defer ft.Close() 15 | 16 | ft.Start() 17 | 18 | resp, err := http.Post(ft.URL+"/foo.json", "application/json", strings.NewReader(`{"bar":true}`)) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer resp.Body.Close() 23 | 24 | b, err := ioutil.ReadAll(resp.Body) 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | 29 | log.Printf("Post Resp: %s\n", string(b)) 30 | 31 | v := ft.Get("foo/bar") 32 | log.Printf("Value: %v", v) 33 | } 34 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/recovery_test.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | ) 10 | 11 | func TestRecovery(t *testing.T) { 12 | buff := bytes.NewBufferString("") 13 | recorder := httptest.NewRecorder() 14 | 15 | rec := NewRecovery() 16 | rec.Logger = log.New(buff, "[negroni] ", 0) 17 | 18 | n := New() 19 | // replace log for testing 20 | n.Use(rec) 21 | n.UseHandler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { 22 | panic("here is a panic!") 23 | })) 24 | n.ServeHTTP(recorder, (*http.Request)(nil)) 25 | expect(t, recorder.Code, http.StatusInternalServerError) 26 | refute(t, recorder.Body.Len(), 0) 27 | refute(t, len(buff.String()), 0) 28 | } 29 | -------------------------------------------------------------------------------- /helm/seanmeme/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: {{ template "fullname" . }} 5 | annotations: 6 | kubernetes.io/ingress.class: nginx 7 | {{- if .Values.ingress.tls }} 8 | kubernetes.io/tls-acme: 'true' 9 | {{- end }} 10 | labels: 11 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 12 | spec: 13 | rules: 14 | - http: 15 | paths: 16 | - backend: 17 | serviceName: {{ template "fullname" . }} 18 | servicePort: {{ .Values.service.externalPort }} 19 | path: / 20 | host: {{ .Values.ingress.host | quote }} 21 | {{- if .Values.ingress.tls }} 22 | tls: 23 | - secretName: {{ .Values.ingress.host }}-tls 24 | hosts: 25 | - {{ .Values.ingress.host | quote }} 26 | {{- end -}} 27 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/logger_test.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | ) 10 | 11 | func Test_Logger(t *testing.T) { 12 | buff := bytes.NewBufferString("") 13 | recorder := httptest.NewRecorder() 14 | 15 | l := NewLogger() 16 | l.Logger = log.New(buff, "[negroni] ", 0) 17 | 18 | n := New() 19 | // replace log for testing 20 | n.Use(l) 21 | n.UseHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 22 | rw.WriteHeader(http.StatusNotFound) 23 | })) 24 | 25 | req, err := http.NewRequest("GET", "http://localhost:3000/foobar", nil) 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | 30 | n.ServeHTTP(recorder, req) 31 | expect(t, recorder.Code, http.StatusNotFound) 32 | refute(t, len(buff.String()), 0) 33 | } 34 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/doc.go: -------------------------------------------------------------------------------- 1 | // Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers. 2 | // 3 | // If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit. 4 | // 5 | // For a full guide visit http://github.com/codegangsta/negroni 6 | // 7 | // package main 8 | // 9 | // import ( 10 | // "github.com/codegangsta/negroni" 11 | // "net/http" 12 | // "fmt" 13 | // ) 14 | // 15 | // func main() { 16 | // mux := http.NewServeMux() 17 | // mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 18 | // fmt.Fprintf(w, "Welcome to the home page!") 19 | // }) 20 | // 21 | // n := negroni.Classic() 22 | // n.UseHandler(mux) 23 | // n.Run(":3000") 24 | // } 25 | package negroni 26 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/context_native_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.7 2 | 3 | package mux 4 | 5 | import ( 6 | "context" 7 | "net/http" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestNativeContextMiddleware(t *testing.T) { 13 | withTimeout := func(h http.Handler) http.Handler { 14 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 15 | ctx, cancel := context.WithTimeout(r.Context(), time.Minute) 16 | defer cancel() 17 | h.ServeHTTP(w, r.WithContext(ctx)) 18 | }) 19 | } 20 | 21 | r := NewRouter() 22 | r.Handle("/path/{foo}", withTimeout(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | vars := Vars(r) 24 | if vars["foo"] != "bar" { 25 | t.Fatal("Expected foo var to be set") 26 | } 27 | }))) 28 | 29 | rec := NewRecorder() 30 | req := newRequest("GET", "/path/bar") 31 | r.ServeHTTP(rec, req) 32 | } 33 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/logger.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "os" 7 | "time" 8 | ) 9 | 10 | // Logger is a middleware handler that logs the request as it goes in and the response as it goes out. 11 | type Logger struct { 12 | // Logger inherits from log.Logger used to log messages with the Logger middleware 13 | *log.Logger 14 | } 15 | 16 | // NewLogger returns a new Logger instance 17 | func NewLogger() *Logger { 18 | return &Logger{log.New(os.Stdout, "[negroni] ", 0)} 19 | } 20 | 21 | func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 22 | start := time.Now() 23 | l.Printf("Started %s %s", r.Method, r.URL.Path) 24 | 25 | next(rw, r) 26 | 27 | res := rw.(ResponseWriter) 28 | l.Printf("Completed %v %s in %v", res.Status(), http.StatusText(res.Status()), time.Since(start)) 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | **ATTN**: This project uses [semantic versioning](http://semver.org/). 4 | 5 | ## [Unreleased] 6 | 7 | ## [0.2.0] - 2016-05-10 8 | ### Added 9 | - Support for variadic handlers in `New()` 10 | - Added `Negroni.Handlers()` to fetch all of the handlers for a given chain 11 | - Allowed size in `Recovery` handler was bumped to 8k 12 | - `Negroni.UseFunc` to push another handler onto the chain 13 | 14 | ### Changed 15 | - Set the status before calling `beforeFuncs` so the information is available to them 16 | - Set default status to `200` in the case that no handler writes status -- was previously `0` 17 | - Panic if `nil` handler is given to `negroni.Use` 18 | 19 | ## 0.1.0 - 2013-07-22 20 | ### Added 21 | - Initial implementation. 22 | 23 | [Unreleased]: https://github.com/codegangsta/negroni/compare/v0.2.0...HEAD 24 | [0.2.0]: https://github.com/codegangsta/negroni/compare/v0.1.0...v0.2.0 25 | -------------------------------------------------------------------------------- /helm/seanmeme/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for seanmeme. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | replicaCount: 1 5 | image: 6 | repository: prydonius/seanmeme 7 | tag: latest 8 | pullPolicy: IfNotPresent 9 | service: 10 | name: http 11 | type: ClusterIP 12 | externalPort: 80 13 | internalPort: 8080 14 | ingress: 15 | host: seanmeme.local 16 | # Enable automatic TLS with Let's Encrypt 17 | tls: true 18 | resources: {} 19 | # We usually recommend not to specify default resources and to leave this as a conscious 20 | # choice for the user. This also increases chances charts run on environments with little 21 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 22 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 23 | # limits: 24 | # cpu: 100m 25 | # memory: 128Mi 26 | #requests: 27 | # cpu: 100m 28 | # memory: 128Mi 29 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/gorilla/context" 6 | packages = ["."] 7 | revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a" 8 | version = "v1.1" 9 | 10 | [[projects]] 11 | name = "github.com/gorilla/mux" 12 | packages = ["."] 13 | revision = "bcd8bc72b08df0f70df986b97f95590779502d31" 14 | version = "v1.4.0" 15 | 16 | [[projects]] 17 | name = "github.com/urfave/negroni" 18 | packages = ["."] 19 | revision = "fde5e16d32adc7ad637e9cd9ad21d4ebc6192535" 20 | version = "v0.2.0" 21 | 22 | [[projects]] 23 | branch = "v1" 24 | name = "github.com/zabawaba99/firego" 25 | packages = [".","sync"] 26 | revision = "4d07741c1dde0b0d76218e8365baea10c33cf968" 27 | 28 | [solve-meta] 29 | analyzer-name = "dep" 30 | analyzer-version = 1 31 | inputs-digest = "6898dfc6d22ee826a60f29484f714d2e61de34a26550d2d8f1b3c8899dbf0f3d" 32 | solver-name = "gps-cdcl" 33 | solver-version = 1 34 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/context_gorilla_test.go: -------------------------------------------------------------------------------- 1 | // +build !go1.7 2 | 3 | package mux 4 | 5 | import ( 6 | "net/http" 7 | "testing" 8 | 9 | "github.com/gorilla/context" 10 | ) 11 | 12 | // Tests that the context is cleared or not cleared properly depending on 13 | // the configuration of the router 14 | func TestKeepContext(t *testing.T) { 15 | func1 := func(w http.ResponseWriter, r *http.Request) {} 16 | 17 | r := NewRouter() 18 | r.HandleFunc("/", func1).Name("func1") 19 | 20 | req, _ := http.NewRequest("GET", "http://localhost/", nil) 21 | context.Set(req, "t", 1) 22 | 23 | res := new(http.ResponseWriter) 24 | r.ServeHTTP(*res, req) 25 | 26 | if _, ok := context.GetOk(req, "t"); ok { 27 | t.Error("Context should have been cleared at end of request") 28 | } 29 | 30 | r.KeepContext = true 31 | 32 | req, _ = http.NewRequest("GET", "http://localhost/", nil) 33 | context.Set(req, "t", 1) 34 | 35 | r.ServeHTTP(*res, req) 36 | if _, ok := context.GetOk(req, "t"); !ok { 37 | t.Error("Context should NOT have been cleared at end of request") 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/recovery.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | "runtime" 9 | ) 10 | 11 | // Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one. 12 | type Recovery struct { 13 | Logger *log.Logger 14 | PrintStack bool 15 | StackAll bool 16 | StackSize int 17 | } 18 | 19 | // NewRecovery returns a new instance of Recovery 20 | func NewRecovery() *Recovery { 21 | return &Recovery{ 22 | Logger: log.New(os.Stdout, "[negroni] ", 0), 23 | PrintStack: true, 24 | StackAll: false, 25 | StackSize: 1024 * 8, 26 | } 27 | } 28 | 29 | func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | rw.WriteHeader(http.StatusInternalServerError) 33 | stack := make([]byte, rec.StackSize) 34 | stack = stack[:runtime.Stack(stack, rec.StackAll)] 35 | 36 | f := "PANIC: %s\n%s" 37 | rec.Logger.Printf(f, err, stack) 38 | 39 | if rec.PrintStack { 40 | fmt.Fprintf(rw, f, err, stack) 41 | } 42 | } 43 | }() 44 | 45 | next(rw, r) 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Steven Berlanga, Google Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/internal/firetest/README.md: -------------------------------------------------------------------------------- 1 | # firetest 2 | 3 | Firebase test server meant for use in unit tests 4 | 5 | ### Implemented 6 | 7 | * [Basic API Usage](https://www.firebase.com/docs/rest/api/#section-api-usage) 8 | * POST 9 | * GET 10 | * PUT 11 | * PATCH 12 | * DELETE 13 | * [Query parameters](https://www.firebase.com/docs/rest/api/#section-query-parameters): 14 | * auth 15 | * [Streaming](https://www.firebase.com/docs/rest/api/#section-streaming) 16 | 17 | ### Not Supported 18 | 19 | * [Query parameters](https://www.firebase.com/docs/rest/api/#section-query-parameters): 20 | * shallow 21 | * print 22 | * format 23 | * download 24 | * [Priorities](https://www.firebase.com/docs/rest/api/#section-priorities) 25 | * [Server Values](https://www.firebase.com/docs/rest/api/#section-server-values) 26 | * [Security Rules](https://www.firebase.com/docs/rest/api/#section-security-rules) 27 | * [Error Conditions](https://www.firebase.com/docs/rest/api/#section-error-conditions) 28 | 29 | ## Contributing 30 | 31 | 1. Fork it 32 | 2. Create your feature branch (`git checkout -b new-feature`) 33 | 3. Commit your changes (`git commit -am 'awesome things with tests'`) 34 | 4. Push to the branch (`git push origin new-feature`) 35 | 5. Create new Pull Request 36 | -------------------------------------------------------------------------------- /helm/seanmeme/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "fullname" . }} 5 | labels: 6 | app: {{ template "name" . }} 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.replicaCount }} 12 | template: 13 | metadata: 14 | labels: 15 | app: {{ template "name" . }} 16 | release: {{ .Release.Name }} 17 | spec: 18 | containers: 19 | - name: {{ .Chart.Name }} 20 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 21 | imagePullPolicy: {{ .Values.image.pullPolicy }} 22 | ports: 23 | - containerPort: {{ .Values.service.internalPort }} 24 | livenessProbe: 25 | httpGet: 26 | path: / 27 | port: {{ .Values.service.internalPort }} 28 | readinessProbe: 29 | httpGet: 30 | path: / 31 | port: {{ .Values.service.internalPort }} 32 | resources: 33 | {{ toYaml .Values.resources | indent 12 }} 34 | {{- if .Values.nodeSelector }} 35 | nodeSelector: 36 | {{ toYaml .Values.nodeSelector | indent 8 }} 37 | {{- end }} 38 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/snapshot.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/zabawaba99/firego/sync" 7 | ) 8 | 9 | // DataSnapshot instances contains data from a Firebase reference. 10 | type DataSnapshot struct { 11 | // Key retrieves the key for the source location of this snapshot 12 | Key string 13 | 14 | // Value retrieves the data contained in this snapshot. 15 | Value interface{} 16 | } 17 | 18 | func newSnapshot(node *sync.Node) DataSnapshot { 19 | return DataSnapshot{ 20 | Key: node.Key, 21 | Value: node.Objectify(), 22 | } 23 | } 24 | 25 | // Child gets a DataSnapshot for the location at the specified relative path. 26 | // The relative path can either be a simple child key (e.g. 'fred') or a deeper 27 | // slash-separated path (e.g. 'fred/name/first'). 28 | func (d *DataSnapshot) Child(name string) (DataSnapshot, bool) { 29 | name = strings.Trim(name, "/") 30 | rabbitHole := strings.Split(name, "/") 31 | 32 | current := *d 33 | for _, tkn := range rabbitHole { 34 | children, ok := current.Value.(map[string]interface{}) 35 | if !ok { 36 | return current, false 37 | } 38 | 39 | v, ok := children[tkn] 40 | if !ok { 41 | return current, ok 42 | } 43 | 44 | current = DataSnapshot{ 45 | Key: tkn, 46 | Value: v, 47 | } 48 | } 49 | 50 | return current, true 51 | } 52 | -------------------------------------------------------------------------------- /helm/seanmeme/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.hostname }} 3 | http://{{- .Values.ingress.hostname }} 4 | {{- else if contains "NodePort" .Values.service.type }} 5 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }}) 6 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 7 | echo http://$NODE_IP:$NODE_PORT 8 | {{- else if contains "LoadBalancer" .Values.service.type }} 9 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 10 | You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}' 11 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 12 | echo http://$SERVICE_IP:{{ .Values.service.externalPort }} 13 | {{- else if contains "ClusterIP" .Values.service.type }} 14 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 15 | echo "Visit http://127.0.0.1:8080 to use your application" 16 | kubectl port-forward $POD_NAME 8080:{{ .Values.service.externalPort }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/snapshot_test.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | "github.com/zabawaba99/firego/sync" 9 | ) 10 | 11 | func TestDataSnapshotKey(t *testing.T) { 12 | n := sync.Node{Key: "foo"} 13 | d := newSnapshot(&n) 14 | 15 | assert.Equal(t, n.Key, d.Key) 16 | } 17 | 18 | func TestDataSnapshotValue(t *testing.T) { 19 | n := sync.Node{Value: "foo"} 20 | d := newSnapshot(&n) 21 | 22 | assert.Equal(t, n.Value, d.Value) 23 | } 24 | 25 | func TestDataSnapshotChild(t *testing.T) { 26 | n := sync.NewNode("", map[string]interface{}{ 27 | "one": map[string]interface{}{ 28 | "two": map[string]interface{}{ 29 | "three": true, 30 | }, 31 | }, 32 | }) 33 | d := newSnapshot(n) 34 | 35 | one, ok := d.Child("one") 36 | require.True(t, ok) 37 | assert.Equal(t, one.Key, "one") 38 | assert.Equal(t, one.Value, map[string]interface{}{ 39 | "two": map[string]interface{}{ 40 | "three": true, 41 | }, 42 | }) 43 | 44 | two, ok := one.Child("two") 45 | require.True(t, ok) 46 | assert.Equal(t, two.Key, "two") 47 | assert.Equal(t, two.Value, map[string]interface{}{ 48 | "three": true, 49 | }) 50 | 51 | three, ok := two.Child("three") 52 | require.True(t, ok) 53 | assert.Equal(t, three.Key, "three") 54 | assert.Equal(t, three.Value, true) 55 | 56 | three2, ok := d.Child("one/two/three") 57 | require.True(t, ok) 58 | assert.Equal(t, three, three2) 59 | } 60 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mux 6 | 7 | import ( 8 | "net/http" 9 | "net/http/httptest" 10 | "testing" 11 | ) 12 | 13 | func BenchmarkMux(b *testing.B) { 14 | router := new(Router) 15 | handler := func(w http.ResponseWriter, r *http.Request) {} 16 | router.HandleFunc("/v1/{v1}", handler) 17 | 18 | request, _ := http.NewRequest("GET", "/v1/anything", nil) 19 | for i := 0; i < b.N; i++ { 20 | router.ServeHTTP(nil, request) 21 | } 22 | } 23 | 24 | func BenchmarkMuxAlternativeInRegexp(b *testing.B) { 25 | router := new(Router) 26 | handler := func(w http.ResponseWriter, r *http.Request) {} 27 | router.HandleFunc("/v1/{v1:(?:a|b)}", handler) 28 | 29 | requestA, _ := http.NewRequest("GET", "/v1/a", nil) 30 | requestB, _ := http.NewRequest("GET", "/v1/b", nil) 31 | for i := 0; i < b.N; i++ { 32 | router.ServeHTTP(nil, requestA) 33 | router.ServeHTTP(nil, requestB) 34 | } 35 | } 36 | 37 | func BenchmarkManyPathVariables(b *testing.B) { 38 | router := new(Router) 39 | handler := func(w http.ResponseWriter, r *http.Request) {} 40 | router.HandleFunc("/v1/{v1}/{v2}/{v3}/{v4}/{v5}", handler) 41 | 42 | matchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4/5", nil) 43 | notMatchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4", nil) 44 | recorder := httptest.NewRecorder() 45 | for i := 0; i < b.N; i++ { 46 | router.ServeHTTP(nil, matchingRequest) 47 | router.ServeHTTP(recorder, notMatchingRequest) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "os" 10 | 11 | "github.com/gorilla/mux" 12 | "github.com/urfave/negroni" 13 | "github.com/zabawaba99/firego" 14 | ) 15 | 16 | func main() { 17 | f := firego.New("https://seanmeme-fdcf7.firebaseio.com/memes", nil) 18 | r := mux.NewRouter() 19 | 20 | r.Methods("POST").Path("/gen/{type}/{text}").HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 21 | vars := mux.Vars(req) 22 | memeType := vars["type"] 23 | text := vars["text"] 24 | 25 | var url string 26 | switch memeType { 27 | case "winter": 28 | // Shamefully using the awesome https://github.com/jacebrowning/memegen 29 | url = "https://memegen.link/winter/brace_yourselves/" + text + ".jpg" 30 | case "mordor": 31 | url = "https://memegen.link/mordor/one_does_not_simply/" + text + ".jpg" 32 | default: 33 | fmt.Fprint(w, "not found") 34 | return 35 | } 36 | 37 | response, err := http.Get(url + "?width=250") 38 | if err != nil { 39 | log.Print(err) 40 | return 41 | } 42 | defer response.Body.Close() 43 | 44 | data, err := ioutil.ReadAll(response.Body) 45 | if err != nil { 46 | log.Print(err) 47 | return 48 | } 49 | 50 | img := base64.StdEncoding.EncodeToString(data) 51 | _, err = f.Push(img) 52 | if err != nil { 53 | log.Print(err) 54 | return 55 | } 56 | }) 57 | 58 | r.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/"))) 59 | 60 | n := negroni.Classic() // Includes some default middlewares 61 | n.UseHandler(r) 62 | 63 | port := os.Getenv("PORT") 64 | addr := ":" + port 65 | http.ListenAndServe(addr, n) 66 | } 67 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8 5 | - 1.9 6 | - tip 7 | 8 | matrix: 9 | allow_failures: 10 | - go: tip 11 | fast_finish: true 12 | 13 | before_install: 14 | # linting tools 15 | - go get github.com/golang/lint/golint 16 | - go get github.com/fzipp/gocyclo 17 | 18 | # code coverage 19 | - go get github.com/axw/gocov/gocov 20 | - go get github.com/mattn/goveralls 21 | - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi 22 | 23 | install: 24 | # make sure stuff actually builds 25 | - go build 26 | 27 | script: 28 | # ensure everything is formatted all pretty like 29 | - if gofmt -l -s . | grep '**.go'; then exit 1; fi 30 | # vet out possible issues 31 | - go vet ./... 32 | # run tests 33 | - go get -t 34 | - go test -a -race -v ./... 35 | 36 | after_success: 37 | - | 38 | echo "mode: count" > profile.cov 39 | for dir in $(find . -maxdepth 10 -not -path 'vendor' -not -path './.git*' -not -path '*/_*' -type d); 40 | do 41 | if ls $dir/*.go &> /dev/null; then 42 | go test -covermode=count -coverprofile=$dir/profile.tmp $dir 43 | if [ -f $dir/profile.tmp ] 44 | then 45 | cat $dir/profile.tmp | tail -n +2 >> profile.cov 46 | rm $dir/profile.tmp 47 | fi 48 | fi 49 | done 50 | go tool cover -func profile.cov 51 | goveralls -coverprofile=profile.cov -service=travis-ci -repotoken=$COVERALLS -v 52 | 53 | after_script: 54 | # check possible styling errors 55 | - golint ./... 56 | # check for potentially complex functions but don't false build 57 | - gocyclo -over 15 . || true 58 | # refresh godocs in case there were api changes 59 | - | 60 | if [[ "$TRAVIS_PULL_REQUEST" == "false" ]] && [[ "$TRAVIS_BRANCH" == "master" ]]; then 61 | go list ./... | xargs -n 1 -I{} curl http://godoc.org/-/refresh -d path={} 62 | fi 63 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/static.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "net/http" 5 | "path" 6 | "strings" 7 | ) 8 | 9 | // Static is a middleware handler that serves static files in the given directory/filesystem. 10 | type Static struct { 11 | // Dir is the directory to serve static files from 12 | Dir http.FileSystem 13 | // Prefix is the optional prefix used to serve the static directory content 14 | Prefix string 15 | // IndexFile defines which file to serve as index if it exists. 16 | IndexFile string 17 | } 18 | 19 | // NewStatic returns a new instance of Static 20 | func NewStatic(directory http.FileSystem) *Static { 21 | return &Static{ 22 | Dir: directory, 23 | Prefix: "", 24 | IndexFile: "index.html", 25 | } 26 | } 27 | 28 | func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 29 | if r.Method != "GET" && r.Method != "HEAD" { 30 | next(rw, r) 31 | return 32 | } 33 | file := r.URL.Path 34 | // if we have a prefix, filter requests by stripping the prefix 35 | if s.Prefix != "" { 36 | if !strings.HasPrefix(file, s.Prefix) { 37 | next(rw, r) 38 | return 39 | } 40 | file = file[len(s.Prefix):] 41 | if file != "" && file[0] != '/' { 42 | next(rw, r) 43 | return 44 | } 45 | } 46 | f, err := s.Dir.Open(file) 47 | if err != nil { 48 | // discard the error? 49 | next(rw, r) 50 | return 51 | } 52 | defer f.Close() 53 | 54 | fi, err := f.Stat() 55 | if err != nil { 56 | next(rw, r) 57 | return 58 | } 59 | 60 | // try to serve index file 61 | if fi.IsDir() { 62 | // redirect if missing trailing slash 63 | if !strings.HasSuffix(r.URL.Path, "/") { 64 | http.Redirect(rw, r, r.URL.Path+"/", http.StatusFound) 65 | return 66 | } 67 | 68 | file = path.Join(file, s.IndexFile) 69 | f, err = s.Dir.Open(file) 70 | if err != nil { 71 | next(rw, r) 72 | return 73 | } 74 | defer f.Close() 75 | 76 | fi, err = f.Stat() 77 | if err != nil || fi.IsDir() { 78 | next(rw, r) 79 | return 80 | } 81 | } 82 | 83 | http.ServeContent(rw, r, file, fi.ModTime(), f) 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/transaction_test.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "encoding/base64" 5 | "net/http" 6 | "net/http/httptest" 7 | "strconv" 8 | "sync/atomic" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | type aBool struct { 16 | addr *int32 17 | } 18 | 19 | func (a *aBool) set(v bool) { 20 | val := int32(0) 21 | if v { 22 | val = 1 23 | } 24 | atomic.StoreInt32(a.addr, val) 25 | } 26 | 27 | func (a *aBool) val() bool { 28 | v := atomic.LoadInt32(a.addr) 29 | if v == 1 { 30 | return true 31 | } 32 | return false 33 | } 34 | 35 | func newABool() *aBool { 36 | return &aBool{ 37 | addr: new(int32), 38 | } 39 | } 40 | 41 | func TestTransaction(t *testing.T) { 42 | storedVal := newABool() 43 | hitConflict := newABool() 44 | 45 | fbCounter := new(int64) 46 | atomic.StoreInt64(fbCounter, 1) 47 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 48 | counter := atomic.LoadInt64(fbCounter) 49 | val := strconv.FormatInt(counter, 10) 50 | valBytes := []byte(val) 51 | etag := base64.StdEncoding.EncodeToString(valBytes) 52 | 53 | if req.Method == http.MethodGet { 54 | w.Header().Set("Etag", etag) 55 | w.Write(valBytes) 56 | return 57 | } 58 | 59 | if req.Header.Get("if-match") != etag { 60 | hitConflict.set(true) 61 | w.Header().Set("Etag", etag) 62 | w.WriteHeader(http.StatusConflict) 63 | w.Write(valBytes) 64 | return 65 | } 66 | 67 | storedVal.set(true) 68 | w.WriteHeader(http.StatusOK) 69 | })) 70 | defer server.Close() 71 | 72 | fb := New(server.URL, nil) 73 | err := fb.Transaction(func(currentSnapshot interface{}) (interface{}, error) { 74 | counter, ok := currentSnapshot.(float64) 75 | require.True(t, ok, "counter is not of type float64") 76 | if !hitConflict.val() { 77 | // set some random value so that we can test out the conflict logic 78 | atomic.StoreInt64(fbCounter, 123) 79 | } 80 | return counter + 1, nil 81 | }) 82 | assert.NoError(t, err) 83 | assert.True(t, storedVal.val()) 84 | assert.True(t, hitConflict.val()) 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/internal/firetest/notify_db_test.go: -------------------------------------------------------------------------------- 1 | package firetest 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/zabawaba99/firego/sync" 10 | ) 11 | 12 | func TestNotifyDBAdd(t *testing.T) { 13 | for _, test := range []struct { 14 | path string 15 | node *sync.Node 16 | }{ 17 | { 18 | path: "scalars/string", 19 | node: sync.NewNode("", "foo"), 20 | }, 21 | { 22 | path: "s/c/a/l/a/r/s/s/t/r/i/n/g", 23 | node: sync.NewNode("", []interface{}{"foo", "bar"}), 24 | }, 25 | } { 26 | db := newNotifyDB() 27 | 28 | // listen for notifications 29 | notifications := db.watch("") 30 | exited := make(chan struct{}) 31 | go func() { 32 | n, ok := <-notifications 33 | assert.True(t, ok) 34 | assert.Equal(t, "put", n.Name) 35 | assert.Equal(t, test.path, n.Data.Path, "wat?") 36 | assert.Equal(t, test.node, n.Data.Data) 37 | close(exited) 38 | }() 39 | 40 | db.add(test.path, test.node) 41 | 42 | select { 43 | case <-exited: 44 | case <-time.After(250 * time.Millisecond): 45 | } 46 | db.stopWatching(test.path, notifications) 47 | } 48 | } 49 | 50 | func TestNotifyDBDel(t *testing.T) { 51 | existingNodes := []string{ 52 | "root/only/two", 53 | "root/only/three", 54 | "root/only/one/child/here", 55 | } 56 | db := newNotifyDB() 57 | for _, p := range existingNodes { 58 | db.add(p, sync.NewNode("", 1)) 59 | } 60 | 61 | // listen for notifications 62 | notifications := db.watch("") 63 | exited := make(chan struct{}) 64 | go func() { 65 | regex := regexp.MustCompile("(root/only/one/child|root)") 66 | n, ok := <-notifications 67 | assert.True(t, ok) 68 | assert.Equal(t, "put", n.Name) 69 | assert.Regexp(t, regex, n.Data.Path) 70 | 71 | n, ok = <-notifications 72 | assert.True(t, ok) 73 | assert.Equal(t, "put", n.Name) 74 | assert.Regexp(t, regex, n.Data.Path) 75 | close(exited) 76 | }() 77 | 78 | db.del("root/only/one/child") 79 | db.del("root") 80 | 81 | select { 82 | case <-exited: 83 | case <-time.After(250 * time.Millisecond): 84 | } 85 | db.stopWatching("", notifications) 86 | } 87 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | environment { 3 | IMAGE_NAME = 'prydonius/seanmeme' 4 | } 5 | 6 | agent any 7 | 8 | stages { 9 | stage('Build') { 10 | steps { 11 | checkout scm 12 | sh ''' 13 | docker run --rm -v "${PWD}":/go/src/seanmeme -w /go/src/seanmeme -e CGO_ENABLED=0 golang:1.8 go build 14 | docker build -t $IMAGE_NAME:$BUILD_ID . 15 | ''' 16 | } 17 | } 18 | stage('Test') { 19 | steps { 20 | echo 'TODO: add tests' 21 | } 22 | } 23 | stage('Image Release') { 24 | when { 25 | expression { env.BRANCH_NAME == 'master' } 26 | } 27 | 28 | steps { 29 | withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'dockerhub', 30 | usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD']]) { 31 | sh ''' 32 | docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD 33 | docker push $IMAGE_NAME:$BUILD_ID 34 | ''' 35 | } 36 | } 37 | } 38 | stage('Staging Deployment') { 39 | when { 40 | expression { env.BRANCH_NAME == 'master' } 41 | } 42 | 43 | environment { 44 | RELEASE_NAME = 'seanmeme-staging' 45 | SERVER_HOST = 'staging.seanmeme.k8s.prydoni.us' 46 | } 47 | 48 | steps { 49 | sh ''' 50 | . ./helm/helm-init.sh 51 | helm upgrade --install --namespace staging $RELEASE_NAME ./helm/seanmeme --set image.tag=$BUILD_ID,ingress.host=$SERVER_HOST 52 | ''' 53 | } 54 | } 55 | stage('Deploy to Production?') { 56 | when { 57 | expression { env.BRANCH_NAME == 'master' } 58 | } 59 | 60 | steps { 61 | // Prevent any older builds from deploying to production 62 | milestone(1) 63 | input 'Deploy to Production?' 64 | milestone(2) 65 | } 66 | } 67 | stage('Production Deployment') { 68 | when { 69 | expression { env.BRANCH_NAME == 'master' } 70 | } 71 | 72 | environment { 73 | RELEASE_NAME = 'seanmeme-production' 74 | SERVER_HOST = 'seanmeme.k8s.prydoni.us' 75 | } 76 | 77 | steps { 78 | sh ''' 79 | . ./helm/helm-init.sh 80 | helm upgrade --install --namespace production $RELEASE_NAME ./helm/seanmeme --set image.tag=$BUILD_ID,ingress.host=$SERVER_HOST 81 | ''' 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sean Meme 8 | 9 | 15 | 16 | 17 |
18 |

Sean Meme's photos - bit.ly/seanmeme

19 |
20 | 24 | 25 | 26 | 27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | 65 | 66 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/negroni_test.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | /* Test Helpers */ 11 | func expect(t *testing.T, a interface{}, b interface{}) { 12 | if a != b { 13 | t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 14 | } 15 | } 16 | 17 | func refute(t *testing.T, a interface{}, b interface{}) { 18 | if a == b { 19 | t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 20 | } 21 | } 22 | 23 | func TestNegroniRun(t *testing.T) { 24 | // just test that Run doesn't bomb 25 | go New().Run(":3000") 26 | } 27 | 28 | func TestNegroniServeHTTP(t *testing.T) { 29 | result := "" 30 | response := httptest.NewRecorder() 31 | 32 | n := New() 33 | n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 34 | result += "foo" 35 | next(rw, r) 36 | result += "ban" 37 | })) 38 | n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 39 | result += "bar" 40 | next(rw, r) 41 | result += "baz" 42 | })) 43 | n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 44 | result += "bat" 45 | rw.WriteHeader(http.StatusBadRequest) 46 | })) 47 | 48 | n.ServeHTTP(response, (*http.Request)(nil)) 49 | 50 | expect(t, result, "foobarbatbazban") 51 | expect(t, response.Code, http.StatusBadRequest) 52 | } 53 | 54 | // Ensures that a Negroni middleware chain 55 | // can correctly return all of its handlers. 56 | func TestHandlers(t *testing.T) { 57 | response := httptest.NewRecorder() 58 | n := New() 59 | handlers := n.Handlers() 60 | expect(t, 0, len(handlers)) 61 | 62 | n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 63 | rw.WriteHeader(http.StatusOK) 64 | })) 65 | 66 | // Expects the length of handlers to be exactly 1 67 | // after adding exactly one handler to the middleware chain 68 | handlers = n.Handlers() 69 | expect(t, 1, len(handlers)) 70 | 71 | // Ensures that the first handler that is in sequence behaves 72 | // exactly the same as the one that was registered earlier 73 | handlers[0].ServeHTTP(response, (*http.Request)(nil), nil) 74 | expect(t, response.Code, http.StatusOK) 75 | } 76 | 77 | func TestNegroni_Use_Nil(t *testing.T) { 78 | defer func() { 79 | err := recover() 80 | if err == nil { 81 | t.Errorf("Expected negroni.Use(nil) to panic, but it did not") 82 | } 83 | }() 84 | 85 | n := New() 86 | n.Use(nil) 87 | } 88 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/node_bench_test.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func BenchmarkNewNode(b *testing.B) { 8 | nodesToCreate := []interface{}{ 9 | // primitives 10 | "somestirng", 11 | 1, 12 | int8(2), 13 | int16(12), 14 | int32(222), 15 | int64(3245), 16 | float32(123123123123234), 17 | float64(7654323456543245654), 18 | true, 19 | 20 | // slice 21 | []interface{}{ 22 | "foobar", 23 | 1234, 24 | true, 25 | }, 26 | 27 | // simple map 28 | map[string]interface{}{ 29 | "hello": "world", 30 | "foo": false, 31 | "number": 222, 32 | }, 33 | 34 | // something that is not interface type 35 | map[string]bool{ 36 | "1": true, 37 | "2": true, 38 | "3": true, 39 | }, 40 | 41 | // nest map 42 | map[string]interface{}{ 43 | "aaa": []interface{}{ 44 | "ahhhh", 45 | map[string]interface{}{ 46 | "wat?": "another map?!", 47 | "yup": true, 48 | }, 49 | }, 50 | "doras-backpack": map[string]interface{}{ 51 | "si se puede": "yes you can!", 52 | "map map map map": 333, 53 | }, 54 | "something-simple": 1, 55 | "inception": map[string]interface{}{ 56 | "aaa": []interface{}{ 57 | "ahhhh", 58 | map[string]interface{}{ 59 | "wat?": "another map?!", 60 | "yup": true, 61 | }, 62 | }, 63 | "doras-backpack": map[string]interface{}{ 64 | "si se puede": "yes you can!", 65 | "map map map map": 333, 66 | }, 67 | "something-simple": 1, 68 | "inception": map[string]interface{}{ 69 | "aaa": []interface{}{ 70 | "ahhhh", 71 | map[string]interface{}{ 72 | "wat?": "another map?!", 73 | "yup": true, 74 | }, 75 | }, 76 | "doras-backpack": map[string]interface{}{ 77 | "si se puede": "yes you can!", 78 | "map map map map": 333, 79 | }, 80 | "something-simple": 1, 81 | "inception": map[string]interface{}{ 82 | "aaa": []interface{}{ 83 | "ahhhh", 84 | map[string]interface{}{ 85 | "wat?": "another map?!", 86 | "yup": true, 87 | }, 88 | }, 89 | "doras-backpack": map[string]interface{}{ 90 | "si se puede": "yes you can!", 91 | "map map map map": 333, 92 | }, 93 | "something-simple": 1, 94 | }, 95 | }, 96 | }, 97 | }, 98 | } 99 | 100 | b.ResetTimer() 101 | for i := 0; i < b.N; i++ { 102 | for _, node := range nodesToCreate { 103 | _ = NewNode("", node) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/database_test.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestAdd(t *testing.T) { 12 | for _, test := range []struct { 13 | path string 14 | node *Node 15 | }{ 16 | { 17 | path: "scalars/string", 18 | node: NewNode("", "foo"), 19 | }, 20 | { 21 | path: "s/c/a/l/a/r/s/s/t/r/i/n/g", 22 | node: NewNode("", []interface{}{"foo", "bar"}), 23 | }, 24 | } { 25 | db := NewDB() 26 | 27 | db.Add(test.path, test.node) 28 | 29 | rabbitHole := strings.Split(test.path, "/") 30 | previous := db.root 31 | for i := 0; i < len(rabbitHole); i++ { 32 | var ok bool 33 | previous, ok = previous.Children[rabbitHole[i]] 34 | assert.True(t, ok, test.path) 35 | } 36 | 37 | assert.NoError(t, equalNodes(test.node, previous), test.path) 38 | } 39 | } 40 | 41 | func TestUpdate(t *testing.T) { 42 | db := NewDB() 43 | 44 | db.Add("", NewNode("", map[string]interface{}{ 45 | "hello": map[string]interface{}{ 46 | "world": 2, 47 | "world2": 1, 48 | }, 49 | })) 50 | 51 | db.Update("hello/world", NewNode("", "hi")) 52 | db.Update("hello/world3", NewNode("", "lol")) 53 | 54 | assert.Equal(t, "hi", db.Get("hello/world").Value) 55 | assert.Equal(t, 1, db.Get("hello/world2").Value) 56 | assert.Equal(t, "lol", db.Get("hello/world3").Value) 57 | } 58 | 59 | func TestGet(t *testing.T) { 60 | for _, test := range []struct { 61 | path string 62 | node *Node 63 | }{ 64 | { 65 | path: "scalars/string", 66 | node: NewNode("", "foo"), 67 | }, 68 | { 69 | path: "s/c/a/l/a/r/s/s/t/r/i/n/g", 70 | node: NewNode("", []interface{}{"foo", "bar"}), 71 | }, 72 | } { 73 | db := NewDB() 74 | db.Add(test.path, test.node) 75 | 76 | assert.NoError(t, equalNodes(test.node, db.Get(test.path)), test.path) 77 | } 78 | } 79 | 80 | func TestDel(t *testing.T) { 81 | existingNodes := []string{ 82 | "root/only/two", 83 | "root/only/three", 84 | "root/only/one/child/here", 85 | } 86 | db := NewDB() 87 | for _, p := range existingNodes { 88 | db.Add(p, NewNode("", 1)) 89 | } 90 | 91 | db.Del("root/only/one/child") 92 | assert.Nil(t, db.Get("root/only/one/child/here")) 93 | assert.Nil(t, db.Get("root/only/one/child")) 94 | assert.Nil(t, db.Get("root/only/one")) 95 | 96 | n := db.Get("root/only") 97 | require.NotNil(t, n) 98 | 99 | assert.Len(t, n.Children, 2) 100 | _, ok := n.Children["one"] 101 | assert.False(t, ok) 102 | 103 | db.Del("root") 104 | n = db.Get("") 105 | require.NotNil(t, n) 106 | assert.Len(t, n.Children, 0) 107 | } 108 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/transaction.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | // TransactionFn is used to run a transaction on a Firebase reference. 11 | // See Firebase.Transaction for more information. 12 | type TransactionFn func(currentSnapshot interface{}) (result interface{}, err error) 13 | 14 | func getTransactionParams(headers http.Header, body []byte) (etag string, snapshot interface{}, err error) { 15 | etag = headers.Get("ETag") 16 | if len(etag) == 0 { 17 | return etag, snapshot, errors.New("no etag returned by Firebase") 18 | } 19 | 20 | if err := json.Unmarshal(body, &snapshot); err != nil { 21 | return etag, snapshot, fmt.Errorf("failed to unmarshal Firebase response. %s", err) 22 | } 23 | 24 | return etag, snapshot, nil 25 | } 26 | 27 | // Transaction runs a transaction on the data at this location. The TransactionFn parameter 28 | // will be called, possibly multiple times, with the current data at this location. 29 | // It is responsible for inspecting that data and specifying either the desired new data 30 | // at the location or that the transaction should be aborted. 31 | // 32 | // Since the provided function may be called repeatedly for the same transaction, be extremely careful of 33 | // any side effects that may be triggered by this method. 34 | // 35 | // Best practices for this method are to rely only on the data that is passed in. 36 | func (fb *Firebase) Transaction(fn TransactionFn) error { 37 | // fetch etag and current value 38 | headers, body, err := fb.doRequest("GET", nil, withHeader("X-Firebase-ETag", "true")) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | etag, snapshot, err := getTransactionParams(headers, body) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | // set the error value to something non-nil so that 49 | // we step into the loop 50 | tErr := errors.New("") 51 | for i := 0; i < 25 && tErr != nil; i++ { 52 | // run transaction 53 | result, err := fn(snapshot) 54 | if err != nil { 55 | return nil 56 | } 57 | 58 | newBody, err := json.Marshal(result) 59 | if err != nil { 60 | return fmt.Errorf("failed to marshal transaction result. %s", err) 61 | } 62 | 63 | // attempt to update it 64 | headers, body, tErr = fb.doRequest("PUT", newBody, withHeader("if-match", etag)) 65 | if tErr == nil { 66 | // we're good, break the loop 67 | break 68 | } 69 | 70 | // we failed to update, so grab the new snapshot/etag 71 | e, s, tErr := getTransactionParams(headers, body) 72 | if tErr != nil { 73 | return tErr 74 | } 75 | etag, snapshot = e, s 76 | } 77 | 78 | if tErr != nil { 79 | return fmt.Errorf("failed to run transaction. %s", tErr) 80 | } 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/internal/firetest/direct.go: -------------------------------------------------------------------------------- 1 | package firetest 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/zabawaba99/firego/sync" 10 | ) 11 | 12 | // RequireAuth determines whether or not a Firetest server 13 | // will require that each request be authorized 14 | func (ft *Firetest) RequireAuth(v bool) { 15 | var val int32 16 | if v { 17 | val = 1 18 | } 19 | atomic.StoreInt32(ft.requireAuth, val) 20 | } 21 | 22 | // Create generates a new child under the given location 23 | // using a unique name and returns the name 24 | // 25 | // Reference https://www.firebase.com/docs/rest/api/#section-post 26 | func (ft *Firetest) Create(path string, v interface{}) string { 27 | src := []byte(fmt.Sprint(time.Now().UnixNano())) 28 | name := "~" + base64.StdEncoding.EncodeToString(src) 29 | 30 | path = fmt.Sprintf("%s/%s", sanitizePath(path), name) 31 | // sanitize one more time in case initial path was empty 32 | path = sanitizePath(path) 33 | ft.db.add(path, sync.NewNode("", v)) 34 | return name 35 | } 36 | 37 | // Delete removes the data at the requested location. 38 | // Any data at child locations will also be deleted. 39 | // 40 | // Reference https://www.firebase.com/docs/rest/api/#section-delete 41 | func (ft *Firetest) Delete(path string) { 42 | ft.db.del(sanitizePath(path)) 43 | } 44 | 45 | // Update writes the enumerated children to this the given location. 46 | // This will overwrite only children enumerated in the "value" parameter 47 | // and will leave others untouched. Note that the update function is equivalent 48 | // to calling Set() on the named children; it does not recursively update children 49 | // if they are objects. Passing null as a value for a child is equivalent to 50 | // calling remove() on that child. 51 | // 52 | // Reference https://www.firebase.com/docs/rest/api/#section-patch 53 | func (ft *Firetest) Update(path string, v interface{}) { 54 | path = sanitizePath(path) 55 | if v == nil { 56 | ft.db.del(path) 57 | } else { 58 | ft.db.update(path, sync.NewNode("", v)) 59 | } 60 | } 61 | 62 | // Set writes data to at the given location. 63 | // This will overwrite any data at this location and all child locations. 64 | // 65 | // Reference https://www.firebase.com/docs/rest/api/#section-put 66 | func (ft *Firetest) Set(path string, v interface{}) { 67 | ft.db.add(sanitizePath(path), sync.NewNode("", v)) 68 | } 69 | 70 | // Get retrieves the data and all its children at the 71 | // requested location 72 | // 73 | // Reference https://www.firebase.com/docs/rest/api/#section-get 74 | func (ft *Firetest) Get(path string) (v interface{}) { 75 | n := ft.db.intDB.Get(sanitizePath(path)) 76 | if n != nil { 77 | v = n.Objectify() 78 | } 79 | return v 80 | } 81 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/internal/firetest/direct_test.go: -------------------------------------------------------------------------------- 1 | package firetest 2 | 3 | import ( 4 | "strings" 5 | "sync/atomic" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/zabawaba99/firego/sync" 10 | ) 11 | 12 | func TestRequireAuth(t *testing.T) { 13 | for _, require := range []bool{true, false} { 14 | ft := New() 15 | ft.RequireAuth(require) 16 | var expected int32 17 | if require { 18 | expected = 1 19 | } 20 | assert.Equal(t, expected, atomic.LoadInt32(ft.requireAuth)) 21 | } 22 | } 23 | 24 | func TestCreate(t *testing.T) { 25 | var ( 26 | ft = New() 27 | v = true 28 | ) 29 | 30 | for _, p := range []string{"path/hi", ""} { 31 | name := ft.Create(p, v) 32 | assert.True(t, strings.HasPrefix(name, "~"), "name is missing `~` prefix") 33 | 34 | n := ft.db.get(sanitizePath(p + "/" + name)) 35 | assert.Equal(t, v, n.Value) 36 | } 37 | } 38 | 39 | func TestDelete(t *testing.T) { 40 | var ( 41 | ft = New() 42 | path = "foo/bar" 43 | v = true 44 | ) 45 | 46 | // delete path directly 47 | ft.db.add(path, sync.NewNode("", v)) 48 | ft.Delete(path) 49 | assert.Nil(t, ft.db.get(path)) 50 | 51 | // delete parent 52 | ft.db.add(path, sync.NewNode("", v)) 53 | ft.Delete("foo") 54 | assert.Nil(t, ft.db.get(path)) 55 | } 56 | 57 | func TestUpdate(t *testing.T) { 58 | var ( 59 | ft = New() 60 | path = "foo/bar" 61 | v = map[string]interface{}{ 62 | "1": "one", 63 | "2": "two", 64 | "3": "three", 65 | } 66 | ) 67 | ft.db.add(path, sync.NewNode("", v)) 68 | 69 | ft.Update(path, map[string]interface{}{ 70 | "1": "three", 71 | "3": "one", 72 | }) 73 | 74 | one := ft.db.get(path + "/1") 75 | three := ft.db.get(path + "/3") 76 | assert.Equal(t, "three", one.Value) 77 | assert.Equal(t, "one", three.Value) 78 | } 79 | 80 | func TestUpdateNil(t *testing.T) { 81 | var ( 82 | ft = New() 83 | path = "foo/bar" 84 | v = map[string]string{ 85 | "1": "one", 86 | "2": "two", 87 | "3": "three", 88 | } 89 | ) 90 | ft.db.add(path, sync.NewNode("", v)) 91 | 92 | ft.Update(path, nil) 93 | assert.Nil(t, ft.db.get(path)) 94 | assert.Nil(t, ft.db.get(path+"/1")) 95 | assert.Nil(t, ft.db.get(path+"/2")) 96 | assert.Nil(t, ft.db.get(path+"/3")) 97 | } 98 | 99 | func TestSet(t *testing.T) { 100 | var ( 101 | ft = New() 102 | path = "foo/bar" 103 | v = true 104 | ) 105 | ft.Set(path, v) 106 | 107 | n := ft.db.get(path) 108 | assert.Equal(t, v, n.Value) 109 | } 110 | 111 | func TestGet(t *testing.T) { 112 | var ( 113 | ft = New() 114 | path = "foo/bar" 115 | v = true 116 | ) 117 | ft.db.add(path, sync.NewNode("", v)) 118 | 119 | val := ft.Get(path) 120 | assert.Equal(t, v, val) 121 | } 122 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package context stores values shared during a request lifetime. 7 | 8 | For example, a router can set variables extracted from the URL and later 9 | application handlers can access those values, or it can be used to store 10 | sessions values to be saved at the end of a request. There are several 11 | others common uses. 12 | 13 | The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: 14 | 15 | http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 16 | 17 | Here's the basic usage: first define the keys that you will need. The key 18 | type is interface{} so a key can be of any type that supports equality. 19 | Here we define a key using a custom int type to avoid name collisions: 20 | 21 | package foo 22 | 23 | import ( 24 | "github.com/gorilla/context" 25 | ) 26 | 27 | type key int 28 | 29 | const MyKey key = 0 30 | 31 | Then set a variable. Variables are bound to an http.Request object, so you 32 | need a request instance to set a value: 33 | 34 | context.Set(r, MyKey, "bar") 35 | 36 | The application can later access the variable using the same key you provided: 37 | 38 | func MyHandler(w http.ResponseWriter, r *http.Request) { 39 | // val is "bar". 40 | val := context.Get(r, foo.MyKey) 41 | 42 | // returns ("bar", true) 43 | val, ok := context.GetOk(r, foo.MyKey) 44 | // ... 45 | } 46 | 47 | And that's all about the basic usage. We discuss some other ideas below. 48 | 49 | Any type can be stored in the context. To enforce a given type, make the key 50 | private and wrap Get() and Set() to accept and return values of a specific 51 | type: 52 | 53 | type key int 54 | 55 | const mykey key = 0 56 | 57 | // GetMyKey returns a value for this package from the request values. 58 | func GetMyKey(r *http.Request) SomeType { 59 | if rv := context.Get(r, mykey); rv != nil { 60 | return rv.(SomeType) 61 | } 62 | return nil 63 | } 64 | 65 | // SetMyKey sets a value for this package in the request values. 66 | func SetMyKey(r *http.Request, val SomeType) { 67 | context.Set(r, mykey, val) 68 | } 69 | 70 | Variables must be cleared at the end of a request, to remove all values 71 | that were stored. This can be done in an http.Handler, after a request was 72 | served. Just call Clear() passing the request: 73 | 74 | context.Clear(r) 75 | 76 | ...or use ClearHandler(), which conveniently wraps an http.Handler to clear 77 | variables at the end of a request lifetime. 78 | 79 | The Routers from the packages gorilla/mux and gorilla/pat call Clear() 80 | so if you are using either of them you don't need to clear the context manually. 81 | */ 82 | package context 83 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/internal/firetest/notify_db.go: -------------------------------------------------------------------------------- 1 | package firetest 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | _sync "sync" 7 | "time" 8 | 9 | "github.com/zabawaba99/firego/sync" 10 | ) 11 | 12 | type event struct { 13 | Name string 14 | Data eventData 15 | } 16 | 17 | type eventData struct { 18 | Path string `json:"path"` 19 | Data *sync.Node `json:"data"` 20 | } 21 | 22 | func (ed eventData) MarshalJSON() ([]byte, error) { 23 | type eventData2 eventData 24 | ed2 := eventData2(ed) 25 | ed2.Path = "/" + ed2.Path 26 | return json.Marshal(ed2) 27 | } 28 | 29 | func newEvent(name, path string, n *sync.Node) event { 30 | return event{ 31 | Name: "put", 32 | Data: eventData{ 33 | Path: path, 34 | Data: n, 35 | }, 36 | } 37 | } 38 | 39 | type notifyDB struct { 40 | intDB *sync.Database 41 | 42 | watchersMtx _sync.RWMutex 43 | watchers map[string][]chan event 44 | } 45 | 46 | func newNotifyDB() *notifyDB { 47 | return ¬ifyDB{ 48 | intDB: sync.NewDB(), 49 | watchers: map[string][]chan event{}, 50 | } 51 | } 52 | 53 | func (db *notifyDB) add(path string, n *sync.Node) { 54 | db.intDB.Add(path, n) 55 | go db.notify(newEvent("put", path, n)) 56 | } 57 | 58 | func (db *notifyDB) update(path string, n *sync.Node) { 59 | db.intDB.Update(path, n) 60 | go db.notify(newEvent("patch", path, n)) 61 | } 62 | 63 | func (db *notifyDB) del(path string) { 64 | db.intDB.Del(path) 65 | go db.notify(newEvent("put", path, nil)) 66 | } 67 | 68 | func (db *notifyDB) get(path string) *sync.Node { 69 | return db.intDB.Get(path) 70 | } 71 | 72 | func (db *notifyDB) notify(e event) { 73 | db.watchersMtx.RLock() 74 | for path, listeners := range db.watchers { 75 | if !strings.HasPrefix(e.Data.Path, path) { 76 | continue 77 | } 78 | 79 | // Make sure to not return full path when notifying 80 | // only return the path relative to the watcher 81 | e.Data.Path = strings.TrimPrefix(e.Data.Path, path) 82 | e.Data.Path = sanitizePath(e.Data.Path) 83 | 84 | for _, c := range listeners { 85 | select { 86 | case c <- e: 87 | case <-time.After(250 * time.Millisecond): 88 | continue 89 | } 90 | } 91 | } 92 | db.watchersMtx.RUnlock() 93 | } 94 | 95 | func (db *notifyDB) stopWatching(path string, c chan event) { 96 | db.watchersMtx.Lock() 97 | index := -1 98 | for i, ch := range db.watchers[path] { 99 | if ch == c { 100 | index = i 101 | break 102 | } 103 | } 104 | 105 | if index > -1 { 106 | a := db.watchers[path] 107 | db.watchers[path] = append(a[:index], a[index+1:]...) 108 | close(c) 109 | } 110 | db.watchersMtx.Unlock() 111 | } 112 | 113 | func (db *notifyDB) watch(path string) chan event { 114 | c := make(chan event) 115 | 116 | db.watchersMtx.Lock() 117 | db.watchers[path] = append(db.watchers[path], c) 118 | db.watchersMtx.Unlock() 119 | 120 | return c 121 | } 122 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/static_test.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | ) 9 | 10 | func TestStatic(t *testing.T) { 11 | response := httptest.NewRecorder() 12 | response.Body = new(bytes.Buffer) 13 | 14 | n := New() 15 | n.Use(NewStatic(http.Dir("."))) 16 | 17 | req, err := http.NewRequest("GET", "http://localhost:3000/negroni.go", nil) 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | n.ServeHTTP(response, req) 22 | expect(t, response.Code, http.StatusOK) 23 | expect(t, response.Header().Get("Expires"), "") 24 | if response.Body.Len() == 0 { 25 | t.Errorf("Got empty body for GET request") 26 | } 27 | } 28 | 29 | func TestStaticHead(t *testing.T) { 30 | response := httptest.NewRecorder() 31 | response.Body = new(bytes.Buffer) 32 | 33 | n := New() 34 | n.Use(NewStatic(http.Dir("."))) 35 | n.UseHandler(http.NotFoundHandler()) 36 | 37 | req, err := http.NewRequest("HEAD", "http://localhost:3000/negroni.go", nil) 38 | if err != nil { 39 | t.Error(err) 40 | } 41 | 42 | n.ServeHTTP(response, req) 43 | expect(t, response.Code, http.StatusOK) 44 | if response.Body.Len() != 0 { 45 | t.Errorf("Got non-empty body for HEAD request") 46 | } 47 | } 48 | 49 | func TestStaticAsPost(t *testing.T) { 50 | response := httptest.NewRecorder() 51 | 52 | n := New() 53 | n.Use(NewStatic(http.Dir("."))) 54 | n.UseHandler(http.NotFoundHandler()) 55 | 56 | req, err := http.NewRequest("POST", "http://localhost:3000/negroni.go", nil) 57 | if err != nil { 58 | t.Error(err) 59 | } 60 | 61 | n.ServeHTTP(response, req) 62 | expect(t, response.Code, http.StatusNotFound) 63 | } 64 | 65 | func TestStaticBadDir(t *testing.T) { 66 | response := httptest.NewRecorder() 67 | 68 | n := Classic() 69 | n.UseHandler(http.NotFoundHandler()) 70 | 71 | req, err := http.NewRequest("GET", "http://localhost:3000/negroni.go", nil) 72 | if err != nil { 73 | t.Error(err) 74 | } 75 | 76 | n.ServeHTTP(response, req) 77 | refute(t, response.Code, http.StatusOK) 78 | } 79 | 80 | func TestStaticOptionsServeIndex(t *testing.T) { 81 | response := httptest.NewRecorder() 82 | 83 | n := New() 84 | s := NewStatic(http.Dir(".")) 85 | s.IndexFile = "negroni.go" 86 | n.Use(s) 87 | 88 | req, err := http.NewRequest("GET", "http://localhost:3000/", nil) 89 | if err != nil { 90 | t.Error(err) 91 | } 92 | 93 | n.ServeHTTP(response, req) 94 | expect(t, response.Code, http.StatusOK) 95 | } 96 | 97 | func TestStaticOptionsPrefix(t *testing.T) { 98 | response := httptest.NewRecorder() 99 | 100 | n := New() 101 | s := NewStatic(http.Dir(".")) 102 | s.Prefix = "/public" 103 | n.Use(s) 104 | 105 | // Check file content behaviour 106 | req, err := http.NewRequest("GET", "http://localhost:3000/public/negroni.go", nil) 107 | if err != nil { 108 | t.Error(err) 109 | } 110 | 111 | n.ServeHTTP(response, req) 112 | expect(t, response.Code, http.StatusOK) 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/response_writer.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | // ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about 11 | // the response. It is recommended that middleware handlers use this construct to wrap a responsewriter 12 | // if the functionality calls for it. 13 | type ResponseWriter interface { 14 | http.ResponseWriter 15 | http.Flusher 16 | // Status returns the status code of the response or 0 if the response has not been written. 17 | Status() int 18 | // Written returns whether or not the ResponseWriter has been written. 19 | Written() bool 20 | // Size returns the size of the response body. 21 | Size() int 22 | // Before allows for a function to be called before the ResponseWriter has been written to. This is 23 | // useful for setting headers or any other operations that must happen before a response has been written. 24 | Before(func(ResponseWriter)) 25 | } 26 | 27 | type beforeFunc func(ResponseWriter) 28 | 29 | // NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter 30 | func NewResponseWriter(rw http.ResponseWriter) ResponseWriter { 31 | return &responseWriter{ 32 | ResponseWriter: rw, 33 | status: http.StatusOK, 34 | size: 0, 35 | beforeFuncs: nil} 36 | } 37 | 38 | type responseWriter struct { 39 | http.ResponseWriter 40 | status int 41 | size int 42 | beforeFuncs []beforeFunc 43 | } 44 | 45 | func (rw *responseWriter) WriteHeader(s int) { 46 | rw.status = s 47 | rw.callBefore() 48 | rw.ResponseWriter.WriteHeader(s) 49 | } 50 | 51 | func (rw *responseWriter) Write(b []byte) (int, error) { 52 | if !rw.Written() { 53 | // The status will be StatusOK if WriteHeader has not been called yet 54 | rw.WriteHeader(http.StatusOK) 55 | } 56 | size, err := rw.ResponseWriter.Write(b) 57 | rw.size += size 58 | return size, err 59 | } 60 | 61 | func (rw *responseWriter) Status() int { 62 | return rw.status 63 | } 64 | 65 | func (rw *responseWriter) Size() int { 66 | return rw.size 67 | } 68 | 69 | func (rw *responseWriter) Written() bool { 70 | return rw.status != 0 71 | } 72 | 73 | func (rw *responseWriter) Before(before func(ResponseWriter)) { 74 | rw.beforeFuncs = append(rw.beforeFuncs, before) 75 | } 76 | 77 | func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 78 | hijacker, ok := rw.ResponseWriter.(http.Hijacker) 79 | if !ok { 80 | return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") 81 | } 82 | return hijacker.Hijack() 83 | } 84 | 85 | func (rw *responseWriter) CloseNotify() <-chan bool { 86 | return rw.ResponseWriter.(http.CloseNotifier).CloseNotify() 87 | } 88 | 89 | func (rw *responseWriter) callBefore() { 90 | for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { 91 | rw.beforeFuncs[i](rw) 92 | } 93 | } 94 | 95 | func (rw *responseWriter) Flush() { 96 | flusher, ok := rw.ResponseWriter.(http.Flusher) 97 | if ok { 98 | flusher.Flush() 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/database.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "strings" 5 | "sync" 6 | ) 7 | 8 | // Database is a local representation of a Firebase database. 9 | type Database struct { 10 | root *Node 11 | 12 | mtx sync.RWMutex 13 | } 14 | 15 | // NewDB creates a new instance of a Database. 16 | func NewDB() *Database { 17 | return &Database{ 18 | root: &Node{ 19 | Children: map[string]*Node{}, 20 | }, 21 | } 22 | } 23 | 24 | // Add puts a Node into the database. 25 | func (d *Database) Add(path string, n *Node) { 26 | d.mtx.Lock() 27 | defer d.mtx.Unlock() 28 | 29 | if path == "" { 30 | d.root = n 31 | return 32 | } 33 | 34 | rabbitHole := strings.Split(path, "/") 35 | current := d.root 36 | for i := 0; i < len(rabbitHole)-1; i++ { 37 | step := rabbitHole[i] 38 | next, ok := current.Children[step] 39 | if !ok { 40 | next = &Node{ 41 | Parent: current, 42 | Key: step, 43 | Children: map[string]*Node{}, 44 | } 45 | current.Children[step] = next 46 | } 47 | next.Value = nil // no long has a value since it now has a child 48 | current, next = next, nil 49 | } 50 | 51 | lastPath := rabbitHole[len(rabbitHole)-1] 52 | current.Children[lastPath] = n 53 | n.Parent = current 54 | } 55 | 56 | // Update merges the current node with the given node. 57 | func (d *Database) Update(path string, n *Node) { 58 | d.mtx.Lock() 59 | defer d.mtx.Unlock() 60 | 61 | current := d.root 62 | rabbitHole := strings.Split(path, "/") 63 | 64 | for i := 0; i < len(rabbitHole); i++ { 65 | step := rabbitHole[i] 66 | if step == "" { 67 | // prevent against empty strings due to strings.Split 68 | continue 69 | } 70 | next, ok := current.Children[step] 71 | if !ok { 72 | next = &Node{ 73 | Parent: current, 74 | Key: step, 75 | Children: map[string]*Node{}, 76 | } 77 | current.Children[step] = next 78 | } 79 | next.Value = nil // no long has a value since it now has a child 80 | current, next = next, nil 81 | } 82 | 83 | current.merge(n) 84 | 85 | } 86 | 87 | // Del removes the node at the given path. 88 | func (d *Database) Del(path string) { 89 | d.mtx.Lock() 90 | defer d.mtx.Unlock() 91 | 92 | if path == "" { 93 | d.root = &Node{ 94 | Children: map[string]*Node{}, 95 | } 96 | return 97 | } 98 | 99 | rabbitHole := strings.Split(path, "/") 100 | current := d.root 101 | 102 | // traverse to target node's parent 103 | var delIdx int 104 | for ; delIdx < len(rabbitHole)-1; delIdx++ { 105 | next, ok := current.Children[rabbitHole[delIdx]] 106 | if !ok { 107 | // item does not exist, no need to do anything 108 | return 109 | } 110 | 111 | current = next 112 | } 113 | 114 | endNode := current 115 | leafPath := rabbitHole[len(rabbitHole)-1] 116 | delete(endNode.Children, leafPath) 117 | 118 | for tmp := endNode.prune(); tmp != nil; tmp = tmp.prune() { 119 | delIdx-- 120 | endNode = tmp 121 | } 122 | 123 | if endNode != nil { 124 | delete(endNode.Children, rabbitHole[delIdx]) 125 | } 126 | } 127 | 128 | // Get fetches a node at a given path. 129 | func (d *Database) Get(path string) *Node { 130 | d.mtx.RLock() 131 | defer d.mtx.RUnlock() 132 | 133 | current := d.root 134 | if path == "" { 135 | return current 136 | } 137 | 138 | rabbitHole := strings.Split(path, "/") 139 | for i := 0; i < len(rabbitHole); i++ { 140 | var ok bool 141 | current, ok = current.Children[rabbitHole[i]] 142 | if !ok { 143 | return nil 144 | } 145 | } 146 | return current 147 | } 148 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/context_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package context 6 | 7 | import ( 8 | "net/http" 9 | "testing" 10 | ) 11 | 12 | type keyType int 13 | 14 | const ( 15 | key1 keyType = iota 16 | key2 17 | ) 18 | 19 | func TestContext(t *testing.T) { 20 | assertEqual := func(val interface{}, exp interface{}) { 21 | if val != exp { 22 | t.Errorf("Expected %v, got %v.", exp, val) 23 | } 24 | } 25 | 26 | r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) 27 | emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) 28 | 29 | // Get() 30 | assertEqual(Get(r, key1), nil) 31 | 32 | // Set() 33 | Set(r, key1, "1") 34 | assertEqual(Get(r, key1), "1") 35 | assertEqual(len(data[r]), 1) 36 | 37 | Set(r, key2, "2") 38 | assertEqual(Get(r, key2), "2") 39 | assertEqual(len(data[r]), 2) 40 | 41 | //GetOk 42 | value, ok := GetOk(r, key1) 43 | assertEqual(value, "1") 44 | assertEqual(ok, true) 45 | 46 | value, ok = GetOk(r, "not exists") 47 | assertEqual(value, nil) 48 | assertEqual(ok, false) 49 | 50 | Set(r, "nil value", nil) 51 | value, ok = GetOk(r, "nil value") 52 | assertEqual(value, nil) 53 | assertEqual(ok, true) 54 | 55 | // GetAll() 56 | values := GetAll(r) 57 | assertEqual(len(values), 3) 58 | 59 | // GetAll() for empty request 60 | values = GetAll(emptyR) 61 | if values != nil { 62 | t.Error("GetAll didn't return nil value for invalid request") 63 | } 64 | 65 | // GetAllOk() 66 | values, ok = GetAllOk(r) 67 | assertEqual(len(values), 3) 68 | assertEqual(ok, true) 69 | 70 | // GetAllOk() for empty request 71 | values, ok = GetAllOk(emptyR) 72 | assertEqual(value, nil) 73 | assertEqual(ok, false) 74 | 75 | // Delete() 76 | Delete(r, key1) 77 | assertEqual(Get(r, key1), nil) 78 | assertEqual(len(data[r]), 2) 79 | 80 | Delete(r, key2) 81 | assertEqual(Get(r, key2), nil) 82 | assertEqual(len(data[r]), 1) 83 | 84 | // Clear() 85 | Clear(r) 86 | assertEqual(len(data), 0) 87 | } 88 | 89 | func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) { 90 | <-wait 91 | for i := 0; i < iterations; i++ { 92 | Get(r, key) 93 | } 94 | done <- struct{}{} 95 | 96 | } 97 | 98 | func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { 99 | <-wait 100 | for i := 0; i < iterations; i++ { 101 | Set(r, key, value) 102 | } 103 | done <- struct{}{} 104 | 105 | } 106 | 107 | func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { 108 | 109 | b.StopTimer() 110 | r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) 111 | done := make(chan struct{}) 112 | b.StartTimer() 113 | 114 | for i := 0; i < b.N; i++ { 115 | wait := make(chan struct{}) 116 | 117 | for i := 0; i < numReaders; i++ { 118 | go parallelReader(r, "test", iterations, wait, done) 119 | } 120 | 121 | for i := 0; i < numWriters; i++ { 122 | go parallelWriter(r, "test", "123", iterations, wait, done) 123 | } 124 | 125 | close(wait) 126 | 127 | for i := 0; i < numReaders+numWriters; i++ { 128 | <-done 129 | } 130 | 131 | } 132 | 133 | } 134 | 135 | func BenchmarkMutexSameReadWrite1(b *testing.B) { 136 | benchmarkMutex(b, 1, 1, 32) 137 | } 138 | func BenchmarkMutexSameReadWrite2(b *testing.B) { 139 | benchmarkMutex(b, 2, 2, 32) 140 | } 141 | func BenchmarkMutexSameReadWrite4(b *testing.B) { 142 | benchmarkMutex(b, 4, 4, 32) 143 | } 144 | func BenchmarkMutex1(b *testing.B) { 145 | benchmarkMutex(b, 2, 8, 32) 146 | } 147 | func BenchmarkMutex2(b *testing.B) { 148 | benchmarkMutex(b, 16, 4, 64) 149 | } 150 | func BenchmarkMutex3(b *testing.B) { 151 | benchmarkMutex(b, 1, 2, 128) 152 | } 153 | func BenchmarkMutex4(b *testing.B) { 154 | benchmarkMutex(b, 128, 32, 256) 155 | } 156 | func BenchmarkMutex5(b *testing.B) { 157 | benchmarkMutex(b, 1024, 2048, 64) 158 | } 159 | func BenchmarkMutex6(b *testing.B) { 160 | benchmarkMutex(b, 2048, 1024, 512) 161 | } 162 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/response_writer_test.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "bufio" 5 | "net" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | type closeNotifyingRecorder struct { 13 | *httptest.ResponseRecorder 14 | closed chan bool 15 | } 16 | 17 | func newCloseNotifyingRecorder() *closeNotifyingRecorder { 18 | return &closeNotifyingRecorder{ 19 | httptest.NewRecorder(), 20 | make(chan bool, 1), 21 | } 22 | } 23 | 24 | func (c *closeNotifyingRecorder) close() { 25 | c.closed <- true 26 | } 27 | 28 | func (c *closeNotifyingRecorder) CloseNotify() <-chan bool { 29 | return c.closed 30 | } 31 | 32 | type hijackableResponse struct { 33 | Hijacked bool 34 | } 35 | 36 | func newHijackableResponse() *hijackableResponse { 37 | return &hijackableResponse{} 38 | } 39 | 40 | func (h *hijackableResponse) Header() http.Header { return nil } 41 | func (h *hijackableResponse) Write(buf []byte) (int, error) { return 0, nil } 42 | func (h *hijackableResponse) WriteHeader(code int) {} 43 | func (h *hijackableResponse) Flush() {} 44 | func (h *hijackableResponse) Hijack() (net.Conn, *bufio.ReadWriter, error) { 45 | h.Hijacked = true 46 | return nil, nil, nil 47 | } 48 | 49 | func TestResponseWriterWritingString(t *testing.T) { 50 | rec := httptest.NewRecorder() 51 | rw := NewResponseWriter(rec) 52 | 53 | rw.Write([]byte("Hello world")) 54 | 55 | expect(t, rec.Code, rw.Status()) 56 | expect(t, rec.Body.String(), "Hello world") 57 | expect(t, rw.Status(), http.StatusOK) 58 | expect(t, rw.Size(), 11) 59 | expect(t, rw.Written(), true) 60 | } 61 | 62 | func TestResponseWriterWritingStrings(t *testing.T) { 63 | rec := httptest.NewRecorder() 64 | rw := NewResponseWriter(rec) 65 | 66 | rw.Write([]byte("Hello world")) 67 | rw.Write([]byte("foo bar bat baz")) 68 | 69 | expect(t, rec.Code, rw.Status()) 70 | expect(t, rec.Body.String(), "Hello worldfoo bar bat baz") 71 | expect(t, rw.Status(), http.StatusOK) 72 | expect(t, rw.Size(), 26) 73 | } 74 | 75 | func TestResponseWriterWritingHeader(t *testing.T) { 76 | rec := httptest.NewRecorder() 77 | rw := NewResponseWriter(rec) 78 | 79 | rw.WriteHeader(http.StatusNotFound) 80 | 81 | expect(t, rec.Code, rw.Status()) 82 | expect(t, rec.Body.String(), "") 83 | expect(t, rw.Status(), http.StatusNotFound) 84 | expect(t, rw.Size(), 0) 85 | } 86 | 87 | func TestResponseWriterBefore(t *testing.T) { 88 | rec := httptest.NewRecorder() 89 | rw := NewResponseWriter(rec) 90 | result := "" 91 | 92 | rw.Before(func(ResponseWriter) { 93 | result += "foo" 94 | }) 95 | rw.Before(func(ResponseWriter) { 96 | result += "bar" 97 | }) 98 | 99 | rw.WriteHeader(http.StatusNotFound) 100 | 101 | expect(t, rec.Code, rw.Status()) 102 | expect(t, rec.Body.String(), "") 103 | expect(t, rw.Status(), http.StatusNotFound) 104 | expect(t, rw.Size(), 0) 105 | expect(t, result, "barfoo") 106 | } 107 | 108 | func TestResponseWriterHijack(t *testing.T) { 109 | hijackable := newHijackableResponse() 110 | rw := NewResponseWriter(hijackable) 111 | hijacker, ok := rw.(http.Hijacker) 112 | expect(t, ok, true) 113 | _, _, err := hijacker.Hijack() 114 | if err != nil { 115 | t.Error(err) 116 | } 117 | expect(t, hijackable.Hijacked, true) 118 | } 119 | 120 | func TestResponseWriteHijackNotOK(t *testing.T) { 121 | hijackable := new(http.ResponseWriter) 122 | rw := NewResponseWriter(*hijackable) 123 | hijacker, ok := rw.(http.Hijacker) 124 | expect(t, ok, true) 125 | _, _, err := hijacker.Hijack() 126 | 127 | refute(t, err, nil) 128 | } 129 | 130 | func TestResponseWriterCloseNotify(t *testing.T) { 131 | rec := newCloseNotifyingRecorder() 132 | rw := NewResponseWriter(rec) 133 | closed := false 134 | notifier := rw.(http.CloseNotifier).CloseNotify() 135 | rec.close() 136 | select { 137 | case <-notifier: 138 | closed = true 139 | case <-time.After(time.Second): 140 | } 141 | expect(t, closed, true) 142 | } 143 | 144 | func TestResponseWriterFlusher(t *testing.T) { 145 | rec := httptest.NewRecorder() 146 | rw := NewResponseWriter(rec) 147 | 148 | _, ok := rw.(http.Flusher) 149 | expect(t, ok, true) 150 | } 151 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package context 6 | 7 | import ( 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | var ( 14 | mutex sync.RWMutex 15 | data = make(map[*http.Request]map[interface{}]interface{}) 16 | datat = make(map[*http.Request]int64) 17 | ) 18 | 19 | // Set stores a value for a given key in a given request. 20 | func Set(r *http.Request, key, val interface{}) { 21 | mutex.Lock() 22 | if data[r] == nil { 23 | data[r] = make(map[interface{}]interface{}) 24 | datat[r] = time.Now().Unix() 25 | } 26 | data[r][key] = val 27 | mutex.Unlock() 28 | } 29 | 30 | // Get returns a value stored for a given key in a given request. 31 | func Get(r *http.Request, key interface{}) interface{} { 32 | mutex.RLock() 33 | if ctx := data[r]; ctx != nil { 34 | value := ctx[key] 35 | mutex.RUnlock() 36 | return value 37 | } 38 | mutex.RUnlock() 39 | return nil 40 | } 41 | 42 | // GetOk returns stored value and presence state like multi-value return of map access. 43 | func GetOk(r *http.Request, key interface{}) (interface{}, bool) { 44 | mutex.RLock() 45 | if _, ok := data[r]; ok { 46 | value, ok := data[r][key] 47 | mutex.RUnlock() 48 | return value, ok 49 | } 50 | mutex.RUnlock() 51 | return nil, false 52 | } 53 | 54 | // GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. 55 | func GetAll(r *http.Request) map[interface{}]interface{} { 56 | mutex.RLock() 57 | if context, ok := data[r]; ok { 58 | result := make(map[interface{}]interface{}, len(context)) 59 | for k, v := range context { 60 | result[k] = v 61 | } 62 | mutex.RUnlock() 63 | return result 64 | } 65 | mutex.RUnlock() 66 | return nil 67 | } 68 | 69 | // GetAllOk returns all stored values for the request as a map and a boolean value that indicates if 70 | // the request was registered. 71 | func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { 72 | mutex.RLock() 73 | context, ok := data[r] 74 | result := make(map[interface{}]interface{}, len(context)) 75 | for k, v := range context { 76 | result[k] = v 77 | } 78 | mutex.RUnlock() 79 | return result, ok 80 | } 81 | 82 | // Delete removes a value stored for a given key in a given request. 83 | func Delete(r *http.Request, key interface{}) { 84 | mutex.Lock() 85 | if data[r] != nil { 86 | delete(data[r], key) 87 | } 88 | mutex.Unlock() 89 | } 90 | 91 | // Clear removes all values stored for a given request. 92 | // 93 | // This is usually called by a handler wrapper to clean up request 94 | // variables at the end of a request lifetime. See ClearHandler(). 95 | func Clear(r *http.Request) { 96 | mutex.Lock() 97 | clear(r) 98 | mutex.Unlock() 99 | } 100 | 101 | // clear is Clear without the lock. 102 | func clear(r *http.Request) { 103 | delete(data, r) 104 | delete(datat, r) 105 | } 106 | 107 | // Purge removes request data stored for longer than maxAge, in seconds. 108 | // It returns the amount of requests removed. 109 | // 110 | // If maxAge <= 0, all request data is removed. 111 | // 112 | // This is only used for sanity check: in case context cleaning was not 113 | // properly set some request data can be kept forever, consuming an increasing 114 | // amount of memory. In case this is detected, Purge() must be called 115 | // periodically until the problem is fixed. 116 | func Purge(maxAge int) int { 117 | mutex.Lock() 118 | count := 0 119 | if maxAge <= 0 { 120 | count = len(data) 121 | data = make(map[*http.Request]map[interface{}]interface{}) 122 | datat = make(map[*http.Request]int64) 123 | } else { 124 | min := time.Now().Unix() - int64(maxAge) 125 | for r := range data { 126 | if datat[r] < min { 127 | clear(r) 128 | count++ 129 | } 130 | } 131 | } 132 | mutex.Unlock() 133 | return count 134 | } 135 | 136 | // ClearHandler wraps an http.Handler and clears request values at the end 137 | // of a request lifetime. 138 | func ClearHandler(h http.Handler) http.Handler { 139 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 140 | defer Clear(r) 141 | h.ServeHTTP(w, r) 142 | }) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/example_test.go: -------------------------------------------------------------------------------- 1 | package firego_test 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "github.com/zabawaba99/firego" 8 | ) 9 | 10 | func ExampleFirebase_Auth() { 11 | fb := firego.New("https://someapp.firebaseio.com", nil) 12 | fb.Auth("my-token") 13 | } 14 | 15 | func ExampleFirebase_Child() { 16 | fb := firego.New("https://someapp.firebaseio.com", nil) 17 | childFB := fb.Child("some/child/path") 18 | 19 | log.Printf("My new ref %s\n", childFB) 20 | } 21 | 22 | func ExampleFirebase_Shallow() { 23 | fb := firego.New("https://someapp.firebaseio.com", nil) 24 | // Set value 25 | fb.Shallow(true) 26 | // Remove query parameter 27 | fb.Shallow(false) 28 | } 29 | 30 | func ExampleFirebase_IncludePriority() { 31 | fb := firego.New("https://someapp.firebaseio.com", nil) 32 | // Set value 33 | fb.IncludePriority(true) 34 | // Remove query parameter 35 | fb.IncludePriority(false) 36 | } 37 | 38 | func ExampleFirebase_StartAt() { 39 | fb := firego.New("https://someapp.firebaseio.com", nil) 40 | // Set value 41 | fb = fb.StartAt("a") 42 | // Remove query parameter 43 | fb = fb.StartAt("") 44 | } 45 | 46 | func ExampleFirebase_EndAt() { 47 | fb := firego.New("https://someapp.firebaseio.com", nil) 48 | // Set value 49 | fb = fb.EndAt("a") 50 | // Remove query parameter 51 | fb = fb.EndAt("") 52 | } 53 | 54 | func ExampleFirebase_OrderBy() { 55 | fb := firego.New("https://someapp.firebaseio.com", nil) 56 | // Set value 57 | fb = fb.OrderBy("a") 58 | // Remove query parameter 59 | fb = fb.OrderBy("") 60 | } 61 | 62 | func ExampleFirebase_LimitToFirst() { 63 | fb := firego.New("https://someapp.firebaseio.com", nil) 64 | // Set value 65 | fb = fb.LimitToFirst(5) 66 | // Remove query parameter 67 | fb = fb.LimitToFirst(-1) 68 | } 69 | 70 | func ExampleFirebase_LimitToLast() { 71 | fb := firego.New("https://someapp.firebaseio.com", nil) 72 | // Set value 73 | fb = fb.LimitToLast(8) 74 | // Remove query parameter 75 | fb = fb.LimitToLast(-1) 76 | } 77 | 78 | func ExampleFirebase_Push() { 79 | fb := firego.New("https://someapp.firebaseio.com", nil) 80 | newRef, err := fb.Push("my-value") 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | 85 | log.Printf("My new ref %s\n", newRef) 86 | } 87 | 88 | func ExampleFirebase_Remove() { 89 | fb := firego.New("https://someapp.firebaseio.com/some/value", nil) 90 | if err := fb.Remove(); err != nil { 91 | log.Fatal(err) 92 | } 93 | } 94 | 95 | func ExampleFirebase_Set() { 96 | fb := firego.New("https://someapp.firebaseio.com", nil) 97 | 98 | v := map[string]interface{}{ 99 | "foo": "bar", 100 | "bar": 1, 101 | "bez": []string{"hello", "world"}, 102 | } 103 | if err := fb.Set(v); err != nil { 104 | log.Fatal(err) 105 | } 106 | } 107 | 108 | func ExampleFirebase_Update() { 109 | fb := firego.New("https://someapp.firebaseio.com/some/value", nil) 110 | if err := fb.Update("new-value"); err != nil { 111 | log.Fatal(err) 112 | } 113 | } 114 | 115 | func ExampleFirebase_Value() { 116 | fb := firego.New("https://someapp.firebaseio.com/some/value", nil) 117 | var v interface{} 118 | if err := fb.Value(v); err != nil { 119 | log.Fatal(err) 120 | } 121 | 122 | log.Printf("My value %v\n", v) 123 | } 124 | 125 | func ExampleFirebase_Watch() { 126 | fb := firego.New("https://someapp.firebaseio.com/some/value", nil) 127 | notifications := make(chan firego.Event) 128 | if err := fb.Watch(notifications); err != nil { 129 | log.Fatal(err) 130 | } 131 | 132 | for event := range notifications { 133 | log.Println("Event Received") 134 | log.Printf("Type: %s\n", event.Type) 135 | log.Printf("Path: %s\n", event.Path) 136 | log.Printf("Data: %v\n", event.Data) 137 | if event.Type == firego.EventTypeError { 138 | log.Print("Error occurred, loop ending") 139 | } 140 | } 141 | } 142 | 143 | func ExampleFirebase_StopWatching() { 144 | fb := firego.New("https://someapp.firebaseio.com/some/value", nil) 145 | notifications := make(chan firego.Event) 146 | if err := fb.Watch(notifications); err != nil { 147 | log.Fatal(err) 148 | } 149 | 150 | go func() { 151 | for range notifications { 152 | } 153 | log.Println("Channel closed") 154 | }() 155 | time.Sleep(10 * time.Millisecond) // let go routine start 156 | 157 | fb.StopWatching() 158 | } 159 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/node.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | // Node represents an object linked in Database. This object 13 | // should not be created by hand, use NewNode when creating 14 | // a new instance of Node. 15 | type Node struct { 16 | mtx sync.RWMutex 17 | Key string 18 | Value interface{} 19 | Children map[string]*Node 20 | 21 | Parent *Node 22 | sliceKids bool 23 | } 24 | 25 | // NewNode converts the given data into a node. 26 | func NewNode(key string, data interface{}) *Node { 27 | n := &Node{ 28 | Key: key, 29 | } 30 | 31 | n.mtx.Lock() 32 | n.Children = map[string]*Node{} 33 | n.mtx.Unlock() 34 | 35 | if data == nil { 36 | return n 37 | } 38 | 39 | switch val := reflect.ValueOf(data); val.Kind() { 40 | case reflect.Map: 41 | for _, k := range val.MapKeys() { 42 | v := val.MapIndex(k) 43 | key := fmt.Sprintf("%s", k.Interface()) 44 | 45 | child := NewNode(key, v.Interface()) 46 | child.Parent = n 47 | n.Children[key] = child 48 | } 49 | 50 | case reflect.Array, reflect.Slice: 51 | n.sliceKids = true 52 | 53 | for i := 0; i < val.Len(); i++ { 54 | v := val.Index(i) 55 | key := strconv.FormatInt(int64(i), 10) 56 | 57 | child := NewNode(key, v.Interface()) 58 | child.Parent = n 59 | n.Children[key] = child 60 | } 61 | 62 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 63 | fallthrough 64 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 65 | fallthrough 66 | case reflect.Float32, reflect.Float64: 67 | fallthrough 68 | case reflect.String, reflect.Bool, reflect.Interface: 69 | n.Value = val.Interface() 70 | default: 71 | fmt.Printf("Unsupported type %s(%#v)If you see this log please report an issue on https://github.com/zabawaba99/firego", data, data) 72 | } 73 | 74 | return n 75 | } 76 | 77 | // MarshalJSON turns the node object into JSON bytes. 78 | func (n *Node) MarshalJSON() ([]byte, error) { 79 | return json.Marshal(n.Objectify()) 80 | } 81 | 82 | // Objectify turns the node and all its children into a go type. 83 | // If a node was created from a slice initially, a slice will be return. 84 | // If a node has child nodes, a map will be returned. 85 | // Otherwise, a primitive type will be returned. 86 | func (n *Node) Objectify() interface{} { 87 | n.mtx.RLock() 88 | defer n.mtx.RUnlock() 89 | 90 | if n.isNil() { 91 | return nil 92 | } 93 | 94 | if n.Value != nil { 95 | return n.Value 96 | } 97 | 98 | if n.sliceKids { 99 | obj := make([]interface{}, len(n.Children)) 100 | for k, v := range n.Children { 101 | index, err := strconv.Atoi(k) 102 | if err != nil { 103 | continue 104 | } 105 | obj[index] = v.Objectify() 106 | } 107 | return obj 108 | } 109 | 110 | obj := map[string]interface{}{} 111 | for k, v := range n.Children { 112 | obj[k] = v.Objectify() 113 | } 114 | 115 | return obj 116 | } 117 | 118 | // Child gets a DataSnapshot for the location at the specified relative path. 119 | // The relative path can either be a simple child key (e.g. 'fred') or a deeper 120 | // slash-separated path (e.g. 'fred/name/first'). 121 | func (n *Node) Child(name string) (*Node, bool) { 122 | n.mtx.RLock() 123 | defer n.mtx.RUnlock() 124 | 125 | name = strings.Trim(name, "/") 126 | rabbitHole := strings.Split(name, "/") 127 | 128 | current := n 129 | for i := 0; i < len(rabbitHole); i++ { 130 | next, ok := current.Children[rabbitHole[i]] 131 | if !ok { 132 | // item does not exist, no need to do anything 133 | return nil, false 134 | } 135 | 136 | current = next 137 | } 138 | return current, true 139 | } 140 | 141 | func (n *Node) isNil() bool { 142 | n.mtx.RLock() 143 | defer n.mtx.RUnlock() 144 | 145 | return n.Value == nil && len(n.Children) == 0 146 | } 147 | 148 | func (n *Node) merge(newNode *Node) { 149 | n.mtx.Lock() 150 | defer n.mtx.Unlock() 151 | 152 | for k, v := range newNode.Children { 153 | n.Children[k] = v 154 | } 155 | n.Value = newNode.Value 156 | } 157 | 158 | func (n *Node) prune() *Node { 159 | n.mtx.Lock() 160 | defer n.mtx.Unlock() 161 | 162 | if len(n.Children) > 0 || n.Value != nil { 163 | return nil 164 | } 165 | 166 | parent := n.Parent 167 | n.Parent = nil 168 | return parent 169 | } 170 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/negroni.go: -------------------------------------------------------------------------------- 1 | package negroni 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | // Handler handler is an interface that objects can implement to be registered to serve as middleware 10 | // in the Negroni middleware stack. 11 | // ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc 12 | // passed in. 13 | // 14 | // If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked. 15 | type Handler interface { 16 | ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) 17 | } 18 | 19 | // HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers. 20 | // If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f. 21 | type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) 22 | 23 | func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 24 | h(rw, r, next) 25 | } 26 | 27 | type middleware struct { 28 | handler Handler 29 | next *middleware 30 | } 31 | 32 | func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 33 | m.handler.ServeHTTP(rw, r, m.next.ServeHTTP) 34 | } 35 | 36 | // Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni 37 | // middleware. The next http.HandlerFunc is automatically called after the Handler 38 | // is executed. 39 | func Wrap(handler http.Handler) Handler { 40 | return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 41 | handler.ServeHTTP(rw, r) 42 | next(rw, r) 43 | }) 44 | } 45 | 46 | // Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler. 47 | // Negroni middleware is evaluated in the order that they are added to the stack using 48 | // the Use and UseHandler methods. 49 | type Negroni struct { 50 | middleware middleware 51 | handlers []Handler 52 | } 53 | 54 | // New returns a new Negroni instance with no middleware preconfigured. 55 | func New(handlers ...Handler) *Negroni { 56 | return &Negroni{ 57 | handlers: handlers, 58 | middleware: build(handlers), 59 | } 60 | } 61 | 62 | // Classic returns a new Negroni instance with the default middleware already 63 | // in the stack. 64 | // 65 | // Recovery - Panic Recovery Middleware 66 | // Logger - Request/Response Logging 67 | // Static - Static File Serving 68 | func Classic() *Negroni { 69 | return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public"))) 70 | } 71 | 72 | func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 73 | n.middleware.ServeHTTP(NewResponseWriter(rw), r) 74 | } 75 | 76 | // Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni. 77 | func (n *Negroni) Use(handler Handler) { 78 | if handler == nil { 79 | panic("handler cannot be nil") 80 | } 81 | 82 | n.handlers = append(n.handlers, handler) 83 | n.middleware = build(n.handlers) 84 | } 85 | 86 | // UseFunc adds a Negroni-style handler function onto the middleware stack. 87 | func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) { 88 | n.Use(HandlerFunc(handlerFunc)) 89 | } 90 | 91 | // UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni. 92 | func (n *Negroni) UseHandler(handler http.Handler) { 93 | n.Use(Wrap(handler)) 94 | } 95 | 96 | // UseHandler adds a http.HandlerFunc-style handler function onto the middleware stack. 97 | func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) { 98 | n.UseHandler(http.HandlerFunc(handlerFunc)) 99 | } 100 | 101 | // Run is a convenience function that runs the negroni stack as an HTTP 102 | // server. The addr string takes the same format as http.ListenAndServe. 103 | func (n *Negroni) Run(addr string) { 104 | l := log.New(os.Stdout, "[negroni] ", 0) 105 | l.Printf("listening on %s", addr) 106 | l.Fatal(http.ListenAndServe(addr, n)) 107 | } 108 | 109 | // Returns a list of all the handlers in the current Negroni middleware chain. 110 | func (n *Negroni) Handlers() []Handler { 111 | return n.handlers 112 | } 113 | 114 | func build(handlers []Handler) middleware { 115 | var next middleware 116 | 117 | if len(handlers) == 0 { 118 | return voidMiddleware() 119 | } else if len(handlers) > 1 { 120 | next = build(handlers[1:]) 121 | } else { 122 | next = voidMiddleware() 123 | } 124 | 125 | return middleware{handlers[0], &next} 126 | } 127 | 128 | func voidMiddleware() middleware { 129 | return middleware{ 130 | HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}), 131 | &middleware{}, 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/README.md: -------------------------------------------------------------------------------- 1 | # Firego 2 | --- 3 | [![Build Status](https://travis-ci.org/zabawaba99/firego.svg?branch=v1)](https://travis-ci.org/zabawaba99/firego) [![Coverage Status](https://coveralls.io/repos/github/zabawaba99/firego/badge.svg?branch=v1)](https://coveralls.io/github/zabawaba99/firego?branch=v1) 4 | --- 5 | 6 | A Firebase client written in Go 7 | 8 | ## Installation 9 | 10 | ```bash 11 | go get -u gopkg.in/zabawaba99/firego.v1 12 | ``` 13 | 14 | ## Usage 15 | 16 | Import firego 17 | 18 | ```go 19 | import "gopkg.in/zabawaba99/firego.v1" 20 | ``` 21 | 22 | Create a new firego reference 23 | 24 | ```go 25 | f := firego.New("https://my-firebase-app.firebaseIO.com", nil) 26 | ``` 27 | 28 | with existing http client 29 | 30 | ```go 31 | f := firego.New("https://my-firebase-app.firebaseIO.com", client) 32 | ``` 33 | 34 | ### Request Timeouts 35 | 36 | By default, the `Firebase` reference will timeout after 30 seconds of trying 37 | to reach a Firebase server. You can configure this value by setting the global 38 | timeout duration 39 | 40 | ```go 41 | firego.TimeoutDuration = time.Minute 42 | ``` 43 | 44 | ### Authentication 45 | 46 | You can authenticate with your `service_account.json` file by using the 47 | `golang.org/x/oauth2` package (thanks @m00sey for the snippet) 48 | 49 | ```go 50 | d, err := ioutil.ReadFile("our_service_account.json") 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | conf, err := google.JWTConfigFromJSON(d, "https://www.googleapis.com/auth/userinfo.email", 56 | "https://www.googleapis.com/auth/firebase.database") 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | fb := firego.New("https://you.firebaseio.com", conf.Client(oauth2.NoContext)) 62 | // use the authenticated fb instance 63 | ``` 64 | 65 | ### Legacy Tokens 66 | 67 | ```go 68 | f.Auth("some-token-that-was-created-for-me") 69 | f.Unauth() 70 | ``` 71 | 72 | Visit [Fireauth](https://github.com/zabawaba99/fireauth) if you'd like to generate your own auth tokens 73 | 74 | ### Get Value 75 | 76 | ```go 77 | var v map[string]interface{} 78 | if err := f.Value(&v); err != nil { 79 | log.Fatal(err) 80 | } 81 | fmt.Printf("%s\n", v) 82 | ``` 83 | 84 | #### Querying 85 | 86 | Take a look at Firebase's [query parameters](https://www.firebase.com/docs/rest/guide/retrieving-data.html#section-rest-filtering) 87 | for more information on what each function does. 88 | 89 | ```go 90 | var v map[string]interface{} 91 | if err := f.StartAt("a").EndAt("c").LimitToFirst(8).OrderBy("field").Value(&v); err != nil { 92 | log.Fatal(err) 93 | } 94 | fmt.Printf("%s\n", v) 95 | ``` 96 | 97 | ### Set Value 98 | 99 | ```go 100 | v := map[string]string{"foo":"bar"} 101 | if err := f.Set(v); err != nil { 102 | log.Fatal(err) 103 | } 104 | ``` 105 | 106 | ### Push Value 107 | 108 | ```go 109 | v := "bar" 110 | pushedFirego, err := f.Push(v) 111 | if err != nil { 112 | log.Fatal(err) 113 | } 114 | 115 | var bar string 116 | if err := pushedFirego.Value(&bar); err != nil { 117 | log.Fatal(err) 118 | } 119 | 120 | // prints "https://my-firebase-app.firebaseIO.com/-JgvLHXszP4xS0AUN-nI: bar" 121 | fmt.Printf("%s: %s\n", pushedFirego, bar) 122 | ``` 123 | 124 | ### Update Child 125 | 126 | ```go 127 | v := map[string]string{"foo":"bar"} 128 | if err := f.Update(v); err != nil { 129 | log.Fatal(err) 130 | } 131 | ``` 132 | 133 | ### Remove Value 134 | 135 | ```go 136 | if err := f.Remove(); err != nil { 137 | log.Fatal(err) 138 | } 139 | ``` 140 | 141 | ### Watch a Node 142 | 143 | ```go 144 | notifications := make(chan firego.Event) 145 | if err := f.Watch(notifications); err != nil { 146 | log.Fatal(err) 147 | } 148 | 149 | defer f.StopWatching() 150 | for event := range notifications { 151 | fmt.Printf("Event %#v\n", event) 152 | } 153 | fmt.Printf("Notifications have stopped") 154 | ``` 155 | ### Change reference 156 | 157 | You can use a reference to save or read data from a specified reference 158 | 159 | ```go 160 | userID := "bar" 161 | usersRef,err := f.Ref("users/"+userID) 162 | if err != nil { 163 | log.Fatal(err) 164 | } 165 | v := map[string]string{"id":userID} 166 | if err := usersRef.Set(v); err != nil { 167 | log.Fatal(err) 168 | } 169 | 170 | ``` 171 | 172 | Check the [GoDocs](http://godoc.org/gopkg.in/zabawaba99/firego.v1) or 173 | [Firebase Documentation](https://www.firebase.com/docs/rest/) for more details 174 | 175 | ## Running Tests 176 | 177 | In order to run the tests you need to `go get -t ./...` 178 | first to go-get the test dependencies. 179 | 180 | ## Issues Management 181 | 182 | Feel free to open an issue if you come across any bugs or 183 | if you'd like to request a new feature. 184 | 185 | ## Contributing 186 | 187 | 1. Fork it 188 | 2. Create your feature branch (`git checkout -b new-feature`) 189 | 3. Commit your changes (`git commit -am 'Some cool reflection'`) 190 | 4. Push to the branch (`git push origin new-feature`) 191 | 5. Create new Pull Request 192 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/firebase_test.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "strings" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | "github.com/zabawaba99/firego/internal/firetest" 14 | ) 15 | 16 | const URL = "https://somefirebaseapp.firebaseIO.com" 17 | 18 | const authToken = "token" 19 | 20 | type TestServer struct { 21 | *httptest.Server 22 | receivedReqs []*http.Request 23 | } 24 | 25 | func newTestServer(response string) *TestServer { 26 | ts := &TestServer{} 27 | ts.Server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 28 | ts.receivedReqs = append(ts.receivedReqs, req) 29 | fmt.Fprint(w, response) 30 | })) 31 | return ts 32 | } 33 | 34 | func TestNew(t *testing.T) { 35 | t.Parallel() 36 | testURLs := []string{ 37 | URL, 38 | URL + "/", 39 | "somefirebaseapp.firebaseIO.com", 40 | "somefirebaseapp.firebaseIO.com/", 41 | } 42 | 43 | for _, url := range testURLs { 44 | fb := New(url, nil) 45 | assert.Equal(t, URL, fb.url, "givenURL: %s", url) 46 | } 47 | } 48 | 49 | func TestNewWithProvidedHttpClient(t *testing.T) { 50 | t.Parallel() 51 | 52 | var client = http.DefaultClient 53 | testURLs := []string{ 54 | URL, 55 | URL + "/", 56 | "somefirebaseapp.firebaseIO.com", 57 | "somefirebaseapp.firebaseIO.com/", 58 | } 59 | 60 | for _, url := range testURLs { 61 | fb := New(url, client) 62 | assert.Equal(t, URL, fb.url, "givenURL: %s", url) 63 | assert.Equal(t, client, fb.client) 64 | } 65 | } 66 | 67 | func TestAuth(t *testing.T) { 68 | t.Parallel() 69 | server := firetest.New() 70 | server.Start() 71 | defer server.Close() 72 | 73 | server.RequireAuth(true) 74 | fb := New(server.URL, nil) 75 | 76 | fb.Auth(server.Secret) 77 | var v interface{} 78 | err := fb.Value(&v) 79 | assert.NoError(t, err) 80 | } 81 | 82 | func TestUnauth(t *testing.T) { 83 | t.Parallel() 84 | server := firetest.New() 85 | server.Start() 86 | defer server.Close() 87 | 88 | server.RequireAuth(true) 89 | fb := New(server.URL, nil) 90 | 91 | fb.params.Add("auth", server.Secret) 92 | fb.Unauth() 93 | err := fb.Value("") 94 | assert.Error(t, err) 95 | } 96 | 97 | func TestPush(t *testing.T) { 98 | t.Parallel() 99 | var ( 100 | payload = map[string]interface{}{"foo": "bar"} 101 | server = firetest.New() 102 | ) 103 | server.Start() 104 | defer server.Close() 105 | 106 | fb := New(server.URL, nil) 107 | childRef, err := fb.Push(payload) 108 | assert.NoError(t, err) 109 | 110 | path := strings.TrimPrefix(childRef.String(), server.URL+"/") 111 | v := server.Get(path) 112 | assert.Equal(t, payload, v) 113 | 114 | childRef.Auth(server.Secret) 115 | var m map[string]interface{} 116 | require.NoError(t, childRef.Value(&m)) 117 | assert.Equal(t, payload, m, childRef.String()) 118 | } 119 | 120 | func TestRemove(t *testing.T) { 121 | t.Parallel() 122 | server := firetest.New() 123 | server.Start() 124 | defer server.Close() 125 | 126 | server.Set("", true) 127 | 128 | fb := New(server.URL, nil) 129 | err := fb.Remove() 130 | assert.NoError(t, err) 131 | 132 | v := server.Get("") 133 | assert.Nil(t, v) 134 | } 135 | 136 | func TestSet(t *testing.T) { 137 | t.Parallel() 138 | var ( 139 | payload = map[string]interface{}{"foo": "bar"} 140 | server = firetest.New() 141 | ) 142 | server.Start() 143 | defer server.Close() 144 | 145 | fb := New(server.URL, nil) 146 | err := fb.Set(payload) 147 | assert.NoError(t, err) 148 | 149 | v := server.Get("") 150 | assert.Equal(t, payload, v) 151 | } 152 | 153 | func TestUpdate(t *testing.T) { 154 | t.Parallel() 155 | var ( 156 | payload = map[string]interface{}{"foo": "bar"} 157 | server = firetest.New() 158 | ) 159 | server.Start() 160 | defer server.Close() 161 | 162 | fb := New(server.URL, nil) 163 | err := fb.Update(payload) 164 | assert.NoError(t, err) 165 | 166 | v := server.Get("") 167 | assert.Equal(t, payload, v) 168 | } 169 | 170 | func TestValue(t *testing.T) { 171 | t.Parallel() 172 | var ( 173 | response = map[string]interface{}{"foo": "bar"} 174 | server = firetest.New() 175 | ) 176 | server.Start() 177 | defer server.Close() 178 | 179 | fb := New(server.URL, nil) 180 | 181 | server.Set("", response) 182 | 183 | var v map[string]interface{} 184 | err := fb.Value(&v) 185 | assert.NoError(t, err) 186 | assert.Equal(t, response, v) 187 | } 188 | 189 | func TestChild(t *testing.T) { 190 | t.Parallel() 191 | var ( 192 | parent = New(URL, nil) 193 | childNode = "node" 194 | child = parent.Child(childNode) 195 | ) 196 | 197 | assert.Equal(t, fmt.Sprintf("%s/%s", parent.url, childNode), child.url) 198 | } 199 | 200 | func TestChild_Issue26(t *testing.T) { 201 | t.Parallel() 202 | parent := New(URL, nil) 203 | child1 := parent.Child("one") 204 | child2 := child1.Child("two") 205 | 206 | child1.Shallow(true) 207 | assert.Len(t, child2.params, 0) 208 | } 209 | 210 | func TestTimeoutDuration_Headers(t *testing.T) { 211 | var fb *Firebase 212 | done := make(chan struct{}) 213 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 214 | time.Sleep(2 * fb.clientTimeout) 215 | close(done) 216 | })) 217 | defer server.Close() 218 | 219 | fb = New(server.URL, nil) 220 | fb.clientTimeout = time.Millisecond 221 | err := fb.Value("") 222 | <-done 223 | assert.NotNil(t, err) 224 | assert.IsType(t, ErrTimeout{}, err) 225 | 226 | // ResponseHeaderTimeout should be TimeoutDuration less the time it took to dial, and should be positive 227 | require.IsType(t, (*http.Transport)(nil), fb.client.Transport) 228 | tr := fb.client.Transport.(*http.Transport) 229 | assert.True(t, tr.ResponseHeaderTimeout < TimeoutDuration) 230 | assert.True(t, tr.ResponseHeaderTimeout > 0) 231 | } 232 | 233 | func TestTimeoutDuration_Dial(t *testing.T) { 234 | fb := New("http://dialtimeouterr.or/", nil) 235 | fb.clientTimeout = time.Millisecond 236 | 237 | err := fb.Value("") 238 | assert.NotNil(t, err) 239 | assert.IsType(t, ErrTimeout{}, err) 240 | 241 | // ResponseHeaderTimeout should be negative since the total duration was consumed when dialing 242 | require.IsType(t, (*http.Transport)(nil), fb.client.Transport) 243 | assert.True(t, fb.client.Transport.(*http.Transport).ResponseHeaderTimeout < 0) 244 | } 245 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/translations/README_zh_tw.md: -------------------------------------------------------------------------------- 1 | # Negroni(尼格龍尼) [![GoDoc](https://godoc.org/github.com/codegangsta/negroni?status.svg)](http://godoc.org/github.com/codegangsta/negroni) [![wercker status](https://app.wercker.com/status/13688a4a94b82d84a0b8d038c4965b61/s "wercker status")](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61) 2 | 3 | 尼格龍尼符合Go的web 中介器特性. 精簡、非侵入式、鼓勵使用 `net/http` Handler. 4 | 5 | 如果你喜歡[Martini](http://github.com/go-martini/martini),但覺得這其中包太多神奇的功能,那麼尼格龍尼會是你的最佳選擇。 6 | 7 | ## 入門 8 | 9 | 安裝完Go且設好[GOPATH](http://golang.org/doc/code.html#GOPATH),建立你的第一個`.go`檔。可以命名為`server.go`。 10 | 11 | ~~~ go 12 | package main 13 | 14 | import ( 15 | "github.com/codegangsta/negroni" 16 | "net/http" 17 | "fmt" 18 | ) 19 | 20 | func main() { 21 | mux := http.NewServeMux() 22 | mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 23 | fmt.Fprintf(w, "Welcome to the home page!") 24 | }) 25 | 26 | n := negroni.Classic() 27 | n.UseHandler(mux) 28 | n.Run(":3000") 29 | } 30 | ~~~ 31 | 32 | 安裝尼格龍尼套件 (最低需求為**go 1.1**或更高版本): 33 | ~~~ 34 | go get github.com/codegangsta/negroni 35 | ~~~ 36 | 37 | 執行伺服器: 38 | ~~~ 39 | go run server.go 40 | ~~~ 41 | 42 | 你現在起了一個Go的net/http網頁伺服器在`localhost:3000`. 43 | 44 | ## 有問題? 45 | 如果你有問題或新功能建議,[到這郵件群組討論](https://groups.google.com/forum/#!forum/negroni-users)。尼格龍尼在GitHub上的issues專欄是專門用來回報bug跟pull requests。 46 | 47 | ## 尼格龍尼是個framework嗎? 48 | 尼格龍尼**不是**framework,是個設計用來直接使用net/http的library。 49 | 50 | ## 路由? 51 | 尼格龍尼是BYOR (Bring your own Router,帶給你自訂路由)。在Go社群已經有大量可用的http路由器, 尼格龍尼試著做好完全支援`net/http`,例如與[Gorilla Mux](http://github.com/gorilla/mux)整合: 52 | 53 | ~~~ go 54 | router := mux.NewRouter() 55 | router.HandleFunc("/", HomeHandler) 56 | 57 | n := negroni.New(中介器1, 中介器2) 58 | // Or use a 中介器 with the Use() function 59 | n.Use(中介器3) 60 | // router goes last 61 | n.UseHandler(router) 62 | 63 | n.Run(":3000") 64 | ~~~ 65 | 66 | ## `negroni.Classic()` 67 | `negroni.Classic()` 提供一些好用的預設中介器: 68 | 69 | * `negroni.Recovery` - Panic 還原中介器 70 | * `negroni.Logging` - Request/Response 紀錄中介器 71 | * `negroni.Static` - 在"public"目錄下的靜態檔案服務 72 | 73 | 尼格龍尼的這些功能讓你開發變得很簡單。 74 | 75 | ## 處理器(Handlers) 76 | 尼格龍尼提供一個雙向中介器的機制,介面為`negroni.Handler`: 77 | 78 | ~~~ go 79 | type Handler interface { 80 | ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) 81 | } 82 | ~~~ 83 | 84 | 如果中介器沒有寫入ResponseWriter,會呼叫通道裡面的下個`http.HandlerFunc`讓給中介處理器。可以被用來做良好的應用: 85 | 86 | ~~~ go 87 | func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 88 | // 在這之前做一些事 89 | next(rw, r) 90 | // 在這之後做一些事 91 | } 92 | ~~~ 93 | 94 | 然後你可以透過`Use`函數對應到處理器的通道: 95 | 96 | ~~~ go 97 | n := negroni.New() 98 | n.Use(negroni.HandlerFunc(MyMiddleware)) 99 | ~~~ 100 | 101 | 你也可以應原始的舊`http.Handler`: 102 | 103 | ~~~ go 104 | n := negroni.New() 105 | 106 | mux := http.NewServeMux() 107 | // map your routes 108 | 109 | n.UseHandler(mux) 110 | 111 | n.Run(":3000") 112 | ~~~ 113 | 114 | ## `Run()` 115 | 尼格龍尼有一個很好用的函數`Run`,`Run`接收addr字串辨識[http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe)。 116 | 117 | ~~~ go 118 | n := negroni.Classic() 119 | // ... 120 | log.Fatal(http.ListenAndServe(":8080", n)) 121 | ~~~ 122 | 123 | ## 路由特有中介器 124 | 如果你有一群路由需要執行特別的中介器,你可以簡單的建立一個新的尼格龍尼實體當作路由處理器。 125 | 126 | ~~~ go 127 | router := mux.NewRouter() 128 | adminRoutes := mux.NewRouter() 129 | // add admin routes here 130 | 131 | // 為管理中介器建立一個新的尼格龍尼 132 | router.Handle("/admin", negroni.New( 133 | Middleware1, 134 | Middleware2, 135 | negroni.Wrap(adminRoutes), 136 | )) 137 | ~~~ 138 | 139 | ## 第三方中介器 140 | 141 | 以下為目前尼格龍尼兼容的中介器清單。如果你自己做了一個中介器請自由放入你的中介器互換連結: 142 | 143 | | 中介器 | 作者 | 說明 | 144 | | -----------|--------|-------------| 145 | | [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | REST API入口的安全認證 | 146 | | [Graceful](https://github.com/stretchr/graceful) | [Tyler Bunnell](https://github.com/tylerb) | 優雅的HTTP關機 | 147 | | [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | 檢疫安全功能的中介器 | 148 | | [JWT Middleware](https://github.com/auth0/go-jwt-middleware) | [Auth0](https://github.com/auth0) | 檢查JWT的中介器用來解析傳入請求的`Authorization` header | 149 | | [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | 將HTTP請求轉到structs的資料綁定 | 150 | | [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | 基於Logrus的紀錄器 | 151 | | [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | 渲染JSON、XML、HTML的樣板 | 152 | | [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | Go執行中的New Relic agent | 153 | | [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP回應壓縮 | 154 | | [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2中介器 | 155 | | [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session管理 | 156 | | [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies與使用者權限 | 157 | | [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | 快速產生TinySVG、HTM、CSS | 158 | | [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) 支援(CORS) | 159 | | [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | 在每個request指定一個隨機X-Request-Id header的中介器 | 160 | | [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC 授權中介器 | 161 | | [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | 儲存關於你的網頁應用資訊 (回應時間之類) | 162 | 163 | ## 範例 164 | [mooseware](https://github.com/xyproto/mooseware)是用來寫尼格龍尼中介處理器的骨架,由[Alexander Rødseth](https://github.com/xyproto)建立。 165 | 166 | ## 即時程式重載? 167 | [gin](https://github.com/codegangsta/gin)和[fresh](https://github.com/pilu/fresh)兩個尼格龍尼即時重載的應用。 168 | 169 | ## Go & 尼格龍尼初學者必讀 170 | 171 | * [使用Context將資訊從中介器送到處理器](http://elithrar.github.io/article/map-string-interface/) 172 | * [理解中介器](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters) 173 | 174 | ## 關於 175 | 176 | 尼格龍尼正是[Code Gangsta](http://codegangsta.io/)的執著設計。 177 | 譯者: Festum Qin (Festum@G.PL) 178 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/watch.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/json" 7 | "errors" 8 | "log" 9 | "net/http" 10 | "time" 11 | ) 12 | 13 | const ( 14 | // EventTypePut is the event type sent when new data is inserted to the 15 | // Firebase instance. 16 | EventTypePut = "put" 17 | // EventTypePatch is the event type sent when data at the Firebase instance is 18 | // updated. 19 | EventTypePatch = "patch" 20 | // EventTypeError is the event type sent when an unknown error is encountered. 21 | EventTypeError = "event_error" 22 | // EventTypeAuthRevoked is the event type sent when the supplied auth parameter 23 | // is no longer valid. 24 | EventTypeAuthRevoked = "auth_revoked" 25 | 26 | eventTypeKeepAlive = "keep-alive" 27 | eventTypeCancel = "cancel" 28 | eventTypeRulesDebug = "rules_debug" 29 | ) 30 | 31 | // Event represents a notification received when watching a 32 | // firebase reference. 33 | type Event struct { 34 | // Type of event that was received 35 | Type string 36 | // Path to the data that changed 37 | Path string 38 | // Data that changed 39 | Data interface{} 40 | 41 | rawData []byte 42 | } 43 | 44 | // Value converts the raw payload of the event into the given interface. 45 | func (e Event) Value(v interface{}) error { 46 | var tmp struct { 47 | Data interface{} `json:"data"` 48 | } 49 | tmp.Data = &v 50 | return json.Unmarshal(e.rawData, &tmp) 51 | } 52 | 53 | // StopWatching stops tears down all connections that are watching. 54 | func (fb *Firebase) StopWatching() { 55 | fb.watchMtx.Lock() 56 | defer fb.watchMtx.Unlock() 57 | 58 | if fb.watching { 59 | // flip the bit back to not watching 60 | fb.watching = false 61 | // signal connection to terminal 62 | fb.stopWatching <- struct{}{} 63 | } 64 | } 65 | 66 | func (fb *Firebase) setWatching(v bool) { 67 | fb.watchMtx.Lock() 68 | fb.watching = v 69 | fb.watchMtx.Unlock() 70 | } 71 | 72 | // Watch listens for changes on a firebase instance and 73 | // passes over to the given chan. 74 | // 75 | // Only one connection can be established at a time. The 76 | // second call to this function without a call to fb.StopWatching 77 | // will close the channel given and return nil immediately. 78 | func (fb *Firebase) Watch(notifications chan Event) error { 79 | fb.watchMtx.Lock() 80 | if fb.watching { 81 | fb.watchMtx.Unlock() 82 | close(notifications) 83 | return nil 84 | } 85 | fb.watching = true 86 | fb.watchMtx.Unlock() 87 | 88 | stop := make(chan struct{}) 89 | events, err := fb.watch(stop) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | var closedManually bool 95 | 96 | go func() { 97 | <-fb.stopWatching 98 | closedManually = true 99 | stop <- struct{}{} 100 | }() 101 | 102 | go func() { 103 | defer close(notifications) 104 | 105 | for event := range events { 106 | if closedManually { 107 | return 108 | } 109 | 110 | notifications <- event 111 | } 112 | }() 113 | 114 | return nil 115 | } 116 | 117 | func readLine(rdr *bufio.Reader, prefix string) ([]byte, error) { 118 | // read event: line 119 | line, err := rdr.ReadBytes('\n') 120 | if err != nil { 121 | return nil, err 122 | } 123 | 124 | // empty line check for empty prefix 125 | if len(prefix) == 0 { 126 | line = bytes.TrimSpace(line) 127 | if len(line) != 0 { 128 | return nil, errors.New("expected empty line") 129 | } 130 | return line, nil 131 | } 132 | 133 | // check line has event prefix 134 | if !bytes.HasPrefix(line, []byte(prefix)) { 135 | return nil, errors.New("missing prefix") 136 | } 137 | 138 | // trim space 139 | line = line[len(prefix):] 140 | return bytes.TrimSpace(line), nil 141 | } 142 | 143 | func (fb *Firebase) watch(stop chan struct{}) (chan Event, error) { 144 | // build SSE request 145 | req, err := http.NewRequest("GET", fb.String(), nil) 146 | if err != nil { 147 | fb.setWatching(false) 148 | return nil, err 149 | } 150 | req.Header.Add("Accept", "text/event-stream") 151 | 152 | // do request 153 | resp, err := fb.client.Do(req) 154 | if err != nil { 155 | fb.setWatching(false) 156 | return nil, err 157 | } 158 | 159 | notifications := make(chan Event) 160 | 161 | go func() { 162 | <-stop 163 | resp.Body.Close() 164 | }() 165 | 166 | heartbeat := make(chan struct{}) 167 | go func() { 168 | for { 169 | select { 170 | case <-heartbeat: 171 | // do nothing 172 | case <-time.After(fb.watchHeartbeat): 173 | resp.Body.Close() 174 | return 175 | } 176 | } 177 | }() 178 | 179 | // start parsing response body 180 | go func() { 181 | defer func() { 182 | resp.Body.Close() 183 | close(notifications) 184 | }() 185 | 186 | // build scanner for response body 187 | scanner := bufio.NewReader(resp.Body) 188 | sendError := func(err error) { 189 | notifications <- Event{ 190 | Type: EventTypeError, 191 | Data: err, 192 | } 193 | } 194 | for { 195 | select { 196 | case heartbeat <- struct{}{}: 197 | default: 198 | } 199 | // scan for 'event:' 200 | evt, err := readLine(scanner, "event: ") 201 | if err != nil { 202 | sendError(err) 203 | return 204 | } 205 | 206 | // scan for 'data:' 207 | dat, err := readLine(scanner, "data: ") 208 | if err != nil { 209 | sendError(err) 210 | return 211 | } 212 | 213 | // read the empty line 214 | _, err = readLine(scanner, "") 215 | if err != nil { 216 | sendError(err) 217 | return 218 | } 219 | 220 | // create a base event 221 | event := Event{ 222 | Type: string(evt), 223 | Data: string(dat), 224 | rawData: dat, 225 | } 226 | 227 | // should be reacting differently based off the type of event 228 | switch event.Type { 229 | case EventTypePut, EventTypePatch: 230 | // we've got extra data we've got to parse 231 | var data map[string]interface{} 232 | if err := json.Unmarshal(event.rawData, &data); err != nil { 233 | sendError(err) 234 | return 235 | } 236 | 237 | // set the extra fields 238 | event.Path = data["path"].(string) 239 | event.Data = data["data"] 240 | 241 | // ship it 242 | notifications <- event 243 | case eventTypeKeepAlive: 244 | // received ping - nothing to do here 245 | case eventTypeCancel: 246 | // The data for this event is null 247 | // This event will be sent if the Security and Firebase Rules 248 | // cause a read at the requested location to no longer be allowed 249 | 250 | // send the cancel event 251 | notifications <- event 252 | return 253 | case EventTypeAuthRevoked: 254 | // The data for this event is a string indicating that a the credential has expired 255 | // This event will be sent when the supplied auth parameter is no longer valid 256 | notifications <- event 257 | return 258 | case eventTypeRulesDebug: 259 | log.Printf("Rules-Debug: %s\n%s\n", evt, dat) 260 | } 261 | } 262 | }() 263 | return notifications, nil 264 | } 265 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/translations/README_zh_cn.md: -------------------------------------------------------------------------------- 1 | # Negroni [![GoDoc](https://godoc.org/github.com/codegangsta/negroni?status.svg)](http://godoc.org/github.com/codegangsta/negroni) [![wercker status](https://app.wercker.com/status/13688a4a94b82d84a0b8d038c4965b61/s "wercker status")](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61) 2 | 3 | 在Go语言里,Negroni 是一个很地道的 web 中间件,它是微型,非嵌入式,并鼓励使用原生 `net/http` 处理器的库。 4 | 5 | 如果你用过并喜欢 [Martini](http://github.com/go-martini/martini) 框架,但又不想框架中有太多魔幻性的特征,那 Negroni 就是你的菜了,相信它非常适合你。 6 | 7 | 8 | 语言翻译: 9 | * [Português Brasileiro (pt_BR)](translations/README_pt_br.md) 10 | * [简体中文 (zh_CN)](translations/README_zh_cn.md) 11 | 12 | ## 入门指导 13 | 14 | 当安装了 Go 语言并设置好了 [GOPATH](http://golang.org/doc/code.html#GOPATH) 后,新建你第一个`.go` 文件,我们叫它 `server.go` 吧。 15 | 16 | ~~~ go 17 | package main 18 | 19 | import ( 20 | "github.com/codegangsta/negroni" 21 | "net/http" 22 | "fmt" 23 | ) 24 | 25 | func main() { 26 | mux := http.NewServeMux() 27 | mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 28 | fmt.Fprintf(w, "Welcome to the home page!") 29 | }) 30 | 31 | n := negroni.Classic() 32 | n.UseHandler(mux) 33 | n.Run(":3000") 34 | } 35 | ~~~ 36 | 37 | 然后安装 Negroni 包(它依赖 **Go 1.1** 或更高的版本): 38 | ~~~ 39 | go get github.com/codegangsta/negroni 40 | ~~~ 41 | 42 | 然后运行刚建好的 server.go 文件: 43 | ~~~ 44 | go run server.go 45 | ~~~ 46 | 47 | 这时一个 Go `net/http` Web 服务器就跑在 `localhost:3000` 上,使用浏览器打开 `localhost:3000` 可以看到输出结果。 48 | 49 | ## 需要你的贡献 50 | 如果你有问题或新想法,请到[邮件群组](https://groups.google.com/forum/#!forum/negroni-users)里反馈,GitHub issues 是专门给提交 bug 报告和 pull 请求用途的,欢迎你的参与。 51 | 52 | ## Negroni 是一个框架吗? 53 | Negroni **不**是一个框架,它是为了方便使用 `net/http` 而设计的一个库而已。 54 | 55 | ## 路由呢? 56 | Negroni 没有带路由功能,使用 Negroni 时,需要找一个适合你的路由。不过好在 Go 社区里已经有相当多可用的路由,Negroni 更喜欢和那些完全支持 `net/http` 库的路由组合使用,比如,结合 [Gorilla Mux](http://github.com/gorilla/mux) 使用像这样: 57 | 58 | ~~~ go 59 | router := mux.NewRouter() 60 | router.HandleFunc("/", HomeHandler) 61 | 62 | n := negroni.New(Middleware1, Middleware2) 63 | // Or use a middleware with the Use() function 64 | n.Use(Middleware3) 65 | // router goes last 66 | n.UseHandler(router) 67 | 68 | n.Run(":3000") 69 | ~~~ 70 | 71 | ## `negroni.Classic()` 经典实例 72 | `negroni.Classic()` 提供一些默认的中间件,这些中间件在多数应用都很有用。 73 | 74 | * `negroni.Recovery` - 异常(恐慌)恢复中间件 75 | * `negroni.Logging` - 请求 / 响应 log 日志中间件 76 | * `negroni.Static` - 静态文件处理中间件,默认目录在 "public" 下. 77 | 78 | `negroni.Classic()` 让你一开始就非常容易上手 Negroni ,并使用它那些通用的功能。 79 | 80 | ## Handlers (处理器) 81 | Negroni 提供双向的中间件机制,这个特征很棒,都是得益于 `negroni.Handler` 这个接口。 82 | 83 | ~~~ go 84 | type Handler interface { 85 | ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) 86 | } 87 | ~~~ 88 | 89 | 如果一个中间件没有写入 ResponseWriter 响应,它会在中间件链里调用下一个 `http.HandlerFunc` 执行下去, 它可以这么优雅的使用。如下: 90 | 91 | ~~~ go 92 | func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 93 | // do some stuff before 94 | next(rw, r) 95 | // do some stuff after 96 | } 97 | ~~~ 98 | 99 | 你也可以用 `Use` 函数把这些 `http.Handler` 处理器引进到处理器链上来: 100 | 101 | ~~~ go 102 | n := negroni.New() 103 | n.Use(negroni.HandlerFunc(MyMiddleware)) 104 | ~~~ 105 | 106 | 你还可以使用 `http.Handler`(s) 把 `http.Handler` 处理器引进来。 107 | 108 | ~~~ go 109 | n := negroni.New() 110 | 111 | mux := http.NewServeMux() 112 | // map your routes 113 | 114 | n.UseHandler(mux) 115 | 116 | n.Run(":3000") 117 | ~~~ 118 | 119 | ## `Run()` 120 | Negroni 提供一个很好用的函数叫 `Run` ,把地址字符串传人该函数,即可实现很地道的 [http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe) 函数功能了。 121 | 122 | ~~~ go 123 | n := negroni.Classic() 124 | // ... 125 | log.Fatal(http.ListenAndServe(":8080", n)) 126 | ~~~ 127 | 128 | ## 特定路由中间件 129 | 如果你需要群组路由功能,需要借助特定的路由中间件完成,做法很简单,只需建立一个新 Negroni 实例,传人路由处理器里即可。 130 | 131 | ~~~ go 132 | router := mux.NewRouter() 133 | adminRoutes := mux.NewRouter() 134 | // add admin routes here 135 | 136 | // Create a new negroni for the admin middleware 137 | router.Handle("/admin", negroni.New( 138 | Middleware1, 139 | Middleware2, 140 | negroni.Wrap(adminRoutes), 141 | )) 142 | ~~~ 143 | 144 | ## 第三方中间件 145 | 146 | 以下的兼容 Negroni 的中间件列表,如果你也有兼容 Negroni 的中间件,可以提交到这个列表来交换链接,我们很乐意做这样有益的事情。 147 | 148 | 149 | | 中间件 | 作者 | 描述 | 150 | | -------------|------------|-------------| 151 | | [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | REST API 接口的安全认证 | 152 | | [Graceful](https://github.com/stretchr/graceful) | [Tyler Bunnell](https://github.com/tylerb) | 优雅关闭 HTTP 的中间件 | 153 | | [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins | 154 | | [JWT Middleware](https://github.com/auth0/go-jwt-middleware) | [Auth0](https://github.com/auth0) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it| 155 | | [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | HTTP 请求数据注入到 structs 实体| 156 | | [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | 基于 Logrus-based logger 日志 | 157 | | [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | 渲染 JSON, XML and HTML 中间件 | 158 | | [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime | 159 | | [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | 响应流 GZIP 压缩 | 160 | | [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 中间件 | 161 | | [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session 会话管理 | 162 | | [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, 用户和权限 | 163 | | [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | 快速生成 TinySVG, HTML and CSS 中间件 | 164 | | [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support | 165 | | [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | 给每个请求指定一个随机 X-Request-Id 头的中间件 | 166 | | [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) 基于 HMAC 鉴权认证的中间件 | 167 | | [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | 检测 web 应用当前运行状态信息 (响应时间等等。) | 168 | 169 | ## 范例 170 | [Alexander Rødseth](https://github.com/xyproto) 创建的 [mooseware](https://github.com/xyproto/mooseware) 是一个写兼容 Negroni 中间件的处理器骨架的范例。 171 | 172 | ## 即时编译 173 | [gin](https://github.com/codegangsta/gin) 和 [fresh](https://github.com/pilu/fresh) 这两个应用是即时编译的 Negroni 工具,推荐用户开发的时候使用。 174 | 175 | ## Go & Negroni 初学者必读推荐 176 | 177 | * [在中间件中使用上下文把消息传递给后端处理器](http://elithrar.github.io/article/map-string-interface/) 178 | * [了解中间件](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters) 179 | 180 | ## 关于 181 | 182 | Negroni 由 [Code Gangsta](http://codegangsta.io/) 主导设计开发完成 -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/watch_test.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "strings" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | "github.com/zabawaba99/firego/internal/firetest" 14 | ) 15 | 16 | func setupLargeResult() string { 17 | return "start" + strings.Repeat("0", 64*1024) + "end" 18 | } 19 | 20 | func TestWatch(t *testing.T) { 21 | server := firetest.New() 22 | server.Start() 23 | defer server.Close() 24 | 25 | fb := New(server.URL, nil) 26 | 27 | notifications := make(chan Event) 28 | err := fb.Watch(notifications) 29 | assert.NoError(t, err) 30 | 31 | l := setupLargeResult() 32 | server.Set("/foo", l) 33 | 34 | select { 35 | case event, ok := <-notifications: 36 | assert.True(t, ok) 37 | assert.Equal(t, "put", event.Type) 38 | assert.Equal(t, "/", event.Path) 39 | assert.Nil(t, event.Data) 40 | case <-time.After(250 * time.Millisecond): 41 | require.FailNow(t, "did not receive a notification initial notification") 42 | } 43 | 44 | select { 45 | case event, ok := <-notifications: 46 | assert.True(t, ok) 47 | assert.Equal(t, "/foo", event.Path) 48 | assert.EqualValues(t, l, event.Data) 49 | var v string 50 | assert.NoError(t, event.Value(&v)) 51 | assert.EqualValues(t, l, v) 52 | case <-time.After(250 * time.Millisecond): 53 | require.FailNow(t, "did not receive a notification") 54 | } 55 | } 56 | 57 | func TestWatchRedirectPreservesHeader(t *testing.T) { 58 | t.Parallel() 59 | 60 | redirectServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 61 | assert.Equal(t, []string{"text/event-stream"}, req.Header["Accept"]) 62 | w.Header().Set("Content-Type", "text/event-stream") 63 | w.WriteHeader(http.StatusOK) 64 | })) 65 | defer redirectServer.Close() 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 68 | w.Header().Set("Content-Type", "application/json") 69 | w.Header().Set("Location", redirectServer.URL) 70 | w.WriteHeader(http.StatusTemporaryRedirect) 71 | })) 72 | defer server.Close() 73 | 74 | fb := New(server.URL, nil) 75 | notifications := make(chan Event) 76 | 77 | err := fb.Watch(notifications) 78 | assert.NoError(t, err) 79 | } 80 | 81 | func TestWatchHeartbeatTimeout(t *testing.T) { 82 | var fb *Firebase 83 | 84 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 85 | flusher, ok := w.(http.Flusher) 86 | require.True(t, ok, "streaming unsupported") 87 | 88 | w.Header().Set("Content-Type", "text/event-stream") 89 | w.Header().Set("Cache-Control", "no-cache") 90 | w.Header().Set("Connection", "keep-alive") 91 | w.Header().Set("Access-Control-Allow-Origin", "*") 92 | flusher.Flush() 93 | time.Sleep(2 * fb.watchHeartbeat) 94 | })) 95 | defer server.Close() 96 | 97 | notifications := make(chan Event) 98 | fb = New(server.URL, nil) 99 | fb.watchHeartbeat = 50 * time.Millisecond 100 | 101 | if err := fb.Watch(notifications); err != nil { 102 | t.Fatal(err) 103 | } 104 | 105 | event, ok := <-notifications 106 | require.True(t, ok, "notifications closed") 107 | assert.Equal(t, EventTypeError, event.Type, "event type doesn't match") 108 | assert.Empty(t, event.Path, "event path is not empty") 109 | assert.NotNil(t, event.Data, "event data is nil") 110 | assert.Implements(t, new(error), event.Data) 111 | t.Logf("%#v\n", event) 112 | 113 | _, ok = <-notifications 114 | require.False(t, ok, "notifications still open") 115 | } 116 | 117 | func TestWatchError(t *testing.T) { 118 | t.Parallel() 119 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 120 | flusher, ok := w.(http.Flusher) 121 | require.True(t, ok, "streaming unsupported") 122 | 123 | w.Header().Set("Content-Type", "text/event-stream") 124 | w.Header().Set("Cache-Control", "no-cache") 125 | w.Header().Set("Connection", "keep-alive") 126 | w.Header().Set("Access-Control-Allow-Origin", "*") 127 | flusher.Flush() 128 | })) 129 | 130 | var ( 131 | notifications = make(chan Event) 132 | fb = New(server.URL, nil) 133 | ) 134 | defer server.Close() 135 | 136 | if err := fb.Watch(notifications); err != nil { 137 | t.Fatal(err) 138 | } 139 | 140 | go server.Close() 141 | event, ok := <-notifications 142 | require.True(t, ok, "notifications closed") 143 | assert.Equal(t, EventTypeError, event.Type, "event type doesn't match") 144 | assert.Empty(t, event.Path, "event path is not empty") 145 | assert.NotNil(t, event.Data, "event data is nil") 146 | assert.Implements(t, new(error), event.Data) 147 | } 148 | 149 | func TestWatchAuthRevoked(t *testing.T) { 150 | t.Parallel() 151 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 152 | flusher, ok := w.(http.Flusher) 153 | require.True(t, ok, "streaming unsupported") 154 | 155 | w.Header().Set("Content-Type", "text/event-stream") 156 | w.Header().Set("Cache-Control", "no-cache") 157 | w.Header().Set("Connection", "keep-alive") 158 | w.Header().Set("Access-Control-Allow-Origin", "*") 159 | flusher.Flush() 160 | fmt.Fprintf(w, "event: %s\ndata: %q\n\n", EventTypeAuthRevoked, "token expired") 161 | })) 162 | defer server.Close() 163 | 164 | var ( 165 | notifications = make(chan Event) 166 | fb = New(server.URL, nil) 167 | ) 168 | 169 | if err := fb.Watch(notifications); err != nil { 170 | t.Fatal(err) 171 | } 172 | 173 | event, ok := <-notifications 174 | require.True(t, ok, "notifications closed") 175 | assert.Equal(t, EventTypeAuthRevoked, event.Type, "event type doesn't match") 176 | assert.Empty(t, event.Path, "event path is not empty") 177 | assert.Equal(t, event.Data, `"token expired"`, "event data does not match") 178 | } 179 | 180 | func TestWatch_Issue66(t *testing.T) { 181 | t.Parallel() 182 | 183 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 184 | flusher, ok := w.(http.Flusher) 185 | require.True(t, ok, "streaming unsupported") 186 | 187 | w.Header().Set("Content-Type", "text/event-stream") 188 | w.Header().Set("Cache-Control", "no-cache") 189 | w.Header().Set("Connection", "keep-alive") 190 | w.Header().Set("Access-Control-Allow-Origin", "*") 191 | flusher.Flush() 192 | })) 193 | defer server.Close() 194 | 195 | // create an initial sse connection 196 | fb := New(server.URL, nil) 197 | notifications := make(chan Event) 198 | err := fb.Watch(notifications) 199 | require.NoError(t, err) 200 | 201 | // read the first connection 202 | <-notifications 203 | 204 | // close the server connection and read the error event 205 | go server.Close() 206 | <-notifications 207 | 208 | // call stop watching - everything should be a-ok 209 | fb.StopWatching() 210 | 211 | _, ok := <-notifications 212 | assert.False(t, ok) 213 | } 214 | 215 | func TestStopWatch(t *testing.T) { 216 | t.Parallel() 217 | 218 | server := firetest.New() 219 | server.Start() 220 | defer server.Close() 221 | 222 | fb := New(server.URL, nil) 223 | 224 | notifications := make(chan Event) 225 | err := fb.Watch(notifications) 226 | assert.NoError(t, err) 227 | 228 | <-notifications // get initial notification 229 | fb.StopWatching() 230 | _, ok := <-notifications 231 | assert.False(t, ok, "notifications should be closed") 232 | } 233 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/translations/README_pt_br.md: -------------------------------------------------------------------------------- 1 | # Negroni [![GoDoc](https://godoc.org/github.com/codegangsta/negroni?status.svg)](http://godoc.org/github.com/codegangsta/negroni) [![wercker status](https://app.wercker.com/status/13688a4a94b82d84a0b8d038c4965b61/s "wercker status")](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61) 2 | 3 | Negroni é uma abordagem idiomática para middleware web em Go. É pequeno, não intrusivo, e incentiva uso da biblioteca `net/http`. 4 | 5 | Se gosta da idéia do [Martini](http://github.com/go-martini/martini), mas acha que contém muita mágica, então Negroni é ideal. 6 | 7 | ## Começando 8 | 9 | Depois de instalar Go e definir seu [GOPATH](http://golang.org/doc/code.html#GOPATH), criar seu primeirto arquivo `.go`. Iremos chamá-lo `server.go`. 10 | 11 | ~~~ go 12 | package main 13 | 14 | import ( 15 | "github.com/codegangsta/negroni" 16 | "net/http" 17 | "fmt" 18 | ) 19 | 20 | func main() { 21 | mux := http.NewServeMux() 22 | mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 23 | fmt.Fprintf(w, "Welcome to the home page!") 24 | }) 25 | 26 | n := negroni.Classic() 27 | n.UseHandler(mux) 28 | n.Run(":3000") 29 | } 30 | ~~~ 31 | 32 | Depois instale o pacote Negroni (**go 1.1** ou superior) 33 | ~~~ 34 | go get github.com/codegangsta/negroni 35 | ~~~ 36 | 37 | Depois execute seu servidor: 38 | ~~~ 39 | go run server.go 40 | ~~~ 41 | 42 | Agora terá um servidor web Go net/http rodando em `localhost:3000`. 43 | 44 | ## Precisa de Ajuda? 45 | Se você tem uma pergunta ou pedido de recurso,[go ask the mailing list](https://groups.google.com/forum/#!forum/negroni-users). O Github issues para o Negroni será usado exclusivamente para Reportar bugs e pull requests. 46 | 47 | ## Negroni é um Framework? 48 | Negroni **não** é a framework. É uma biblioteca que é desenhada para trabalhar diretamente com net/http. 49 | 50 | ## Roteamento? 51 | Negroni é TSPR(Traga seu próprio Roteamento). A comunidade Go já tem um grande número de roteadores http disponíveis, Negroni tenta rodar bem com todos eles pelo suporte total `net/http`/ Por exemplo, a integração com [Gorilla Mux](http://github.com/gorilla/mux) se parece com isso: 52 | 53 | ~~~ go 54 | router := mux.NewRouter() 55 | router.HandleFunc("/", HomeHandler) 56 | 57 | n := negroni.New(Middleware1, Middleware2) 58 | // Or use a middleware with the Use() function 59 | n.Use(Middleware3) 60 | // router goes last 61 | n.UseHandler(router) 62 | 63 | n.Run(":3000") 64 | ~~~ 65 | 66 | ## `negroni.Classic()` 67 | `negroni.Classic()` fornece alguns middlewares padrão que são úteis para maioria das aplicações: 68 | 69 | * `negroni.Recovery` - Panic Recovery Middleware. 70 | * `negroni.Logging` - Request/Response Logging Middleware. 71 | * `negroni.Static` - Static File serving under the "public" directory. 72 | 73 | Isso torna muito fácil começar com alguns recursos úteis do Negroni. 74 | 75 | ## Handlers 76 | Negroni fornece um middleware de fluxo bidirecional. Isso é feito através da interface `negroni.Handler`: 77 | 78 | ~~~ go 79 | type Handler interface { 80 | ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) 81 | } 82 | ~~~ 83 | 84 | Se um middleware não tenha escrito o ResponseWriter, ele deve chamar a próxima `http.HandlerFunc` na cadeia para produzir o próximo handler middleware. Isso pode ser usado muito bem: 85 | 86 | ~~~ go 87 | func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 88 | // do some stuff before 89 | next(rw, r) 90 | // do some stuff after 91 | } 92 | ~~~ 93 | 94 | E pode mapear isso para a cadeia de handler com a função `Use`: 95 | 96 | ~~~ go 97 | n := negroni.New() 98 | n.Use(negroni.HandlerFunc(MyMiddleware)) 99 | ~~~ 100 | 101 | Você também pode mapear `http.Handler` antigos: 102 | 103 | ~~~ go 104 | n := negroni.New() 105 | 106 | mux := http.NewServeMux() 107 | // map your routes 108 | 109 | n.UseHandler(mux) 110 | 111 | n.Run(":3000") 112 | ~~~ 113 | 114 | ## `Run()` 115 | Negroni tem uma função de conveniência chamada `Run`. `Run` pega um endereço de string idêntico para [http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe). 116 | 117 | ~~~ go 118 | n := negroni.Classic() 119 | // ... 120 | log.Fatal(http.ListenAndServe(":8080", n)) 121 | ~~~ 122 | 123 | ## Middleware para Rotas Específicas 124 | Se você tem um grupo de rota com rotas que precisam ser executadas por um middleware específico, pode simplesmente criar uma nova instância de Negroni e usar no seu Manipulador de rota. 125 | 126 | ~~~ go 127 | router := mux.NewRouter() 128 | adminRoutes := mux.NewRouter() 129 | // add admin routes here 130 | 131 | // Criar um middleware negroni para admin 132 | router.Handle("/admin", negroni.New( 133 | Middleware1, 134 | Middleware2, 135 | negroni.Wrap(adminRoutes), 136 | )) 137 | ~~~ 138 | 139 | ## Middleware de Terceiros 140 | 141 | Aqui está uma lista atual de Middleware Compatíveis com Negroni. Sinta se livre para mandar um PR vinculando seu middleware se construiu um: 142 | 143 | 144 | | Middleware | Autor | Descrição | 145 | | -----------|--------|-------------| 146 | | [Graceful](https://github.com/stretchr/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown | 147 | | [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Implementa rapidamente itens de segurança.| 148 | | [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Handler para mapeamento/validação de um request a estrutura. | 149 | | [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger | 150 | | [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Pacote para renderizar JSON, XML, e templates HTML. | 151 | | [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime | 152 | | [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | Handler para adicionar compreção gzip para as requisições | 153 | | [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | Handler que prove sistema de login OAuth 2.0 para aplicações Martini. Google Sign-in, Facebook Connect e Github login são suportados. | 154 | | [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Handler que provê o serviço de sessão. | 155 | | [permissions](https://github.com/xyproto/permissions) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, usuários e permissões. | 156 | | [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Pacote para gerar TinySVG, HTML e CSS em tempo real. | 157 | 158 | ## Exemplos 159 | [Alexander Rødseth](https://github.com/xyproto) criou [mooseware](https://github.com/xyproto/mooseware), uma estrutura para escrever um handler middleware Negroni. 160 | 161 | ## Servidor com autoreload? 162 | [gin](https://github.com/codegangsta/gin) e [fresh](https://github.com/pilu/fresh) são aplicativos para autoreload do Negroni. 163 | 164 | ## Leitura Essencial para Iniciantes em Go & Negroni 165 | * [Usando um contexto para passar informação de um middleware para o manipulador final](http://elithrar.github.io/article/map-string-interface/) 166 | * [Entendendo middleware](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters) 167 | 168 | 169 | ## Sobre 170 | Negroni é obsessivamente desenhado por ninguém menos que [Code Gangsta](http://codegangsta.io/) 171 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/query_test.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestShallow(t *testing.T) { 11 | t.Parallel() 12 | var ( 13 | server = newTestServer("") 14 | fb = New(server.URL, nil) 15 | ) 16 | defer server.Close() 17 | 18 | fb.Shallow(true) 19 | fb.Value("") 20 | require.Len(t, server.receivedReqs, 1) 21 | 22 | req := server.receivedReqs[0] 23 | assert.Equal(t, shallowParam+"=true", req.URL.Query().Encode()) 24 | 25 | fb.Shallow(false) 26 | fb.Value("") 27 | require.Len(t, server.receivedReqs, 2) 28 | 29 | req = server.receivedReqs[1] 30 | assert.Equal(t, "", req.URL.Query().Encode()) 31 | } 32 | 33 | func TestOrderBy(t *testing.T) { 34 | t.Parallel() 35 | var ( 36 | server = newTestServer("") 37 | fb = New(server.URL, nil) 38 | ) 39 | defer server.Close() 40 | 41 | fb.OrderBy("user_id").Value("") 42 | require.Len(t, server.receivedReqs, 1) 43 | 44 | req := server.receivedReqs[0] 45 | assert.Equal(t, orderByParam+"=%22user_id%22", req.URL.Query().Encode()) 46 | } 47 | 48 | func TestEqualTo(t *testing.T) { 49 | t.Parallel() 50 | var ( 51 | server = newTestServer("") 52 | fb = New(server.URL, nil) 53 | ) 54 | defer server.Close() 55 | 56 | fb.EqualTo("user_id").Value("") 57 | require.Len(t, server.receivedReqs, 1) 58 | 59 | req := server.receivedReqs[0] 60 | assert.Equal(t, equalToParam+"=%22user_id%22", req.URL.Query().Encode()) 61 | } 62 | 63 | func TestEqualToValue(t *testing.T) { 64 | t.Parallel() 65 | var ( 66 | server = newTestServer("") 67 | fb = New(server.URL, nil) 68 | ) 69 | defer server.Close() 70 | 71 | fb.EqualToValue(2).Value("") 72 | fb.EqualToValue("2").Value("") 73 | fb.EqualToValue(2.14).Value("") 74 | fb.EqualToValue("bar").Value("") 75 | require.Len(t, server.receivedReqs, 4) 76 | 77 | req := server.receivedReqs[0] 78 | assert.Equal(t, equalToParam+"=2", req.URL.Query().Encode()) 79 | 80 | req = server.receivedReqs[1] 81 | assert.Equal(t, equalToParam+"=%222%22", req.URL.Query().Encode()) 82 | 83 | req = server.receivedReqs[2] 84 | assert.Equal(t, equalToParam+"=2.14", req.URL.Query().Encode()) 85 | 86 | req = server.receivedReqs[3] 87 | assert.Equal(t, equalToParam+"=%22bar%22", req.URL.Query().Encode()) 88 | 89 | } 90 | 91 | func TestLimitToFirst(t *testing.T) { 92 | t.Parallel() 93 | var ( 94 | server = newTestServer("") 95 | fb = New(server.URL, nil) 96 | ) 97 | defer server.Close() 98 | 99 | fb.LimitToFirst(2).Value("") 100 | require.Len(t, server.receivedReqs, 1) 101 | 102 | req := server.receivedReqs[0] 103 | assert.Equal(t, limitToFirstParam+"=2", req.URL.Query().Encode()) 104 | } 105 | 106 | func TestLimitToLast(t *testing.T) { 107 | t.Parallel() 108 | var ( 109 | server = newTestServer("") 110 | fb = New(server.URL, nil) 111 | ) 112 | defer server.Close() 113 | 114 | fb.LimitToLast(2).Value("") 115 | require.Len(t, server.receivedReqs, 1) 116 | 117 | req := server.receivedReqs[0] 118 | assert.Equal(t, limitToLastParam+"=2", req.URL.Query().Encode()) 119 | } 120 | 121 | func TestStartAt(t *testing.T) { 122 | t.Parallel() 123 | var ( 124 | server = newTestServer("") 125 | fb = New(server.URL, nil) 126 | ) 127 | defer server.Close() 128 | 129 | fb.StartAt("3").Value("") 130 | fb.StartAt("foo").Value("") 131 | require.Len(t, server.receivedReqs, 2) 132 | 133 | req := server.receivedReqs[0] 134 | assert.Equal(t, startAtParam+"=3", req.URL.Query().Encode()) 135 | 136 | req = server.receivedReqs[1] 137 | assert.Equal(t, startAtParam+"=%22foo%22", req.URL.Query().Encode()) 138 | } 139 | 140 | func TestStartAtValue(t *testing.T) { 141 | t.Parallel() 142 | var ( 143 | server = newTestServer("") 144 | fb = New(server.URL, nil) 145 | ) 146 | defer server.Close() 147 | 148 | fb.StartAtValue(3).Value("") 149 | fb.StartAtValue("3").Value("") 150 | fb.StartAtValue(3.14).Value("") 151 | fb.StartAtValue("foo").Value("") 152 | require.Len(t, server.receivedReqs, 4) 153 | 154 | req := server.receivedReqs[0] 155 | assert.Equal(t, startAtParam+"=3", req.URL.Query().Encode()) 156 | 157 | req = server.receivedReqs[1] 158 | assert.Equal(t, startAtParam+"=%223%22", req.URL.Query().Encode()) 159 | 160 | req = server.receivedReqs[2] 161 | assert.Equal(t, startAtParam+"=3.14", req.URL.Query().Encode()) 162 | 163 | req = server.receivedReqs[3] 164 | assert.Equal(t, startAtParam+"=%22foo%22", req.URL.Query().Encode()) 165 | } 166 | 167 | func TestEndAt(t *testing.T) { 168 | t.Parallel() 169 | var ( 170 | server = newTestServer("") 171 | fb = New(server.URL, nil) 172 | ) 173 | defer server.Close() 174 | 175 | fb.EndAt("4").Value("") 176 | fb.EndAt("theend").Value("") 177 | require.Len(t, server.receivedReqs, 2) 178 | 179 | req := server.receivedReqs[0] 180 | assert.Equal(t, endAtParam+"=4", req.URL.Query().Encode()) 181 | 182 | req = server.receivedReqs[1] 183 | assert.Equal(t, endAtParam+"=%22theend%22", req.URL.Query().Encode()) 184 | } 185 | 186 | func TestEndAtValue(t *testing.T) { 187 | t.Parallel() 188 | var ( 189 | server = newTestServer("") 190 | fb = New(server.URL, nil) 191 | ) 192 | defer server.Close() 193 | 194 | fb.EndAtValue(4).Value("") 195 | fb.EndAtValue(3.14).Value("") 196 | fb.EndAtValue("4").Value("") 197 | fb.EndAtValue("theend").Value("") 198 | require.Len(t, server.receivedReqs, 4) 199 | 200 | req := server.receivedReqs[0] 201 | assert.Equal(t, endAtParam+"=4", req.URL.Query().Encode()) 202 | 203 | req = server.receivedReqs[1] 204 | assert.Equal(t, endAtParam+"=3.14", req.URL.Query().Encode()) 205 | 206 | req = server.receivedReqs[2] 207 | assert.Equal(t, endAtParam+"=%224%22", req.URL.Query().Encode()) 208 | 209 | req = server.receivedReqs[3] 210 | assert.Equal(t, endAtParam+"=%22theend%22", req.URL.Query().Encode()) 211 | } 212 | 213 | func TestIncludePriority(t *testing.T) { 214 | t.Parallel() 215 | var ( 216 | server = newTestServer("") 217 | fb = New(server.URL, nil) 218 | ) 219 | defer server.Close() 220 | 221 | fb.IncludePriority(true) 222 | fb.Value("") 223 | require.Len(t, server.receivedReqs, 1) 224 | 225 | req := server.receivedReqs[0] 226 | assert.Equal(t, formatParam+"="+formatVal, req.URL.Query().Encode()) 227 | 228 | fb.IncludePriority(false) 229 | fb.Value("") 230 | require.Len(t, server.receivedReqs, 2) 231 | 232 | req = server.receivedReqs[1] 233 | assert.Equal(t, "", req.URL.Query().Encode()) 234 | } 235 | 236 | func TestQueryMultipleParams(t *testing.T) { 237 | t.Parallel() 238 | var ( 239 | server = newTestServer("") 240 | fb = New(server.URL, nil) 241 | ) 242 | defer server.Close() 243 | 244 | fb.OrderBy("user_id").StartAt("7").Value("") 245 | require.Len(t, server.receivedReqs, 1) 246 | 247 | req := server.receivedReqs[0] 248 | assert.Equal(t, orderByParam+"=%22user_id%22&startAt=7", req.URL.Query().Encode()) 249 | } 250 | 251 | func TestEscapeString(t *testing.T) { 252 | t.Parallel() 253 | 254 | testCases := []struct { 255 | value string 256 | expected string 257 | }{ 258 | {"foo", `"foo"`}, 259 | {"2", `2`}, 260 | {"false", `false`}, 261 | } 262 | for _, testCase := range testCases { 263 | assert.Equal(t, testCase.expected, escapeString(testCase.value)) 264 | } 265 | } 266 | 267 | func TestEscapeParameter(t *testing.T) { 268 | t.Parallel() 269 | 270 | testCases := []struct { 271 | value interface{} 272 | expected string 273 | }{ 274 | {"foo", `"foo"`}, 275 | {2, `2`}, 276 | {"3", `"3"`}, 277 | {true, `true`}, 278 | {"false", `"false"`}, 279 | {3.14, `3.14`}, 280 | } 281 | for _, testCase := range testCases { 282 | assert.Equal(t, testCase.expected, escapeParameter(testCase.value)) 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/query.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // StartAt creates a new Firebase reference with the 10 | // requested StartAt configuration. The value that is passed in 11 | // is automatically escaped if it is a string value. 12 | // Numeric strings are automatically converted to numbers. 13 | // 14 | // StartAt(7) // -> startAt=7 15 | // StartAt("7") // -> startAt=7 16 | // StartAt("foo") // -> startAt="foo" 17 | // StartAt(`"foo"`) // -> startAt="foo" 18 | // 19 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#section-rest-filtering 20 | func (fb *Firebase) StartAt(value string) *Firebase { 21 | c := fb.copy() 22 | if value != "" { 23 | c.params.Set(startAtParam, escapeString(value)) 24 | } else { 25 | c.params.Del(startAtParam) 26 | } 27 | return c 28 | } 29 | 30 | // StartAtValue creates a new Firebase reference with the 31 | // requested StartAt configuration. The value that is passed in 32 | // is automatically escaped if it is a string value. 33 | // Numeric strings are preserved as strings. 34 | // 35 | // StartAtValue(7) // -> startAt=7 36 | // StartAtValue("7") // -> startAt="7" 37 | // StartAtValue("foo") // -> startAt="foo" 38 | // StartAtValue(`"foo"`) // -> startAt="foo" 39 | // 40 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#section-rest-filtering 41 | func (fb *Firebase) StartAtValue(value interface{}) *Firebase { 42 | c := fb.copy() 43 | if value != "" { 44 | c.params.Set(startAtParam, escapeParameter(value)) 45 | } else { 46 | c.params.Del(startAtParam) 47 | } 48 | return c 49 | } 50 | 51 | // EndAt creates a new Firebase reference with the 52 | // requested EndAt configuration. The value that is passed in 53 | // is automatically escaped if it is a string value. 54 | // Numeric strings are automatically converted to numbers. 55 | // 56 | // EndAt(7) // -> endAt=7 57 | // EndAt("7") // -> endAt=7 58 | // EndAt("foo") // -> endAt="foo" 59 | // EndAt(`"foo"`) // -> endAt="foo" 60 | // 61 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#section-rest-filtering 62 | func (fb *Firebase) EndAt(value string) *Firebase { 63 | c := fb.copy() 64 | if value != "" { 65 | c.params.Set(endAtParam, escapeString(value)) 66 | } else { 67 | c.params.Del(endAtParam) 68 | } 69 | return c 70 | } 71 | 72 | // EndAtValue creates a new Firebase reference with the 73 | // requested EndAt configuration. The value that is passed in 74 | // is automatically escaped if it is a string value. 75 | // Numeric strings are preserved as strings. 76 | // 77 | // EndAtValue(7) // -> endAt=7 78 | // EndAtValue("7") // -> endAt="7" 79 | // EndAtValue("foo") // -> endAt="foo" 80 | // EndAtValue(`"foo"`) // -> endAt="foo" 81 | // 82 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#section-rest-filtering 83 | func (fb *Firebase) EndAtValue(value interface{}) *Firebase { 84 | c := fb.copy() 85 | if value != "" { 86 | c.params.Set(endAtParam, escapeParameter(value)) 87 | } else { 88 | c.params.Del(endAtParam) 89 | } 90 | return c 91 | } 92 | 93 | // OrderBy creates a new Firebase reference with the 94 | // requested OrderBy configuration. The value that is passed in 95 | // is automatically escaped if it is a string value. 96 | // 97 | // OrderBy("foo") // -> orderBy="foo" 98 | // OrderBy(`"foo"`) // -> orderBy="foo" 99 | // OrderBy("$key") // -> orderBy="$key" 100 | // 101 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#orderby 102 | func (fb *Firebase) OrderBy(value string) *Firebase { 103 | c := fb.copy() 104 | if value != "" { 105 | c.params.Set(orderByParam, escapeString(value)) 106 | } else { 107 | c.params.Del(orderByParam) 108 | } 109 | return c 110 | } 111 | 112 | // EqualTo sends the query string equalTo so that one can find nodes with 113 | // exactly matching values. The value that is passed in is automatically escaped 114 | // if it is a string value. 115 | // Numeric strings are automatically converted to numbers. 116 | // 117 | // EqualTo(7) // -> equalTo=7 118 | // EqualTo("7") // -> equalTo=7 119 | // EqualTo("foo") // -> equalTo="foo" 120 | // EqualTo(`"foo"`) // -> equalTo="foo" 121 | // 122 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#section-rest-filtering 123 | func (fb *Firebase) EqualTo(value string) *Firebase { 124 | c := fb.copy() 125 | if value != "" { 126 | c.params.Set(equalToParam, escapeString(value)) 127 | } else { 128 | c.params.Del(equalToParam) 129 | } 130 | return c 131 | } 132 | 133 | // EqualToValue sends the query string equalTo so that one can find nodes with 134 | // exactly matching values. The value that is passed in is automatically escaped 135 | // if it is a string value. 136 | // Numeric strings are preserved as strings. 137 | // 138 | // EqualToValue(7) // -> equalTo=7 139 | // EqualToValue("7") // -> equalTo="7" 140 | // EqualToValue("foo") // -> equalTo="foo" 141 | // EqualToValue(`"foo"`) // -> equalTo="foo" 142 | // 143 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#section-rest-filtering 144 | func (fb *Firebase) EqualToValue(value interface{}) *Firebase { 145 | c := fb.copy() 146 | if value != "" { 147 | c.params.Set(equalToParam, escapeParameter(value)) 148 | } else { 149 | c.params.Del(equalToParam) 150 | } 151 | return c 152 | } 153 | 154 | func escapeString(s string) string { 155 | _, errNotInt := strconv.ParseInt(s, 10, 64) 156 | _, errNotBool := strconv.ParseBool(s) 157 | if errNotInt == nil || errNotBool == nil { 158 | // we shouldn't escape bools or ints 159 | return s 160 | } 161 | return fmt.Sprintf(`%q`, strings.Trim(s, `"`)) 162 | } 163 | 164 | func escapeParameter(s interface{}) string { 165 | switch s.(type) { 166 | case string: 167 | return fmt.Sprintf(`%q`, strings.Trim(s.(string), `"`)) 168 | default: 169 | return fmt.Sprintf(`%v`, s) 170 | } 171 | } 172 | 173 | // LimitToFirst creates a new Firebase reference with the 174 | // requested limitToFirst configuration. 175 | // 176 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#limit-queries 177 | func (fb *Firebase) LimitToFirst(value int64) *Firebase { 178 | c := fb.copy() 179 | if value > 0 { 180 | c.params.Set(limitToFirstParam, strconv.FormatInt(value, 10)) 181 | } else { 182 | c.params.Del(limitToFirstParam) 183 | } 184 | return c 185 | } 186 | 187 | // LimitToLast creates a new Firebase reference with the 188 | // requested limitToLast configuration. 189 | // 190 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#limit-queries 191 | func (fb *Firebase) LimitToLast(value int64) *Firebase { 192 | c := fb.copy() 193 | if value > 0 { 194 | c.params.Set(limitToLastParam, strconv.FormatInt(value, 10)) 195 | } else { 196 | c.params.Del(limitToLastParam) 197 | } 198 | return c 199 | } 200 | 201 | // Shallow limits the depth of the data returned when calling Value. 202 | // If the data at the location is a JSON primitive (string, number or boolean), 203 | // its value will be returned. If the data is a JSON object, the values 204 | // for each key will be truncated to true. 205 | // 206 | // Reference https://firebase.google.com/docs/database/rest/retrieve-data#shallow 207 | func (fb *Firebase) Shallow(v bool) { 208 | if v { 209 | fb.params.Set(shallowParam, "true") 210 | } else { 211 | fb.params.Del(shallowParam) 212 | } 213 | } 214 | 215 | // IncludePriority determines whether or not to ask Firebase 216 | // for the values priority. By default, the priority is not returned. 217 | // 218 | // Reference https://www.firebase.com/docs/rest/api/#section-param-format 219 | func (fb *Firebase) IncludePriority(v bool) { 220 | if v { 221 | fb.params.Set(formatParam, formatVal) 222 | } else { 223 | fb.params.Del(formatParam) 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/sync/node_test.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func newTestNodeWithKids(children map[string]*Node) *Node { 14 | n := &Node{} 15 | for _, child := range children { 16 | child.Parent = n 17 | } 18 | n.Children = children 19 | return n 20 | } 21 | 22 | func equalNodes(expected, actual *Node) error { 23 | if ec, ac := len(expected.Children), len(actual.Children); ec != ac { 24 | return fmt.Errorf("Children count is not the same\n\tExpected: %d\n\tActual: %d", ec, ac) 25 | } 26 | 27 | if len(expected.Children) == 0 { 28 | if !assert.ObjectsAreEqualValues(expected.Value, actual.Value) { 29 | return fmt.Errorf("Node values not equal\n\tExpected: %T %v\n\tActual: %T %v", expected.Value, expected.Value, actual.Value, actual.Value) 30 | } 31 | return nil 32 | } 33 | 34 | for child, n := range expected.Children { 35 | n2, ok := actual.Children[child] 36 | if !ok { 37 | return fmt.Errorf("Expected node to have child: %s", child) 38 | } 39 | 40 | err := equalNodes(n, n2) 41 | if err != nil { 42 | return err 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | func TestNewNode(t *testing.T) { 49 | 50 | for _, test := range []struct { 51 | name string 52 | node *Node 53 | }{ 54 | { 55 | name: "scalars/string", 56 | node: NewNode("", "foo"), 57 | }, 58 | { 59 | name: "scalars/number", 60 | node: NewNode("", 2), 61 | }, 62 | { 63 | name: "scalars/decimal", 64 | node: NewNode("", 2.2), 65 | }, 66 | { 67 | name: "scalars/boolean", 68 | node: NewNode("", false), 69 | }, 70 | { 71 | name: "arrays/strings", 72 | node: NewNode("", []interface{}{"foo", "bar"}), 73 | }, 74 | { 75 | name: "arrays/booleans", 76 | node: NewNode("", []interface{}{true, false}), 77 | }, 78 | { 79 | name: "arrays/numbers", 80 | node: NewNode("", []interface{}{1, 2, 3}), 81 | }, 82 | { 83 | name: "arrays/decimals", 84 | node: NewNode("", []interface{}{1.1, 2.2, 3.3}), 85 | }, 86 | { 87 | name: "objects/simple", 88 | node: newTestNodeWithKids(map[string]*Node{ 89 | "foo": NewNode("", "bar"), 90 | }), 91 | }, 92 | { 93 | name: "objects/complex", 94 | node: newTestNodeWithKids(map[string]*Node{ 95 | "foo": NewNode("", "bar"), 96 | "foo1": NewNode("", 2), 97 | "foo2": NewNode("", true), 98 | "foo3": NewNode("", 3.42), 99 | }), 100 | }, 101 | { 102 | name: "objects/nested", 103 | node: newTestNodeWithKids(map[string]*Node{ 104 | "dinosaurs": newTestNodeWithKids(map[string]*Node{ 105 | "bruhathkayosaurus": newTestNodeWithKids(map[string]*Node{ 106 | "appeared": NewNode("", -70000000), 107 | "height": NewNode("", 25), 108 | "length": NewNode("", 44), 109 | "order": NewNode("", "saurischia"), 110 | "vanished": NewNode("", -70000000), 111 | "weight": NewNode("", 135000), 112 | }), 113 | "lambeosaurus": newTestNodeWithKids(map[string]*Node{ 114 | "appeared": NewNode("", -76000000), 115 | "height": NewNode("", 2.1), 116 | "length": NewNode("", 12.5), 117 | "order": NewNode("", "ornithischia"), 118 | "vanished": NewNode("", -75000000), 119 | "weight": NewNode("", 5000), 120 | }), 121 | }), 122 | "scores": newTestNodeWithKids(map[string]*Node{ 123 | "bruhathkayosaurus": NewNode("", 55), 124 | "lambeosaurus": NewNode("", 21), 125 | }), 126 | }), 127 | }, 128 | { 129 | name: "objects/with_arrays", 130 | node: newTestNodeWithKids(map[string]*Node{ 131 | "regular": NewNode("", "item"), 132 | "booleans": NewNode("", []interface{}{false, true}), 133 | "numbers": NewNode("", []interface{}{1, 2}), 134 | "decimals": NewNode("", []interface{}{1.1, 2.2}), 135 | "strings": NewNode("", []interface{}{"foo", "bar"}), 136 | }), 137 | }, 138 | } { 139 | data, err := ioutil.ReadFile("fixtures/" + test.name + ".json") 140 | require.NoError(t, err, test.name) 141 | 142 | var v interface{} 143 | require.NoError(t, json.Unmarshal(data, &v), test.name) 144 | 145 | n := NewNode("", v) 146 | assert.NoError(t, equalNodes(test.node, n), test.name) 147 | } 148 | } 149 | 150 | func TestObjectify(t *testing.T) { 151 | for _, test := range []struct { 152 | name string 153 | object interface{} 154 | }{ 155 | { 156 | name: "nil", 157 | }, 158 | { 159 | name: "string", 160 | object: "foo", 161 | }, 162 | { 163 | name: "number", 164 | object: 2, 165 | }, 166 | { 167 | name: "decimal", 168 | object: 2.2, 169 | }, 170 | { 171 | name: "boolean", 172 | object: false, 173 | }, 174 | { 175 | name: "arrays", 176 | object: []interface{}{"foo", 2, 2.2, false}, 177 | }, 178 | { 179 | name: "object", 180 | object: map[string]interface{}{ 181 | "one_fish": "two_fish", 182 | "red_fish": 2.2, 183 | "netflix_list": []interface{}{"Orange is the New Black", "House of Cards"}, 184 | "shopping_list": map[string]interface{}{ 185 | "publix": "milk", 186 | "walmart": "reese's pieces", 187 | }, 188 | }, 189 | }, 190 | } { 191 | node := NewNode("", test.object) 192 | assert.Equal(t, test.object, node.Objectify()) 193 | } 194 | } 195 | 196 | func TestChild(t *testing.T) { 197 | node := NewNode("", map[string]interface{}{ 198 | "one": map[string]interface{}{ 199 | "two": map[string]interface{}{ 200 | "three": true, 201 | }, 202 | }, 203 | }) 204 | 205 | one, ok := node.Child("one") 206 | require.True(t, ok) 207 | require.NotNil(t, one) 208 | 209 | two, ok := one.Child("two") 210 | require.True(t, ok) 211 | require.NotNil(t, two) 212 | 213 | three, ok := two.Child("three") 214 | require.True(t, ok) 215 | require.NotNil(t, three) 216 | 217 | threeNested, ok := node.Child("one/two/three") 218 | require.True(t, ok) 219 | assert.Equal(t, three, threeNested) 220 | 221 | one, ok = node.Child("nope") 222 | assert.False(t, ok) 223 | assert.Nil(t, one) 224 | } 225 | 226 | func TestMerge(t *testing.T) { 227 | base := &Node{Children: map[string]*Node{ 228 | "foo": NewNode("", "bar"), 229 | "notreplace": NewNode("", "yes"), 230 | }} 231 | 232 | newNode := &Node{Children: map[string]*Node{ 233 | "foo": NewNode("", "troll"), 234 | "new": NewNode("", "lala"), 235 | }} 236 | 237 | base.merge(newNode) 238 | 239 | expected := NewNode("", map[string]string{ 240 | "foo": "troll", 241 | "notreplace": "yes", 242 | "new": "lala", 243 | }) 244 | 245 | err := equalNodes(expected, base) 246 | assert.NoError(t, err) 247 | } 248 | 249 | func TestPrune(t *testing.T) { 250 | /* 251 | Children: 0 252 | Value: Non nil 253 | Parent: nil 254 | */ 255 | n := NewNode("", "foo") 256 | assert.Nil(t, n.prune()) 257 | 258 | /* 259 | Children: 0 260 | Value: Non nil 261 | Parent: Non nil 262 | */ 263 | n = NewNode("", "foo") 264 | n.Parent = NewNode("", 1) 265 | assert.Nil(t, n.prune()) 266 | 267 | /* 268 | Children: 0 269 | Value: nil 270 | Parent: Non nil 271 | */ 272 | n = &Node{} 273 | parent := newTestNodeWithKids(map[string]*Node{"foo": n}) 274 | parentFromPrune := n.prune() 275 | 276 | assert.NotNil(t, parentFromPrune) 277 | assert.Equal(t, parent, parentFromPrune) 278 | assert.Nil(t, n.Parent) 279 | assert.Nil(t, n.Children) 280 | 281 | /* 282 | Children: 1 283 | Value: nil 284 | Parent: Non nil 285 | */ 286 | n = newTestNodeWithKids(map[string]*Node{"c1": n}) 287 | parent = newTestNodeWithKids(map[string]*Node{"foo": n}) 288 | assert.Nil(t, n.prune()) 289 | 290 | /* 291 | Children: 1 292 | Value: nil 293 | Parent: nil 294 | */ 295 | n = newTestNodeWithKids(map[string]*Node{"c1": n}) 296 | assert.Nil(t, n.prune()) 297 | 298 | /* 299 | Children: 2 300 | Value: nil 301 | Parent: Non nil 302 | */ 303 | n = newTestNodeWithKids(map[string]*Node{ 304 | "c1": NewNode("", 1), 305 | "c2": NewNode("", 2), 306 | }) 307 | n.Parent = NewNode("", "hello!") 308 | assert.Nil(t, n.prune()) 309 | } 310 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/event_callback.go: -------------------------------------------------------------------------------- 1 | package firego 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sort" 7 | "strings" 8 | "time" 9 | 10 | "github.com/zabawaba99/firego/sync" 11 | ) 12 | 13 | // ChildEventFunc is the type of function that is called for every 14 | // new child added under a firebase reference. The snapshot argument 15 | // contains the data that was added. The previousChildKey argument 16 | // contains the key of the previous child that this function was called for. 17 | type ChildEventFunc func(snapshot DataSnapshot, previousChildKey string) 18 | 19 | // ChildAdded listens on the firebase instance and executes the callback 20 | // for every child that is added. 21 | // 22 | // You cannot set the same function twice on a Firebase reference, if you do 23 | // the first function will be overridden and you will not be able to close the 24 | // connection. 25 | func (fb *Firebase) ChildAdded(fn ChildEventFunc) error { 26 | return fb.addEventFunc(fn, fn.childAdded) 27 | } 28 | 29 | func (fn ChildEventFunc) childAdded(db *sync.Database, prevKey *string, notifications chan Event) error { 30 | for event := range notifications { 31 | if event.Type == EventTypeError { 32 | err, ok := event.Data.(error) 33 | if !ok { 34 | err = fmt.Errorf("Got error from event %#v", event) 35 | } 36 | return err 37 | } 38 | 39 | if event.Type != EventTypePut { 40 | continue 41 | } 42 | 43 | child := strings.Split(event.Path[1:], "/")[0] 44 | if event.Data == nil { 45 | db.Del(child) 46 | continue 47 | } 48 | 49 | if _, ok := db.Get("").Child(child); ok { 50 | // if the child isn't being added, forget it 51 | continue 52 | } 53 | 54 | m, ok := event.Data.(map[string]interface{}) 55 | if child == "" && ok { 56 | // if we were given a map at the root then we have 57 | // to send an event per child 58 | for _, k := range sortedKeys(m) { 59 | v := m[k] 60 | node := sync.NewNode(k, v) 61 | db.Add(k, node) 62 | fn(newSnapshot(node), *prevKey) 63 | *prevKey = k 64 | } 65 | continue 66 | } 67 | 68 | // we have a single event to process 69 | node := sync.NewNode(child, event.Data) 70 | db.Add(strings.Trim(child, "/"), node) 71 | 72 | fn(newSnapshot(node), *prevKey) 73 | *prevKey = child 74 | } 75 | return nil 76 | } 77 | 78 | // ChildChanged listens on the firebase instance and executes the callback 79 | // for every child that is changed. 80 | // 81 | // You cannot set the same function twice on a Firebase reference, if you do 82 | // the first function will be overridden and you will not be able to close the 83 | // connection. 84 | func (fb *Firebase) ChildChanged(fn ChildEventFunc) error { 85 | return fb.addEventFunc(fn, fn.childChanged) 86 | } 87 | 88 | func (fn ChildEventFunc) childChanged(db *sync.Database, prevKey *string, notifications chan Event) error { 89 | first, ok := <-notifications 90 | if !ok { 91 | return errors.New("channel closed") 92 | } 93 | 94 | db.Add("", sync.NewNode("", first.Data)) 95 | for event := range notifications { 96 | if event.Type == EventTypeError { 97 | err, ok := event.Data.(error) 98 | if !ok { 99 | err = fmt.Errorf("Got error from event %#v", event) 100 | } 101 | return err 102 | } 103 | 104 | path := strings.Trim(event.Path, "/") 105 | if event.Data == nil { 106 | db.Del(path) 107 | continue 108 | } 109 | 110 | child := strings.Split(path, "/")[0] 111 | node := sync.NewNode(child, event.Data) 112 | 113 | dbNode := db.Get("") 114 | if _, ok := dbNode.Child(child); child != "" && !ok { 115 | // if the child is new, ignore it. 116 | db.Add(path, node) 117 | continue 118 | } 119 | 120 | if m, ok := event.Data.(map[string]interface{}); child == "" && ok { 121 | // we've got children so send an event per child 122 | for _, k := range sortedKeys(m) { 123 | v := m[k] 124 | node := sync.NewNode(k, v) 125 | newPath := strings.TrimPrefix(child+"/"+k, "/") 126 | if _, ok := dbNode.Child(k); !ok { 127 | db.Add(newPath, node) 128 | continue 129 | } 130 | 131 | db.Update(newPath, node) 132 | fn(newSnapshot(node), *prevKey) 133 | *prevKey = k 134 | } 135 | continue 136 | } 137 | 138 | db.Update(path, node) 139 | fn(newSnapshot(db.Get(child)), *prevKey) 140 | *prevKey = child 141 | } 142 | return nil 143 | } 144 | 145 | // ChildRemoved listens on the firebase instance and executes the callback 146 | // for every child that is deleted. 147 | // 148 | // You cannot set the same function twice on a Firebase reference, if you do 149 | // the first function will be overridden and you will not be able to close the 150 | // connection. 151 | func (fb *Firebase) ChildRemoved(fn ChildEventFunc) error { 152 | return fb.addEventFunc(fn, fn.childRemoved) 153 | } 154 | 155 | func (fn ChildEventFunc) childRemoved(db *sync.Database, prevKey *string, notifications chan Event) error { 156 | first, ok := <-notifications 157 | if !ok { 158 | return errors.New("channel closed") 159 | } 160 | 161 | node := sync.NewNode("", first.Data) 162 | db.Add("", node) 163 | 164 | for event := range notifications { 165 | if event.Type == EventTypeError { 166 | err, ok := event.Data.(error) 167 | if !ok { 168 | err = fmt.Errorf("Got error from event %#v", event) 169 | } 170 | return err 171 | } 172 | 173 | path := strings.Trim(event.Path, "/") 174 | node := sync.NewNode(path, event.Data) 175 | 176 | if event.Type == EventTypePatch { 177 | db.Update(path, node) 178 | continue 179 | } 180 | 181 | if event.Data != nil { 182 | db.Add(path, node) 183 | continue 184 | } 185 | 186 | if path == "" { 187 | // if node that is being listened to is deleted, 188 | // an event should be triggered for every child 189 | children := db.Get("").Children 190 | orderedChildren := make([]string, len(children)) 191 | var i int 192 | for k := range children { 193 | orderedChildren[i] = k 194 | i++ 195 | } 196 | 197 | sort.Strings(orderedChildren) 198 | 199 | for _, k := range orderedChildren { 200 | node := db.Get(k) 201 | fn(newSnapshot(node), "") 202 | db.Del(k) 203 | } 204 | 205 | db.Del(path) 206 | continue 207 | } 208 | 209 | node = db.Get(path) 210 | fn(newSnapshot(node), "") 211 | db.Del(path) 212 | } 213 | return nil 214 | } 215 | 216 | type handleSSEFunc func(*sync.Database, *string, chan Event) error 217 | 218 | func (fb *Firebase) addEventFunc(fn ChildEventFunc, handleSSE handleSSEFunc) error { 219 | fb.eventMtx.Lock() 220 | defer fb.eventMtx.Unlock() 221 | 222 | stop := make(chan struct{}) 223 | key := fmt.Sprintf("%v", fn) 224 | if _, ok := fb.eventFuncs[key]; ok { 225 | return nil 226 | } 227 | 228 | fb.eventFuncs[key] = stop 229 | notifications, err := fb.watch(stop) 230 | if err != nil { 231 | return err 232 | } 233 | 234 | db := sync.NewDB() 235 | prevKey := new(string) 236 | var run func(notifications chan Event, backoff time.Duration) 237 | run = func(notifications chan Event, backoff time.Duration) { 238 | fb.eventMtx.Lock() 239 | if _, ok := fb.eventFuncs[key]; !ok { 240 | fb.eventMtx.Unlock() 241 | // the func has been removed 242 | return 243 | } 244 | fb.eventMtx.Unlock() 245 | 246 | if err := handleSSE(db, prevKey, notifications); err == nil { 247 | // we returned gracefully 248 | return 249 | } 250 | 251 | // give firebase some time 252 | backoff *= 2 253 | time.Sleep(backoff) 254 | 255 | // try and reconnect 256 | for notifications, err = fb.watch(stop); err != nil; time.Sleep(backoff) { 257 | fb.eventMtx.Lock() 258 | if _, ok := fb.eventFuncs[key]; !ok { 259 | fb.eventMtx.Unlock() 260 | // func has been removed 261 | return 262 | } 263 | fb.eventMtx.Unlock() 264 | } 265 | 266 | // give this another shot 267 | run(notifications, backoff) 268 | } 269 | 270 | go run(notifications, fb.watchHeartbeat) 271 | return nil 272 | } 273 | 274 | // RemoveEventFunc removes the given function from the firebase 275 | // reference. 276 | func (fb *Firebase) RemoveEventFunc(fn ChildEventFunc) { 277 | fb.eventMtx.Lock() 278 | defer fb.eventMtx.Unlock() 279 | 280 | key := fmt.Sprintf("%v", fn) 281 | stop, ok := fb.eventFuncs[key] 282 | if !ok { 283 | return 284 | } 285 | 286 | delete(fb.eventFuncs, key) 287 | close(stop) 288 | } 289 | 290 | func sortedKeys(m map[string]interface{}) []string { 291 | orderedKeys := make([]string, len(m)) 292 | var i int 293 | for k := range m { 294 | orderedKeys[i] = k 295 | i++ 296 | } 297 | 298 | sort.Strings(orderedKeys) 299 | return orderedKeys 300 | } 301 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/firebase.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package firego is a REST client for Firebase (https://firebase.com). 3 | */ 4 | package firego 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | "errors" 10 | "fmt" 11 | "io/ioutil" 12 | "net" 13 | "net/http" 14 | _url "net/url" 15 | "strings" 16 | "sync" 17 | "time" 18 | ) 19 | 20 | // TimeoutDuration is the length of time any request will have to establish 21 | // a connection and receive headers from Firebase before returning 22 | // an ErrTimeout error. 23 | var TimeoutDuration = 30 * time.Second 24 | 25 | var defaultRedirectLimit = 30 26 | 27 | // ErrTimeout is an error type is that is returned if a request 28 | // exceeds the TimeoutDuration configured. 29 | type ErrTimeout struct { 30 | error 31 | } 32 | 33 | // query parameter constants 34 | const ( 35 | authParam = "auth" 36 | shallowParam = "shallow" 37 | formatParam = "format" 38 | formatVal = "export" 39 | orderByParam = "orderBy" 40 | limitToFirstParam = "limitToFirst" 41 | limitToLastParam = "limitToLast" 42 | startAtParam = "startAt" 43 | endAtParam = "endAt" 44 | equalToParam = "equalTo" 45 | ) 46 | 47 | const defaultHeartbeat = 2 * time.Minute 48 | 49 | // Firebase represents a location in the cloud. 50 | type Firebase struct { 51 | url string 52 | params _url.Values 53 | client *http.Client 54 | clientTimeout time.Duration 55 | 56 | eventMtx sync.Mutex 57 | eventFuncs map[string]chan struct{} 58 | 59 | watchMtx sync.Mutex 60 | watching bool 61 | watchHeartbeat time.Duration 62 | stopWatching chan struct{} 63 | } 64 | 65 | // New creates a new Firebase reference, 66 | // if client is nil, http.DefaultClient is used. 67 | func New(url string, client *http.Client) *Firebase { 68 | fb := &Firebase{ 69 | url: sanitizeURL(url), 70 | params: _url.Values{}, 71 | clientTimeout: TimeoutDuration, 72 | stopWatching: make(chan struct{}), 73 | watchHeartbeat: defaultHeartbeat, 74 | eventFuncs: map[string]chan struct{}{}, 75 | } 76 | if client == nil { 77 | var tr *http.Transport 78 | tr = &http.Transport{ 79 | Dial: func(network, address string) (net.Conn, error) { 80 | start := time.Now() 81 | c, err := net.DialTimeout(network, address, fb.clientTimeout) 82 | tr.ResponseHeaderTimeout = fb.clientTimeout - time.Since(start) 83 | return c, err 84 | }, 85 | } 86 | 87 | client = &http.Client{ 88 | Transport: tr, 89 | CheckRedirect: redirectPreserveHeaders, 90 | } 91 | } 92 | 93 | fb.client = client 94 | return fb 95 | } 96 | 97 | // Auth sets the custom Firebase token used to authenticate to Firebase. 98 | func (fb *Firebase) Auth(token string) { 99 | fb.params.Set(authParam, token) 100 | } 101 | 102 | // Unauth removes the current token being used to authenticate to Firebase. 103 | func (fb *Firebase) Unauth() { 104 | fb.params.Del(authParam) 105 | } 106 | 107 | // Ref returns a copy of an existing Firebase reference with a new path. 108 | func (fb *Firebase) Ref(path string) (*Firebase, error) { 109 | newFB := fb.copy() 110 | parsedURL, err := _url.Parse(fb.url) 111 | if err != nil { 112 | return newFB, err 113 | } 114 | newFB.url = parsedURL.Scheme + "://" + parsedURL.Host + "/" + strings.Trim(path, "/") 115 | return newFB, nil 116 | } 117 | 118 | // SetURL changes the url for a firebase reference. 119 | func (fb *Firebase) SetURL(url string) { 120 | fb.url = sanitizeURL(url) 121 | } 122 | 123 | // URL returns firebase reference URL 124 | func (fb *Firebase) URL() string { 125 | return fb.url 126 | } 127 | 128 | // Push creates a reference to an auto-generated child location. 129 | func (fb *Firebase) Push(v interface{}) (*Firebase, error) { 130 | bytes, err := json.Marshal(v) 131 | if err != nil { 132 | return nil, err 133 | } 134 | _, bytes, err = fb.doRequest("POST", bytes) 135 | if err != nil { 136 | return nil, err 137 | } 138 | var m map[string]string 139 | if err := json.Unmarshal(bytes, &m); err != nil { 140 | return nil, err 141 | } 142 | newRef := fb.copy() 143 | newRef.url = fb.url + "/" + m["name"] 144 | return newRef, err 145 | } 146 | 147 | // Remove the Firebase reference from the cloud. 148 | func (fb *Firebase) Remove() error { 149 | _, _, err := fb.doRequest("DELETE", nil) 150 | if err != nil { 151 | return err 152 | } 153 | return nil 154 | } 155 | 156 | // Set the value of the Firebase reference. 157 | func (fb *Firebase) Set(v interface{}) error { 158 | bytes, err := json.Marshal(v) 159 | if err != nil { 160 | return err 161 | } 162 | _, _, err = fb.doRequest("PUT", bytes) 163 | return err 164 | } 165 | 166 | // Update the specific child with the given value. 167 | func (fb *Firebase) Update(v interface{}) error { 168 | bytes, err := json.Marshal(v) 169 | if err != nil { 170 | return err 171 | } 172 | _, _, err = fb.doRequest("PATCH", bytes) 173 | return err 174 | } 175 | 176 | // Value gets the value of the Firebase reference. 177 | func (fb *Firebase) Value(v interface{}) error { 178 | _, bytes, err := fb.doRequest("GET", nil) 179 | if err != nil { 180 | return err 181 | } 182 | return json.Unmarshal(bytes, v) 183 | } 184 | 185 | // String returns the string representation of the 186 | // Firebase reference. 187 | func (fb *Firebase) String() string { 188 | path := fb.url + "/.json" 189 | 190 | if len(fb.params) > 0 { 191 | path += "?" + fb.params.Encode() 192 | } 193 | return path 194 | } 195 | 196 | // Child creates a new Firebase reference for the requested 197 | // child with the same configuration as the parent. 198 | func (fb *Firebase) Child(child string) *Firebase { 199 | c := fb.copy() 200 | c.url = c.url + "/" + child 201 | return c 202 | } 203 | 204 | func (fb *Firebase) copy() *Firebase { 205 | c := &Firebase{ 206 | url: fb.url, 207 | params: _url.Values{}, 208 | client: fb.client, 209 | clientTimeout: fb.clientTimeout, 210 | stopWatching: make(chan struct{}), 211 | watchHeartbeat: defaultHeartbeat, 212 | eventFuncs: map[string]chan struct{}{}, 213 | } 214 | 215 | // making sure to manually copy the map items into a new 216 | // map to avoid modifying the map reference. 217 | for k, v := range fb.params { 218 | c.params[k] = v 219 | } 220 | return c 221 | } 222 | 223 | func sanitizeURL(url string) string { 224 | if !strings.HasPrefix(url, "https://") && !strings.HasPrefix(url, "http://") { 225 | url = "https://" + url 226 | } 227 | 228 | if strings.HasSuffix(url, "/") { 229 | url = url[:len(url)-1] 230 | } 231 | 232 | return url 233 | } 234 | 235 | // Preserve headers on redirect. 236 | // 237 | // Reference https://github.com/golang/go/issues/4800 238 | func redirectPreserveHeaders(req *http.Request, via []*http.Request) error { 239 | if len(via) == 0 { 240 | // No redirects 241 | return nil 242 | } 243 | 244 | if len(via) > defaultRedirectLimit { 245 | return fmt.Errorf("%d consecutive requests(redirects)", len(via)) 246 | } 247 | 248 | // mutate the subsequent redirect requests with the first Header 249 | for key, val := range via[0].Header { 250 | req.Header[key] = val 251 | } 252 | return nil 253 | } 254 | 255 | func withHeader(key, value string) func(*http.Request) { 256 | return func(req *http.Request) { 257 | req.Header.Add(key, value) 258 | } 259 | } 260 | 261 | func (fb *Firebase) doRequest(method string, body []byte, options ...func(*http.Request)) (http.Header, []byte, error) { 262 | req, err := http.NewRequest(method, fb.String(), bytes.NewReader(body)) 263 | if err != nil { 264 | return nil, nil, err 265 | } 266 | 267 | for _, opt := range options { 268 | opt(req) 269 | } 270 | 271 | resp, err := fb.client.Do(req) 272 | switch err := err.(type) { 273 | default: 274 | return nil, nil, err 275 | case nil: 276 | // carry on 277 | 278 | case *_url.Error: 279 | // `http.Client.Do` will return a `url.Error` that wraps a `net.Error` 280 | // when exceeding it's `Transport`'s `ResponseHeadersTimeout` 281 | e1, ok := err.Err.(net.Error) 282 | if ok && e1.Timeout() { 283 | return nil, nil, ErrTimeout{err} 284 | } 285 | 286 | return nil, nil, err 287 | 288 | case net.Error: 289 | // `http.Client.Do` will return a `net.Error` directly when Dial times 290 | // out, or when the Client's RoundTripper otherwise returns an err 291 | if err.Timeout() { 292 | return nil, nil, ErrTimeout{err} 293 | } 294 | 295 | return nil, nil, err 296 | } 297 | 298 | defer resp.Body.Close() 299 | respBody, err := ioutil.ReadAll(resp.Body) 300 | if err != nil { 301 | return nil, nil, err 302 | } 303 | if resp.StatusCode/200 != 1 { 304 | return resp.Header, respBody, errors.New(string(respBody)) 305 | } 306 | return resp.Header, respBody, nil 307 | } 308 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/negroni/translations/README_de_de.md: -------------------------------------------------------------------------------- 1 | # Negroni [![GoDoc](https://godoc.org/github.com/codegangsta/negroni?status.svg)](http://godoc.org/github.com/codegangsta/negroni) [![wercker status](https://app.wercker.com/status/13688a4a94b82d84a0b8d038c4965b61/s "wercker status")](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61) 2 | 3 | Negroni ist ein Ansatz für eine idiomatische Middleware in Go. Sie ist klein, nicht-intrusiv und unterstützt die Nutzung von `net/http` Handlern. 4 | 5 | Wenn Dir die Idee hinter [Martini](http://github.com/go-martini/martini) gefällt, aber Du denkst, es stecke zu viel Magie darin, dann ist Negroni eine passende Alternative. 6 | 7 | ## Wo fange ich an? 8 | 9 | Nachdem Du Go installiert und den [GOPATH](http://golang.org/doc/code.html#GOPATH) eingerichtet hast, erstelle eine `.go`-Datei. Nennen wir sie `server.go`. 10 | 11 | ~~~ go 12 | package main 13 | 14 | import ( 15 | "github.com/codegangsta/negroni" 16 | "net/http" 17 | "fmt" 18 | ) 19 | 20 | func main() { 21 | mux := http.NewServeMux() 22 | mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 23 | fmt.Fprintf(w, "Willkommen auf der Homepage!") 24 | }) 25 | 26 | n := negroni.Classic() 27 | n.UseHandler(mux) 28 | n.Run(":3000") 29 | } 30 | ~~~ 31 | 32 | Installiere nun das Negroni Package (**go 1.1** und höher werden vorausgesetzt): 33 | ~~~ 34 | go get github.com/codegangsta/negroni 35 | ~~~ 36 | 37 | Dann starte Deinen Server: 38 | ~~~ 39 | go run server.go 40 | ~~~ 41 | 42 | Nun läuft ein `net/http`-Webserver von Go unter `localhost:3000`. 43 | 44 | ## Hilfe benötigt? 45 | Wenn Du eine Frage hast oder Dir ein bestimmte Funktion wünscht, nutze die [Mailing Liste](https://groups.google.com/forum/#!forum/negroni-users). Issues auf Github werden ausschließlich für Bug Reports und Pull Requests genutzt. 46 | 47 | ## Ist Negroni ein Framework? 48 | Negroni ist **kein** Framework. Es ist eine Bibliothek, geschaffen, um kompatibel mit `net/http` zu sein. 49 | 50 | ## Routing? 51 | Negroni ist BYOR (Bring your own Router - Nutze Deinen eigenen Router). Die Go-Community verfügt bereits über eine Vielzahl von großartigen Routern. Negroni versucht möglichst alle zu unterstützen, indem es `net/http` vollständig unterstützt. Beispielsweise sieht eine Implementation mit [Gorilla Mux](http://github.com/gorilla/mux) folgendermaßen aus: 52 | 53 | ~~~ go 54 | router := mux.NewRouter() 55 | router.HandleFunc("/", HomeHandler) 56 | 57 | n := negroni.New(Middleware1, Middleware2) 58 | // Oder nutze eine Middleware mit der Use()-Funktion 59 | n.Use(Middleware3) 60 | // Der Router kommt als letztes 61 | n.UseHandler(router) 62 | 63 | n.Run(":3000") 64 | ~~~ 65 | 66 | ## `negroni.Classic()` 67 | `negroni.Classic()` stellt einige Standard-Middlewares bereit, die für die meisten Anwendungen von Nutzen ist: 68 | 69 | * `negroni.Recovery` - Middleware für Panic Recovery . 70 | * `negroni.Logging` - Anfrage/Rückmeldungs-Logging-Middleware. 71 | * `negroni.Static` - Ausliefern von statischen Dateien unter dem "public" Verzeichnis. 72 | 73 | Dies macht es wirklich einfach, mit den nützlichen Funktionen von Negroni zu starten. 74 | 75 | ## Handlers 76 | Negroni stellt einen bidirektionalen Middleware-Flow bereit. Dies wird durch das `negroni.Handler`-Interface erreicht: 77 | 78 | ~~~ go 79 | type Handler interface { 80 | ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) 81 | } 82 | ~~~ 83 | 84 | Wenn eine Middleware nicht bereits den ResponseWriter genutzt hat, sollte sie die nächste `http.HandlerFunc` in der Verkettung von Middlewares aufrufen und diese ausführen. Das kann von großem Nutzen sein: 85 | 86 | ~~~ go 87 | func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 88 | // Mache etwas vor dem Aufruf 89 | next(rw, r) 90 | // Mache etwas nach dem Aufruf 91 | } 92 | ~~~ 93 | 94 | Und Du kannst eine Middleware durch die `Use`-Funktion der Verkettung von Middlewares zuordnen. 95 | 96 | ~~~ go 97 | n := negroni.New() 98 | n.Use(negroni.HandlerFunc(MyMiddleware)) 99 | ~~~ 100 | 101 | Stattdessen kannst Du auch herkömmliche `http.Handler` zuordnen: 102 | 103 | ~~~ go 104 | n := negroni.New() 105 | 106 | mux := http.NewServeMux() 107 | // Ordne Deine Routen zu 108 | 109 | n.UseHandler(mux) 110 | 111 | n.Run(":3000") 112 | ~~~ 113 | 114 | ## `Run()` 115 | Negroni hat eine nützliche Funktion namens `Run`. `Run` übernimmt eine Zeichenkette `addr` ähnlich wie [http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe). 116 | 117 | ~~~ go 118 | n := negroni.Classic() 119 | // ... 120 | log.Fatal(http.ListenAndServe(":8080", n)) 121 | ~~~ 122 | 123 | ## Routenspezifische Middleware 124 | Wenn Du eine Gruppe von Routen hast, welche alle die gleiche Middleware ausführen müssen, kannst Du einfach eine neue Negroni-Instanz erstellen und sie als Route-Handler nutzen: 125 | 126 | ~~~ go 127 | router := mux.NewRouter() 128 | adminRoutes := mux.NewRouter() 129 | // Füge die Admin-Routen hier hinzu 130 | 131 | // Erstelle eine neue Negroni-Instanz für die Admin-Middleware 132 | router.Handle("/admin", negroni.New( 133 | Middleware1, 134 | Middleware2, 135 | negroni.Wrap(adminRoutes), 136 | )) 137 | ~~~ 138 | 139 | ## Middlewares von Dritten 140 | 141 | Hier ist eine aktuelle Liste von Middlewares, die kompatible mit Negroni sind. Tue Dir keinen Zwang an, Dich einzutragen, wenn Du selbst eine Middleware programmiert hast: 142 | 143 | 144 | | Middleware | Autor | Beschreibung | 145 | | -----------|--------|-------------| 146 | | [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Sichere Authentifikation für Endpunkte einer REST API | 147 | | [Graceful](https://github.com/stretchr/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown | 148 | | [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Eine Middleware mit ein paar nützlichen Sicherheitseinstellungen | 149 | | [JWT Middleware](https://github.com/auth0/go-jwt-middleware) | [Auth0](https://github.com/auth0) | Eine Middleware die nach JWTs im `Authorization`-Feld des Header sucht und sie dekodiert.| 150 | | [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data Binding von HTTP-Anfragen in Structs | 151 | | [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-basierender Logger | 152 | | [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Rendere JSON, XML und HTML Vorlagen | 153 | | [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic Agent für die Go-Echtzeitumgebung | 154 | | [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | Kompression von HTTP-Rückmeldungen via GZIP | 155 | | [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 Middleware | 156 | | [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management | 157 | | [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, Benutzer und Berechtigungen | 158 | | [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generiere TinySVG, HTML und CSS spontan | 159 | | [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) Unterstützung | 160 | | [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Eine Middleware die zufällige X-Request-Id-Header jedem Request anfügt | 161 | | [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC-basierte Middleware zur Authentifikation | 162 | | [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Speichere wichtige Informationen über Deine Webanwendung (Reaktionszeit, etc.) | 163 | 164 | ## Beispiele 165 | [Alexander Rødseth](https://github.com/xyproto) programmierte [mooseware](https://github.com/xyproto/mooseware), ein Grundgerüst zum Erstellen von Negroni Middleware-Handerln. 166 | 167 | ## Aktualisieren in Echtzeit? 168 | [gin](https://github.com/codegangsta/gin) und [fresh](https://github.com/pilu/fresh) aktualisieren Deine Negroni-Anwendung automatisch. 169 | 170 | ## Unverzichbare Informationen für Go- & Negronineulinge 171 | 172 | * [Nutze einen Kontext zum Übertragen von Middlewareinformationen an Handler (Englisch)](http://elithrar.github.io/article/map-string-interface/) 173 | * [Middlewares verstehen (Englisch)](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters) 174 | 175 | ## Über das Projekt 176 | 177 | Negroni wurde obsseziv von Niemand gerigeren als dem [Code Gangsta](http://codegangsta.io/) entwickelt. 178 | -------------------------------------------------------------------------------- /vendor/github.com/zabawaba99/firego/internal/firetest/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package firetest provides utilities for Firebase testing 3 | 4 | */ 5 | package firetest 6 | 7 | import ( 8 | "crypto/hmac" 9 | "crypto/sha256" 10 | "encoding/base64" 11 | "encoding/json" 12 | "fmt" 13 | "io" 14 | "io/ioutil" 15 | "log" 16 | "net" 17 | "net/http" 18 | "strings" 19 | "sync/atomic" 20 | "time" 21 | ) 22 | 23 | var ( 24 | missingJSONExtension = []byte("append .json to your request URI to use the REST API") 25 | missingBody = []byte(`{"error":"Error: No data supplied."}`) 26 | invalidJSON = []byte(`{"error":"Invalid data; couldn't parse JSON object, array, or value. Perhaps you're using invalid characters in your key names."}`) 27 | invalidAuth = []byte(`{"error" : "Could not parse auth token."}`) 28 | ) 29 | 30 | // Firetest is a Firebase server implementation 31 | type Firetest struct { 32 | // URL of form http://ipaddr:port with no trailing slash 33 | URL string 34 | // Secret used to authenticate with server 35 | Secret string 36 | 37 | listener net.Listener 38 | db *notifyDB 39 | 40 | requireAuth *int32 41 | } 42 | 43 | // New creates a new Firetest server 44 | func New() *Firetest { 45 | secret := []byte(fmt.Sprint(time.Now().UnixNano())) 46 | return &Firetest{ 47 | db: newNotifyDB(), 48 | Secret: base64.URLEncoding.EncodeToString(secret), 49 | requireAuth: new(int32), 50 | } 51 | } 52 | 53 | // Start starts the server 54 | func (ft *Firetest) Start() { 55 | l, err := net.Listen("tcp", "127.0.0.1:0") 56 | if err != nil { 57 | if l, err = net.Listen("tcp6", "[::1]:0"); err != nil { 58 | panic(fmt.Errorf("failed to listen on a port: %v", err)) 59 | } 60 | } 61 | ft.listener = l 62 | 63 | s := http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 64 | ft.serveHTTP(w, req) 65 | })} 66 | go func() { 67 | if err := s.Serve(l); err != nil { 68 | log.Printf("error serving: %s", err) 69 | } 70 | 71 | ft.Close() 72 | }() 73 | ft.URL = "http://" + ft.listener.Addr().String() 74 | } 75 | 76 | // Close closes the server 77 | func (ft *Firetest) Close() { 78 | if ft.listener != nil { 79 | ft.listener.Close() 80 | } 81 | } 82 | 83 | func (ft *Firetest) serveHTTP(w http.ResponseWriter, req *http.Request) { 84 | if !strings.HasSuffix(req.URL.Path, ".json") { 85 | w.WriteHeader(http.StatusForbidden) 86 | w.Write([]byte(missingJSONExtension)) 87 | return 88 | } 89 | 90 | if atomic.LoadInt32(ft.requireAuth) == 1 { 91 | var authenticated bool 92 | authHeader := req.URL.Query().Get("auth") 93 | switch { 94 | case strings.Contains(authHeader, "."): 95 | authenticated = ft.validJWT(authHeader) 96 | default: 97 | authenticated = authHeader == ft.Secret 98 | } 99 | 100 | if !authenticated { 101 | w.WriteHeader(http.StatusUnauthorized) 102 | w.Write(invalidAuth) 103 | return 104 | } 105 | } 106 | 107 | switch req.Method { 108 | case "PUT": 109 | ft.set(w, req) 110 | case "PATCH": 111 | ft.update(w, req) 112 | case "POST": 113 | ft.create(w, req) 114 | case "GET": 115 | switch req.Header.Get("Accept") { 116 | case "text/event-stream": 117 | ft.sse(w, req) 118 | default: 119 | ft.get(w, req) 120 | } 121 | case "DELETE": 122 | ft.del(w, req) 123 | default: 124 | w.WriteHeader(http.StatusMethodNotAllowed) 125 | log.Println("not implemented yet") 126 | } 127 | } 128 | 129 | func decodeSegment(seg string) ([]byte, error) { 130 | if l := len(seg) % 4; l > 0 { 131 | seg += strings.Repeat("=", 4-l) 132 | } 133 | 134 | return base64.URLEncoding.DecodeString(seg) 135 | } 136 | 137 | func (ft *Firetest) validJWT(val string) bool { 138 | parts := strings.Split(val, ".") 139 | if len(parts) != 3 { 140 | return false 141 | } 142 | 143 | // validate header 144 | hb, err := decodeSegment(parts[0]) 145 | if err != nil { 146 | log.Println("error decoding header", err) 147 | return false 148 | } 149 | var header map[string]string 150 | if err := json.Unmarshal(hb, &header); err != nil { 151 | log.Println("error unmarshaling header", err) 152 | return false 153 | } 154 | if header["alg"] != "HS256" || header["typ"] != "JWT" { 155 | return false 156 | } 157 | 158 | // validate claim 159 | cb, err := decodeSegment(parts[1]) 160 | if err != nil { 161 | log.Println("error decoding claim", err) 162 | return false 163 | } 164 | var claim map[string]interface{} 165 | if err := json.Unmarshal(cb, &claim); err != nil { 166 | log.Println("error unmarshaling claim", err) 167 | return false 168 | } 169 | if e, ok := claim["exp"]; ok { 170 | // make sure not expired 171 | exp, ok := e.(float64) 172 | if !ok { 173 | log.Println("expiration not a number") 174 | return false 175 | } 176 | if int64(exp) < time.Now().Unix() { 177 | log.Println("token expired") 178 | return false 179 | } 180 | } 181 | // ensure uid present 182 | data, ok := claim["d"] 183 | if !ok { 184 | log.Println("missing data in claim") 185 | return false 186 | } 187 | 188 | d, ok := data.(map[string]interface{}) 189 | if !ok { 190 | log.Println("claim['data'] is not map") 191 | return false 192 | } 193 | 194 | if _, ok := d["uid"]; !ok { 195 | log.Println("claim['data'] missing uid") 196 | return false 197 | } 198 | 199 | if sig, err := decodeSegment(parts[2]); err == nil { 200 | hasher := hmac.New(sha256.New, []byte(ft.Secret)) 201 | signedString := strings.Join(parts[:2], ".") 202 | hasher.Write([]byte(signedString)) 203 | 204 | if !hmac.Equal(sig, hasher.Sum(nil)) { 205 | log.Println("invalid jwt signature") 206 | return false 207 | } 208 | } 209 | 210 | return true 211 | } 212 | 213 | func (ft *Firetest) set(w http.ResponseWriter, req *http.Request) { 214 | body, v, ok := unmarshal(w, req.Body) 215 | if !ok { 216 | return 217 | } 218 | 219 | ft.Set(req.URL.Path, v) 220 | w.Write(body) 221 | } 222 | 223 | func (ft *Firetest) update(w http.ResponseWriter, req *http.Request) { 224 | body, v, ok := unmarshal(w, req.Body) 225 | if !ok { 226 | return 227 | } 228 | ft.Update(req.URL.Path, v) 229 | w.Write(body) 230 | } 231 | 232 | func (ft *Firetest) create(w http.ResponseWriter, req *http.Request) { 233 | _, v, ok := unmarshal(w, req.Body) 234 | if !ok { 235 | return 236 | } 237 | 238 | name := ft.Create(req.URL.Path, v) 239 | rtn := map[string]string{"name": name} 240 | if err := json.NewEncoder(w).Encode(rtn); err != nil { 241 | log.Printf("Error encoding json: %s", err) 242 | w.WriteHeader(http.StatusInternalServerError) 243 | } 244 | } 245 | 246 | func (ft *Firetest) del(w http.ResponseWriter, req *http.Request) { 247 | ft.Delete(req.URL.Path) 248 | } 249 | 250 | func (ft *Firetest) get(w http.ResponseWriter, req *http.Request) { 251 | w.Header().Add("Content-Type", "application/json") 252 | 253 | v := ft.Get(req.URL.Path) 254 | if err := json.NewEncoder(w).Encode(v); err != nil { 255 | log.Printf("Error encoding json: %s", err) 256 | w.WriteHeader(http.StatusInternalServerError) 257 | } 258 | } 259 | 260 | func (ft *Firetest) sse(w http.ResponseWriter, req *http.Request) { 261 | f, ok := w.(http.Flusher) 262 | if !ok { 263 | http.Error(w, "Streaming is not supported", http.StatusInternalServerError) 264 | return 265 | } 266 | 267 | w.Header().Set("Content-Type", "text/event-stream") 268 | 269 | path := sanitizePath(req.URL.Path) 270 | c := ft.db.watch(path) 271 | defer ft.db.stopWatching(path, c) 272 | 273 | d := eventData{Path: path, Data: ft.db.get(path)} 274 | s, err := json.Marshal(d) 275 | if err != nil { 276 | fmt.Printf("Error marshaling node %s\n", err) 277 | } 278 | fmt.Fprintf(w, "event: put\ndata: %s\n\n", s) 279 | f.Flush() 280 | 281 | httpCloser := w.(http.CloseNotifier).CloseNotify() 282 | for { 283 | select { 284 | case <-httpCloser: 285 | return 286 | case <-time.After(30 * time.Second): 287 | fmt.Fprintf(w, "event: keep-alive\ndata: null\n\n") 288 | f.Flush() 289 | continue 290 | case n, ok := <-c: 291 | if !ok { 292 | return 293 | } 294 | 295 | s, err := json.Marshal(n.Data) 296 | if err != nil { 297 | fmt.Printf("Error marshaling node %s\n", err) 298 | continue 299 | } 300 | 301 | fmt.Fprintf(w, "event: %s\ndata: %s\n\n", n.Name, s) 302 | f.Flush() 303 | } 304 | } 305 | } 306 | 307 | func sanitizePath(p string) string { 308 | // remove slashes from the front and back 309 | // /foo/.json -> foo/.json 310 | s := strings.Trim(p, "/") 311 | 312 | // remove .json extension 313 | // foo/.json -> foo/ 314 | s = strings.TrimSuffix(s, ".json") 315 | 316 | // trim an potential trailing slashes 317 | // foo/ -> foo 318 | return strings.TrimSuffix(s, "/") 319 | } 320 | 321 | func unmarshal(w http.ResponseWriter, r io.Reader) ([]byte, interface{}, bool) { 322 | body, err := ioutil.ReadAll(r) 323 | if err != nil || len(body) == 0 { 324 | w.WriteHeader(http.StatusBadRequest) 325 | w.Write(missingBody) 326 | return nil, nil, false 327 | } 328 | 329 | var v interface{} 330 | if err := json.Unmarshal(body, &v); err != nil { 331 | w.WriteHeader(http.StatusBadRequest) 332 | w.Write(invalidJSON) 333 | return nil, nil, false 334 | } 335 | return body, v, true 336 | } 337 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package mux implements a request router and dispatcher. 7 | 8 | The name mux stands for "HTTP request multiplexer". Like the standard 9 | http.ServeMux, mux.Router matches incoming requests against a list of 10 | registered routes and calls a handler for the route that matches the URL 11 | or other conditions. The main features are: 12 | 13 | * Requests can be matched based on URL host, path, path prefix, schemes, 14 | header and query values, HTTP methods or using custom matchers. 15 | * URL hosts and paths can have variables with an optional regular 16 | expression. 17 | * Registered URLs can be built, or "reversed", which helps maintaining 18 | references to resources. 19 | * Routes can be used as subrouters: nested routes are only tested if the 20 | parent route matches. This is useful to define groups of routes that 21 | share common conditions like a host, a path prefix or other repeated 22 | attributes. As a bonus, this optimizes request matching. 23 | * It implements the http.Handler interface so it is compatible with the 24 | standard http.ServeMux. 25 | 26 | Let's start registering a couple of URL paths and handlers: 27 | 28 | func main() { 29 | r := mux.NewRouter() 30 | r.HandleFunc("/", HomeHandler) 31 | r.HandleFunc("/products", ProductsHandler) 32 | r.HandleFunc("/articles", ArticlesHandler) 33 | http.Handle("/", r) 34 | } 35 | 36 | Here we register three routes mapping URL paths to handlers. This is 37 | equivalent to how http.HandleFunc() works: if an incoming request URL matches 38 | one of the paths, the corresponding handler is called passing 39 | (http.ResponseWriter, *http.Request) as parameters. 40 | 41 | Paths can have variables. They are defined using the format {name} or 42 | {name:pattern}. If a regular expression pattern is not defined, the matched 43 | variable will be anything until the next slash. For example: 44 | 45 | r := mux.NewRouter() 46 | r.HandleFunc("/products/{key}", ProductHandler) 47 | r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) 48 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 49 | 50 | Groups can be used inside patterns, as long as they are non-capturing (?:re). For example: 51 | 52 | r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler) 53 | 54 | The names are used to create a map of route variables which can be retrieved 55 | calling mux.Vars(): 56 | 57 | vars := mux.Vars(request) 58 | category := vars["category"] 59 | 60 | Note that if any capturing groups are present, mux will panic() during parsing. To prevent 61 | this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to 62 | "/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably 63 | when capturing groups were present. 64 | 65 | And this is all you need to know about the basic usage. More advanced options 66 | are explained below. 67 | 68 | Routes can also be restricted to a domain or subdomain. Just define a host 69 | pattern to be matched. They can also have variables: 70 | 71 | r := mux.NewRouter() 72 | // Only matches if domain is "www.example.com". 73 | r.Host("www.example.com") 74 | // Matches a dynamic subdomain. 75 | r.Host("{subdomain:[a-z]+}.domain.com") 76 | 77 | There are several other matchers that can be added. To match path prefixes: 78 | 79 | r.PathPrefix("/products/") 80 | 81 | ...or HTTP methods: 82 | 83 | r.Methods("GET", "POST") 84 | 85 | ...or URL schemes: 86 | 87 | r.Schemes("https") 88 | 89 | ...or header values: 90 | 91 | r.Headers("X-Requested-With", "XMLHttpRequest") 92 | 93 | ...or query values: 94 | 95 | r.Queries("key", "value") 96 | 97 | ...or to use a custom matcher function: 98 | 99 | r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { 100 | return r.ProtoMajor == 0 101 | }) 102 | 103 | ...and finally, it is possible to combine several matchers in a single route: 104 | 105 | r.HandleFunc("/products", ProductsHandler). 106 | Host("www.example.com"). 107 | Methods("GET"). 108 | Schemes("http") 109 | 110 | Setting the same matching conditions again and again can be boring, so we have 111 | a way to group several routes that share the same requirements. 112 | We call it "subrouting". 113 | 114 | For example, let's say we have several URLs that should only match when the 115 | host is "www.example.com". Create a route for that host and get a "subrouter" 116 | from it: 117 | 118 | r := mux.NewRouter() 119 | s := r.Host("www.example.com").Subrouter() 120 | 121 | Then register routes in the subrouter: 122 | 123 | s.HandleFunc("/products/", ProductsHandler) 124 | s.HandleFunc("/products/{key}", ProductHandler) 125 | s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) 126 | 127 | The three URL paths we registered above will only be tested if the domain is 128 | "www.example.com", because the subrouter is tested first. This is not 129 | only convenient, but also optimizes request matching. You can create 130 | subrouters combining any attribute matchers accepted by a route. 131 | 132 | Subrouters can be used to create domain or path "namespaces": you define 133 | subrouters in a central place and then parts of the app can register its 134 | paths relatively to a given subrouter. 135 | 136 | There's one more thing about subroutes. When a subrouter has a path prefix, 137 | the inner routes use it as base for their paths: 138 | 139 | r := mux.NewRouter() 140 | s := r.PathPrefix("/products").Subrouter() 141 | // "/products/" 142 | s.HandleFunc("/", ProductsHandler) 143 | // "/products/{key}/" 144 | s.HandleFunc("/{key}/", ProductHandler) 145 | // "/products/{key}/details" 146 | s.HandleFunc("/{key}/details", ProductDetailsHandler) 147 | 148 | Note that the path provided to PathPrefix() represents a "wildcard": calling 149 | PathPrefix("/static/").Handler(...) means that the handler will be passed any 150 | request that matches "/static/*". This makes it easy to serve static files with mux: 151 | 152 | func main() { 153 | var dir string 154 | 155 | flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") 156 | flag.Parse() 157 | r := mux.NewRouter() 158 | 159 | // This will serve files under http://localhost:8000/static/ 160 | r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) 161 | 162 | srv := &http.Server{ 163 | Handler: r, 164 | Addr: "127.0.0.1:8000", 165 | // Good practice: enforce timeouts for servers you create! 166 | WriteTimeout: 15 * time.Second, 167 | ReadTimeout: 15 * time.Second, 168 | } 169 | 170 | log.Fatal(srv.ListenAndServe()) 171 | } 172 | 173 | Now let's see how to build registered URLs. 174 | 175 | Routes can be named. All routes that define a name can have their URLs built, 176 | or "reversed". We define a name calling Name() on a route. For example: 177 | 178 | r := mux.NewRouter() 179 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 180 | Name("article") 181 | 182 | To build a URL, get the route and call the URL() method, passing a sequence of 183 | key/value pairs for the route variables. For the previous route, we would do: 184 | 185 | url, err := r.Get("article").URL("category", "technology", "id", "42") 186 | 187 | ...and the result will be a url.URL with the following path: 188 | 189 | "/articles/technology/42" 190 | 191 | This also works for host variables: 192 | 193 | r := mux.NewRouter() 194 | r.Host("{subdomain}.domain.com"). 195 | Path("/articles/{category}/{id:[0-9]+}"). 196 | HandlerFunc(ArticleHandler). 197 | Name("article") 198 | 199 | // url.String() will be "http://news.domain.com/articles/technology/42" 200 | url, err := r.Get("article").URL("subdomain", "news", 201 | "category", "technology", 202 | "id", "42") 203 | 204 | All variables defined in the route are required, and their values must 205 | conform to the corresponding patterns. These requirements guarantee that a 206 | generated URL will always match a registered route -- the only exception is 207 | for explicitly defined "build-only" routes which never match. 208 | 209 | Regex support also exists for matching Headers within a route. For example, we could do: 210 | 211 | r.HeadersRegexp("Content-Type", "application/(text|json)") 212 | 213 | ...and the route will match both requests with a Content-Type of `application/json` as well as 214 | `application/text` 215 | 216 | There's also a way to build only the URL host or path for a route: 217 | use the methods URLHost() or URLPath() instead. For the previous route, 218 | we would do: 219 | 220 | // "http://news.domain.com/" 221 | host, err := r.Get("article").URLHost("subdomain", "news") 222 | 223 | // "/articles/technology/42" 224 | path, err := r.Get("article").URLPath("category", "technology", "id", "42") 225 | 226 | And if you use subrouters, host and path defined separately can be built 227 | as well: 228 | 229 | r := mux.NewRouter() 230 | s := r.Host("{subdomain}.domain.com").Subrouter() 231 | s.Path("/articles/{category}/{id:[0-9]+}"). 232 | HandlerFunc(ArticleHandler). 233 | Name("article") 234 | 235 | // "http://news.domain.com/articles/technology/42" 236 | url, err := r.Get("article").URL("subdomain", "news", 237 | "category", "technology", 238 | "id", "42") 239 | */ 240 | package mux 241 | --------------------------------------------------------------------------------