mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	Godeps to lock in dependencies
This commit is contained in:
		
							
								
								
									
										117
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | |||||||
|  | { | ||||||
|  | 	"ImportPath": "github.com/hashicorp/vault", | ||||||
|  | 	"GoVersion": "go1.4.2", | ||||||
|  | 	"Deps": [ | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/armon/go-metrics", | ||||||
|  | 			"Rev": "a54701ebec11868993bc198c3f315353e9de2ed6" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/armon/go-radix", | ||||||
|  | 			"Rev": "0bab926c3433cfd6490c6d3c504a7b471362390c" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/go-sql-driver/mysql", | ||||||
|  | 			"Comment": "v1.2-88-ga197e5d", | ||||||
|  | 			"Rev": "a197e5d40516f2e9f74dcee085a5f2d4604e94df" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/google/go-github/github", | ||||||
|  | 			"Rev": "0aaa85be4f3087c6dd815a69e291775d4e83f9ea" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/google/go-querystring/query", | ||||||
|  | 			"Rev": "547ef5ac979778feb2f760cdb5f4eae1a2207b86" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/aws-sdk-go/aws", | ||||||
|  | 			"Comment": "tf0.4.0-3-ge6ea019", | ||||||
|  | 			"Rev": "e6ea0192eee4640f32ec73c0cbb71f63e4f2b65a" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/aws-sdk-go/gen/endpoints", | ||||||
|  | 			"Comment": "tf0.4.0-3-ge6ea019", | ||||||
|  | 			"Rev": "e6ea0192eee4640f32ec73c0cbb71f63e4f2b65a" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/aws-sdk-go/gen/iam", | ||||||
|  | 			"Comment": "tf0.4.0-3-ge6ea019", | ||||||
|  | 			"Rev": "e6ea0192eee4640f32ec73c0cbb71f63e4f2b65a" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/consul/api", | ||||||
|  | 			"Comment": "v0.5.0-199-g205af6b", | ||||||
|  | 			"Rev": "205af6ba750b88863e6ee50c7c3d19edc180a6f6" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/errwrap", | ||||||
|  | 			"Rev": "7554cd9344cec97297fa6649b055a8c98c2a1e55" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/go-multierror", | ||||||
|  | 			"Rev": "fcdddc395df1ddf4247c69bd436e84cfa0733f7e" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/go-syslog", | ||||||
|  | 			"Rev": "42a2b573b664dbf281bd48c3cc12c086b17a39ba" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/golang-lru", | ||||||
|  | 			"Rev": "d85392d6bc30546d352f52f2632814cde4201d44" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/hcl", | ||||||
|  | 			"Rev": "513e04c400ee2e81e97f5e011c08fb42c6f69b84" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/hashicorp/logutils", | ||||||
|  | 			"Rev": "367a65d59043b4f846d179341d138f01f988c186" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/lib/pq", | ||||||
|  | 			"Comment": "go1.0-cutoff-40-g8910d1c", | ||||||
|  | 			"Rev": "8910d1c3a4bda5c97c50bc38543953f1f1e1f8bb" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/mitchellh/cli", | ||||||
|  | 			"Rev": "afc399c273e70173826fb6f518a48edff23fe897" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/mitchellh/copystructure", | ||||||
|  | 			"Rev": "6fc66267e9da7d155a9d3bd489e00dad02666dc6" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/mitchellh/go-homedir", | ||||||
|  | 			"Rev": "1f6da4a72e57d4e7edd4a7295a585e0a3999a2d4" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/mitchellh/mapstructure", | ||||||
|  | 			"Rev": "442e588f213303bec7936deba67901f8fc8f18b1" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/mitchellh/reflectwalk", | ||||||
|  | 			"Rev": "242be0c275dedfba00a616563e6db75ab8f279ec" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/ryanuber/columnize", | ||||||
|  | 			"Comment": "v2.0.1-6-g44cb478", | ||||||
|  | 			"Rev": "44cb4788b2ec3c3d158dd3d1b50aba7d66f4b59a" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/vaughan0/go-ini", | ||||||
|  | 			"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "golang.org/x/crypto/ssh/terminal", | ||||||
|  | 			"Rev": "c84e1f8e3a7e322d497cd16c0e8a13c7e127baf3" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "golang.org/x/net/context", | ||||||
|  | 			"Rev": "ff8eb9a34a5cbb9941ffc6f84a19a8014c2646ad" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "golang.org/x/oauth2", | ||||||
|  | 			"Rev": "ec6d5d770f531108a6464462b2201b74fcd09314" | ||||||
|  | 		} | ||||||
|  | 	] | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								Godeps/Readme
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Godeps/Readme
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | This directory tree is generated automatically by godep. | ||||||
|  |  | ||||||
|  | Please do not edit. | ||||||
|  |  | ||||||
|  | See https://github.com/tools/godep for more information. | ||||||
							
								
								
									
										2
									
								
								Godeps/_workspace/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								Godeps/_workspace/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | /pkg | ||||||
|  | /bin | ||||||
							
								
								
									
										22
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | # Compiled Object files, Static and Dynamic libs (Shared Objects) | ||||||
|  | *.o | ||||||
|  | *.a | ||||||
|  | *.so | ||||||
|  |  | ||||||
|  | # Folders | ||||||
|  | _obj | ||||||
|  | _test | ||||||
|  |  | ||||||
|  | # Architecture specific extensions/prefixes | ||||||
|  | *.[568vq] | ||||||
|  | [568vq].out | ||||||
|  |  | ||||||
|  | *.cgo1.go | ||||||
|  | *.cgo2.c | ||||||
|  | _cgo_defun.c | ||||||
|  | _cgo_gotypes.go | ||||||
|  | _cgo_export.* | ||||||
|  |  | ||||||
|  | _testmain.go | ||||||
|  |  | ||||||
|  | *.exe | ||||||
							
								
								
									
										20
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | The MIT License (MIT) | ||||||
|  |  | ||||||
|  | Copyright (c) 2013 Armon Dadgar | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||||
|  | this software and associated documentation files (the "Software"), to deal in | ||||||
|  | the Software without restriction, including without limitation the rights to | ||||||
|  | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||||
|  | the Software, and to permit persons to whom the Software is furnished to do so, | ||||||
|  | subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||||
|  | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||||
|  | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||||
|  | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||||
|  | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
							
								
								
									
										71
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | go-metrics | ||||||
|  | ========== | ||||||
|  |  | ||||||
|  | This library provides a `metrics` package which can be used to instrument code, | ||||||
|  | expose application metrics, and profile runtime performance in a flexible manner. | ||||||
|  |  | ||||||
|  | Current API: [](https://godoc.org/github.com/armon/go-metrics) | ||||||
|  |  | ||||||
|  | Sinks | ||||||
|  | ===== | ||||||
|  |  | ||||||
|  | The `metrics` package makes use of a `MetricSink` interface to support delivery | ||||||
|  | to any type of backend. Currently the following sinks are provided: | ||||||
|  |  | ||||||
|  | * StatsiteSink : Sinks to a [statsite](https://github.com/armon/statsite/) instance (TCP) | ||||||
|  | * StatsdSink: Sinks to a [StatsD](https://github.com/etsy/statsd/) / statsite instance (UDP) | ||||||
|  | * PrometheusSink: Sinks to a [Prometheus](http://prometheus.io/) metrics endpoint (exposed via HTTP for scrapes) | ||||||
|  | * InmemSink : Provides in-memory aggregation, can be used to export stats | ||||||
|  | * FanoutSink : Sinks to multiple sinks. Enables writing to multiple statsite instances for example. | ||||||
|  | * BlackholeSink : Sinks to nowhere | ||||||
|  |  | ||||||
|  | In addition to the sinks, the `InmemSignal` can be used to catch a signal, | ||||||
|  | and dump a formatted output of recent metrics. For example, when a process gets | ||||||
|  | a SIGUSR1, it can dump to stderr recent performance metrics for debugging. | ||||||
|  |  | ||||||
|  | Examples | ||||||
|  | ======== | ||||||
|  |  | ||||||
|  | Here is an example of using the package: | ||||||
|  |  | ||||||
|  |     func SlowMethod() { | ||||||
|  |         // Profiling the runtime of a method | ||||||
|  |         defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Configure a statsite sink as the global metrics sink | ||||||
|  |     sink, _ := metrics.NewStatsiteSink("statsite:8125") | ||||||
|  |     metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink) | ||||||
|  |  | ||||||
|  |     // Emit a Key/Value pair | ||||||
|  |     metrics.EmitKey([]string{"questions", "meaning of life"}, 42) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Here is an example of setting up an signal handler: | ||||||
|  |  | ||||||
|  |     // Setup the inmem sink and signal handler | ||||||
|  |     inm := metrics.NewInmemSink(10*time.Second, time.Minute) | ||||||
|  |     sig := metrics.DefaultInmemSignal(inm) | ||||||
|  |     metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm) | ||||||
|  |  | ||||||
|  |     // Run some code | ||||||
|  |     inm.SetGauge([]string{"foo"}, 42) | ||||||
|  |     inm.EmitKey([]string{"bar"}, 30) | ||||||
|  |  | ||||||
|  |     inm.IncrCounter([]string{"baz"}, 42) | ||||||
|  |     inm.IncrCounter([]string{"baz"}, 1) | ||||||
|  |     inm.IncrCounter([]string{"baz"}, 80) | ||||||
|  |  | ||||||
|  |     inm.AddSample([]string{"method", "wow"}, 42) | ||||||
|  |     inm.AddSample([]string{"method", "wow"}, 100) | ||||||
|  |     inm.AddSample([]string{"method", "wow"}, 22) | ||||||
|  |  | ||||||
|  |     .... | ||||||
|  |  | ||||||
|  | When a signal comes in, output like the following will be dumped to stderr: | ||||||
|  |  | ||||||
|  |     [2014-01-28 14:57:33.04 -0800 PST][G] 'foo': 42.000 | ||||||
|  |     [2014-01-28 14:57:33.04 -0800 PST][P] 'bar': 30.000 | ||||||
|  |     [2014-01-28 14:57:33.04 -0800 PST][C] 'baz': Count: 3 Min: 1.000 Mean: 41.000 Max: 80.000 Stddev: 39.509 | ||||||
|  |     [2014-01-28 14:57:33.04 -0800 PST][S] 'method.wow': Count: 3 Min: 22.000 Mean: 54.667 Max: 100.000 Stddev: 40.513 | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/const_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/const_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | // +build !windows | ||||||
|  |  | ||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"syscall" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// DefaultSignal is used with DefaultInmemSignal | ||||||
|  | 	DefaultSignal = syscall.SIGUSR1 | ||||||
|  | ) | ||||||
							
								
								
									
										13
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/const_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/const_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | // +build windows | ||||||
|  |  | ||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"syscall" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// DefaultSignal is used with DefaultInmemSignal | ||||||
|  | 	// Windows has no SIGUSR1, use SIGBREAK | ||||||
|  | 	DefaultSignal = syscall.Signal(21) | ||||||
|  | ) | ||||||
							
								
								
									
										239
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"math" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // InmemSink provides a MetricSink that does in-memory aggregation | ||||||
|  | // without sending metrics over a network. It can be embedded within | ||||||
|  | // an application to provide profiling information. | ||||||
|  | type InmemSink struct { | ||||||
|  | 	// How long is each aggregation interval | ||||||
|  | 	interval time.Duration | ||||||
|  |  | ||||||
|  | 	// Retain controls how many metrics interval we keep | ||||||
|  | 	retain time.Duration | ||||||
|  |  | ||||||
|  | 	// maxIntervals is the maximum length of intervals. | ||||||
|  | 	// It is retain / interval. | ||||||
|  | 	maxIntervals int | ||||||
|  |  | ||||||
|  | 	// intervals is a slice of the retained intervals | ||||||
|  | 	intervals    []*IntervalMetrics | ||||||
|  | 	intervalLock sync.RWMutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IntervalMetrics stores the aggregated metrics | ||||||
|  | // for a specific interval | ||||||
|  | type IntervalMetrics struct { | ||||||
|  | 	sync.RWMutex | ||||||
|  |  | ||||||
|  | 	// The start time of the interval | ||||||
|  | 	Interval time.Time | ||||||
|  |  | ||||||
|  | 	// Gauges maps the key to the last set value | ||||||
|  | 	Gauges map[string]float32 | ||||||
|  |  | ||||||
|  | 	// Points maps the string to the list of emitted values | ||||||
|  | 	// from EmitKey | ||||||
|  | 	Points map[string][]float32 | ||||||
|  |  | ||||||
|  | 	// Counters maps the string key to a sum of the counter | ||||||
|  | 	// values | ||||||
|  | 	Counters map[string]*AggregateSample | ||||||
|  |  | ||||||
|  | 	// Samples maps the key to an AggregateSample, | ||||||
|  | 	// which has the rolled up view of a sample | ||||||
|  | 	Samples map[string]*AggregateSample | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewIntervalMetrics creates a new IntervalMetrics for a given interval | ||||||
|  | func NewIntervalMetrics(intv time.Time) *IntervalMetrics { | ||||||
|  | 	return &IntervalMetrics{ | ||||||
|  | 		Interval: intv, | ||||||
|  | 		Gauges:   make(map[string]float32), | ||||||
|  | 		Points:   make(map[string][]float32), | ||||||
|  | 		Counters: make(map[string]*AggregateSample), | ||||||
|  | 		Samples:  make(map[string]*AggregateSample), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AggregateSample is used to hold aggregate metrics | ||||||
|  | // about a sample | ||||||
|  | type AggregateSample struct { | ||||||
|  | 	Count int     // The count of emitted pairs | ||||||
|  | 	Sum   float64 // The sum of values | ||||||
|  | 	SumSq float64 // The sum of squared values | ||||||
|  | 	Min   float64 // Minimum value | ||||||
|  | 	Max   float64 // Maximum value | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Computes a Stddev of the values | ||||||
|  | func (a *AggregateSample) Stddev() float64 { | ||||||
|  | 	num := (float64(a.Count) * a.SumSq) - math.Pow(a.Sum, 2) | ||||||
|  | 	div := float64(a.Count * (a.Count - 1)) | ||||||
|  | 	if div == 0 { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	return math.Sqrt(num / div) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Computes a mean of the values | ||||||
|  | func (a *AggregateSample) Mean() float64 { | ||||||
|  | 	if a.Count == 0 { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	return a.Sum / float64(a.Count) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Ingest is used to update a sample | ||||||
|  | func (a *AggregateSample) Ingest(v float64) { | ||||||
|  | 	a.Count++ | ||||||
|  | 	a.Sum += v | ||||||
|  | 	a.SumSq += (v * v) | ||||||
|  | 	if v < a.Min || a.Count == 1 { | ||||||
|  | 		a.Min = v | ||||||
|  | 	} | ||||||
|  | 	if v > a.Max || a.Count == 1 { | ||||||
|  | 		a.Max = v | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *AggregateSample) String() string { | ||||||
|  | 	if a.Count == 0 { | ||||||
|  | 		return "Count: 0" | ||||||
|  | 	} else if a.Stddev() == 0 { | ||||||
|  | 		return fmt.Sprintf("Count: %d Sum: %0.3f", a.Count, a.Sum) | ||||||
|  | 	} else { | ||||||
|  | 		return fmt.Sprintf("Count: %d Min: %0.3f Mean: %0.3f Max: %0.3f Stddev: %0.3f Sum: %0.3f", | ||||||
|  | 			a.Count, a.Min, a.Mean(), a.Max, a.Stddev(), a.Sum) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewInmemSink is used to construct a new in-memory sink. | ||||||
|  | // Uses an aggregation interval and maximum retention period. | ||||||
|  | func NewInmemSink(interval, retain time.Duration) *InmemSink { | ||||||
|  | 	i := &InmemSink{ | ||||||
|  | 		interval:     interval, | ||||||
|  | 		retain:       retain, | ||||||
|  | 		maxIntervals: int(retain / interval), | ||||||
|  | 	} | ||||||
|  | 	i.intervals = make([]*IntervalMetrics, 0, i.maxIntervals) | ||||||
|  | 	return i | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *InmemSink) SetGauge(key []string, val float32) { | ||||||
|  | 	k := i.flattenKey(key) | ||||||
|  | 	intv := i.getInterval() | ||||||
|  |  | ||||||
|  | 	intv.Lock() | ||||||
|  | 	defer intv.Unlock() | ||||||
|  | 	intv.Gauges[k] = val | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *InmemSink) EmitKey(key []string, val float32) { | ||||||
|  | 	k := i.flattenKey(key) | ||||||
|  | 	intv := i.getInterval() | ||||||
|  |  | ||||||
|  | 	intv.Lock() | ||||||
|  | 	defer intv.Unlock() | ||||||
|  | 	vals := intv.Points[k] | ||||||
|  | 	intv.Points[k] = append(vals, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *InmemSink) IncrCounter(key []string, val float32) { | ||||||
|  | 	k := i.flattenKey(key) | ||||||
|  | 	intv := i.getInterval() | ||||||
|  |  | ||||||
|  | 	intv.Lock() | ||||||
|  | 	defer intv.Unlock() | ||||||
|  |  | ||||||
|  | 	agg := intv.Counters[k] | ||||||
|  | 	if agg == nil { | ||||||
|  | 		agg = &AggregateSample{} | ||||||
|  | 		intv.Counters[k] = agg | ||||||
|  | 	} | ||||||
|  | 	agg.Ingest(float64(val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *InmemSink) AddSample(key []string, val float32) { | ||||||
|  | 	k := i.flattenKey(key) | ||||||
|  | 	intv := i.getInterval() | ||||||
|  |  | ||||||
|  | 	intv.Lock() | ||||||
|  | 	defer intv.Unlock() | ||||||
|  |  | ||||||
|  | 	agg := intv.Samples[k] | ||||||
|  | 	if agg == nil { | ||||||
|  | 		agg = &AggregateSample{} | ||||||
|  | 		intv.Samples[k] = agg | ||||||
|  | 	} | ||||||
|  | 	agg.Ingest(float64(val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Data is used to retrieve all the aggregated metrics | ||||||
|  | // Intervals may be in use, and a read lock should be acquired | ||||||
|  | func (i *InmemSink) Data() []*IntervalMetrics { | ||||||
|  | 	// Get the current interval, forces creation | ||||||
|  | 	i.getInterval() | ||||||
|  |  | ||||||
|  | 	i.intervalLock.RLock() | ||||||
|  | 	defer i.intervalLock.RUnlock() | ||||||
|  |  | ||||||
|  | 	intervals := make([]*IntervalMetrics, len(i.intervals)) | ||||||
|  | 	copy(intervals, i.intervals) | ||||||
|  | 	return intervals | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *InmemSink) getExistingInterval(intv time.Time) *IntervalMetrics { | ||||||
|  | 	i.intervalLock.RLock() | ||||||
|  | 	defer i.intervalLock.RUnlock() | ||||||
|  |  | ||||||
|  | 	n := len(i.intervals) | ||||||
|  | 	if n > 0 && i.intervals[n-1].Interval == intv { | ||||||
|  | 		return i.intervals[n-1] | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *InmemSink) createInterval(intv time.Time) *IntervalMetrics { | ||||||
|  | 	i.intervalLock.Lock() | ||||||
|  | 	defer i.intervalLock.Unlock() | ||||||
|  |  | ||||||
|  | 	// Check for an existing interval | ||||||
|  | 	n := len(i.intervals) | ||||||
|  | 	if n > 0 && i.intervals[n-1].Interval == intv { | ||||||
|  | 		return i.intervals[n-1] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Add the current interval | ||||||
|  | 	current := NewIntervalMetrics(intv) | ||||||
|  | 	i.intervals = append(i.intervals, current) | ||||||
|  | 	n++ | ||||||
|  |  | ||||||
|  | 	// Truncate the intervals if they are too long | ||||||
|  | 	if n >= i.maxIntervals { | ||||||
|  | 		copy(i.intervals[0:], i.intervals[n-i.maxIntervals:]) | ||||||
|  | 		i.intervals = i.intervals[:i.maxIntervals] | ||||||
|  | 	} | ||||||
|  | 	return current | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getInterval returns the current interval to write to | ||||||
|  | func (i *InmemSink) getInterval() *IntervalMetrics { | ||||||
|  | 	intv := time.Now().Truncate(i.interval) | ||||||
|  | 	if m := i.getExistingInterval(intv); m != nil { | ||||||
|  | 		return m | ||||||
|  | 	} | ||||||
|  | 	return i.createInterval(intv) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Flattens the key for formatting, removes spaces | ||||||
|  | func (i *InmemSink) flattenKey(parts []string) string { | ||||||
|  | 	joined := strings.Join(parts, ".") | ||||||
|  | 	return strings.Replace(joined, " ", "_", -1) | ||||||
|  | } | ||||||
							
								
								
									
										100
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem_signal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem_signal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"os/signal" | ||||||
|  | 	"sync" | ||||||
|  | 	"syscall" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // InmemSignal is used to listen for a given signal, and when received, | ||||||
|  | // to dump the current metrics from the InmemSink to an io.Writer | ||||||
|  | type InmemSignal struct { | ||||||
|  | 	signal syscall.Signal | ||||||
|  | 	inm    *InmemSink | ||||||
|  | 	w      io.Writer | ||||||
|  | 	sigCh  chan os.Signal | ||||||
|  |  | ||||||
|  | 	stop     bool | ||||||
|  | 	stopCh   chan struct{} | ||||||
|  | 	stopLock sync.Mutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewInmemSignal creates a new InmemSignal which listens for a given signal, | ||||||
|  | // and dumps the current metrics out to a writer | ||||||
|  | func NewInmemSignal(inmem *InmemSink, sig syscall.Signal, w io.Writer) *InmemSignal { | ||||||
|  | 	i := &InmemSignal{ | ||||||
|  | 		signal: sig, | ||||||
|  | 		inm:    inmem, | ||||||
|  | 		w:      w, | ||||||
|  | 		sigCh:  make(chan os.Signal, 1), | ||||||
|  | 		stopCh: make(chan struct{}), | ||||||
|  | 	} | ||||||
|  | 	signal.Notify(i.sigCh, sig) | ||||||
|  | 	go i.run() | ||||||
|  | 	return i | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DefaultInmemSignal returns a new InmemSignal that responds to SIGUSR1 | ||||||
|  | // and writes output to stderr. Windows uses SIGBREAK | ||||||
|  | func DefaultInmemSignal(inmem *InmemSink) *InmemSignal { | ||||||
|  | 	return NewInmemSignal(inmem, DefaultSignal, os.Stderr) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Stop is used to stop the InmemSignal from listening | ||||||
|  | func (i *InmemSignal) Stop() { | ||||||
|  | 	i.stopLock.Lock() | ||||||
|  | 	defer i.stopLock.Unlock() | ||||||
|  |  | ||||||
|  | 	if i.stop { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	i.stop = true | ||||||
|  | 	close(i.stopCh) | ||||||
|  | 	signal.Stop(i.sigCh) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // run is a long running routine that handles signals | ||||||
|  | func (i *InmemSignal) run() { | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-i.sigCh: | ||||||
|  | 			i.dumpStats() | ||||||
|  | 		case <-i.stopCh: | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // dumpStats is used to dump the data to output writer | ||||||
|  | func (i *InmemSignal) dumpStats() { | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  |  | ||||||
|  | 	data := i.inm.Data() | ||||||
|  | 	// Skip the last period which is still being aggregated | ||||||
|  | 	for i := 0; i < len(data)-1; i++ { | ||||||
|  | 		intv := data[i] | ||||||
|  | 		intv.RLock() | ||||||
|  | 		for name, val := range intv.Gauges { | ||||||
|  | 			fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val) | ||||||
|  | 		} | ||||||
|  | 		for name, vals := range intv.Points { | ||||||
|  | 			for _, val := range vals { | ||||||
|  | 				fmt.Fprintf(buf, "[%v][P] '%s': %0.3f\n", intv.Interval, name, val) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		for name, agg := range intv.Counters { | ||||||
|  | 			fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg) | ||||||
|  | 		} | ||||||
|  | 		for name, agg := range intv.Samples { | ||||||
|  | 			fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg) | ||||||
|  | 		} | ||||||
|  | 		intv.RUnlock() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Write out the bytes | ||||||
|  | 	i.w.Write(buf.Bytes()) | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem_signal_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem_signal_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	"syscall" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestInmemSignal(t *testing.T) { | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	inm := NewInmemSink(10*time.Millisecond, 50*time.Millisecond) | ||||||
|  | 	sig := NewInmemSignal(inm, syscall.SIGUSR1, buf) | ||||||
|  | 	defer sig.Stop() | ||||||
|  |  | ||||||
|  | 	inm.SetGauge([]string{"foo"}, 42) | ||||||
|  | 	inm.EmitKey([]string{"bar"}, 42) | ||||||
|  | 	inm.IncrCounter([]string{"baz"}, 42) | ||||||
|  | 	inm.AddSample([]string{"wow"}, 42) | ||||||
|  |  | ||||||
|  | 	// Wait for period to end | ||||||
|  | 	time.Sleep(15 * time.Millisecond) | ||||||
|  |  | ||||||
|  | 	// Send signal! | ||||||
|  | 	syscall.Kill(os.Getpid(), syscall.SIGUSR1) | ||||||
|  |  | ||||||
|  | 	// Wait for flush | ||||||
|  | 	time.Sleep(10 * time.Millisecond) | ||||||
|  |  | ||||||
|  | 	// Check the output | ||||||
|  | 	out := string(buf.Bytes()) | ||||||
|  | 	if !strings.Contains(out, "[G] 'foo': 42") { | ||||||
|  | 		t.Fatalf("bad: %v", out) | ||||||
|  | 	} | ||||||
|  | 	if !strings.Contains(out, "[P] 'bar': 42") { | ||||||
|  | 		t.Fatalf("bad: %v", out) | ||||||
|  | 	} | ||||||
|  | 	if !strings.Contains(out, "[C] 'baz': Count: 1 Sum: 42") { | ||||||
|  | 		t.Fatalf("bad: %v", out) | ||||||
|  | 	} | ||||||
|  | 	if !strings.Contains(out, "[S] 'wow': Count: 1 Sum: 42") { | ||||||
|  | 		t.Fatalf("bad: %v", out) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										95
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/inmem_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestInmemSink(t *testing.T) { | ||||||
|  | 	inm := NewInmemSink(10*time.Millisecond, 50*time.Millisecond) | ||||||
|  |  | ||||||
|  | 	data := inm.Data() | ||||||
|  | 	if len(data) != 1 { | ||||||
|  | 		t.Fatalf("bad: %v", data) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Add data points | ||||||
|  | 	inm.SetGauge([]string{"foo", "bar"}, 42) | ||||||
|  | 	inm.EmitKey([]string{"foo", "bar"}, 42) | ||||||
|  | 	inm.IncrCounter([]string{"foo", "bar"}, 20) | ||||||
|  | 	inm.IncrCounter([]string{"foo", "bar"}, 22) | ||||||
|  | 	inm.AddSample([]string{"foo", "bar"}, 20) | ||||||
|  | 	inm.AddSample([]string{"foo", "bar"}, 22) | ||||||
|  |  | ||||||
|  | 	data = inm.Data() | ||||||
|  | 	if len(data) != 1 { | ||||||
|  | 		t.Fatalf("bad: %v", data) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	intvM := data[0] | ||||||
|  | 	intvM.RLock() | ||||||
|  |  | ||||||
|  | 	if time.Now().Sub(intvM.Interval) > 10*time.Millisecond { | ||||||
|  | 		t.Fatalf("interval too old") | ||||||
|  | 	} | ||||||
|  | 	if intvM.Gauges["foo.bar"] != 42 { | ||||||
|  | 		t.Fatalf("bad val: %v", intvM.Gauges) | ||||||
|  | 	} | ||||||
|  | 	if intvM.Points["foo.bar"][0] != 42 { | ||||||
|  | 		t.Fatalf("bad val: %v", intvM.Points) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	agg := intvM.Counters["foo.bar"] | ||||||
|  | 	if agg.Count != 2 { | ||||||
|  | 		t.Fatalf("bad val: %v", agg) | ||||||
|  | 	} | ||||||
|  | 	if agg.Sum != 42 { | ||||||
|  | 		t.Fatalf("bad val: %v", agg) | ||||||
|  | 	} | ||||||
|  | 	if agg.SumSq != 884 { | ||||||
|  | 		t.Fatalf("bad val: %v", agg) | ||||||
|  | 	} | ||||||
|  | 	if agg.Min != 20 { | ||||||
|  | 		t.Fatalf("bad val: %v", agg) | ||||||
|  | 	} | ||||||
|  | 	if agg.Max != 22 { | ||||||
|  | 		t.Fatalf("bad val: %v", agg) | ||||||
|  | 	} | ||||||
|  | 	if agg.Mean() != 21 { | ||||||
|  | 		t.Fatalf("bad val: %v", agg) | ||||||
|  | 	} | ||||||
|  | 	if agg.Stddev() != math.Sqrt(2) { | ||||||
|  | 		t.Fatalf("bad val: %v", agg) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if agg = intvM.Samples["foo.bar"]; agg == nil { | ||||||
|  | 		t.Fatalf("missing sample") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	intvM.RUnlock() | ||||||
|  |  | ||||||
|  | 	for i := 1; i < 10; i++ { | ||||||
|  | 		time.Sleep(10 * time.Millisecond) | ||||||
|  | 		inm.SetGauge([]string{"foo", "bar"}, 42) | ||||||
|  | 		data = inm.Data() | ||||||
|  | 		if len(data) != min(i+1, 5) { | ||||||
|  | 			t.Fatalf("bad: %v", data) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Should not exceed 5 intervals! | ||||||
|  | 	time.Sleep(10 * time.Millisecond) | ||||||
|  | 	inm.SetGauge([]string{"foo", "bar"}, 42) | ||||||
|  | 	data = inm.Data() | ||||||
|  | 	if len(data) != 5 { | ||||||
|  | 		t.Fatalf("bad: %v", data) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func min(a, b int) int { | ||||||
|  | 	if a < b { | ||||||
|  | 		return a | ||||||
|  | 	} | ||||||
|  | 	return b | ||||||
|  | } | ||||||
							
								
								
									
										115
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/metrics.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/metrics.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"runtime" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (m *Metrics) SetGauge(key []string, val float32) { | ||||||
|  | 	if m.HostName != "" && m.EnableHostname { | ||||||
|  | 		key = insert(0, m.HostName, key) | ||||||
|  | 	} | ||||||
|  | 	if m.EnableTypePrefix { | ||||||
|  | 		key = insert(0, "gauge", key) | ||||||
|  | 	} | ||||||
|  | 	if m.ServiceName != "" { | ||||||
|  | 		key = insert(0, m.ServiceName, key) | ||||||
|  | 	} | ||||||
|  | 	m.sink.SetGauge(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Metrics) EmitKey(key []string, val float32) { | ||||||
|  | 	if m.EnableTypePrefix { | ||||||
|  | 		key = insert(0, "kv", key) | ||||||
|  | 	} | ||||||
|  | 	if m.ServiceName != "" { | ||||||
|  | 		key = insert(0, m.ServiceName, key) | ||||||
|  | 	} | ||||||
|  | 	m.sink.EmitKey(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Metrics) IncrCounter(key []string, val float32) { | ||||||
|  | 	if m.EnableTypePrefix { | ||||||
|  | 		key = insert(0, "counter", key) | ||||||
|  | 	} | ||||||
|  | 	if m.ServiceName != "" { | ||||||
|  | 		key = insert(0, m.ServiceName, key) | ||||||
|  | 	} | ||||||
|  | 	m.sink.IncrCounter(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Metrics) AddSample(key []string, val float32) { | ||||||
|  | 	if m.EnableTypePrefix { | ||||||
|  | 		key = insert(0, "sample", key) | ||||||
|  | 	} | ||||||
|  | 	if m.ServiceName != "" { | ||||||
|  | 		key = insert(0, m.ServiceName, key) | ||||||
|  | 	} | ||||||
|  | 	m.sink.AddSample(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Metrics) MeasureSince(key []string, start time.Time) { | ||||||
|  | 	if m.EnableTypePrefix { | ||||||
|  | 		key = insert(0, "timer", key) | ||||||
|  | 	} | ||||||
|  | 	if m.ServiceName != "" { | ||||||
|  | 		key = insert(0, m.ServiceName, key) | ||||||
|  | 	} | ||||||
|  | 	now := time.Now() | ||||||
|  | 	elapsed := now.Sub(start) | ||||||
|  | 	msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity) | ||||||
|  | 	m.sink.AddSample(key, msec) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Periodically collects runtime stats to publish | ||||||
|  | func (m *Metrics) collectStats() { | ||||||
|  | 	for { | ||||||
|  | 		time.Sleep(m.ProfileInterval) | ||||||
|  | 		m.emitRuntimeStats() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Emits various runtime statsitics | ||||||
|  | func (m *Metrics) emitRuntimeStats() { | ||||||
|  | 	// Export number of Goroutines | ||||||
|  | 	numRoutines := runtime.NumGoroutine() | ||||||
|  | 	m.SetGauge([]string{"runtime", "num_goroutines"}, float32(numRoutines)) | ||||||
|  |  | ||||||
|  | 	// Export memory stats | ||||||
|  | 	var stats runtime.MemStats | ||||||
|  | 	runtime.ReadMemStats(&stats) | ||||||
|  | 	m.SetGauge([]string{"runtime", "alloc_bytes"}, float32(stats.Alloc)) | ||||||
|  | 	m.SetGauge([]string{"runtime", "sys_bytes"}, float32(stats.Sys)) | ||||||
|  | 	m.SetGauge([]string{"runtime", "malloc_count"}, float32(stats.Mallocs)) | ||||||
|  | 	m.SetGauge([]string{"runtime", "free_count"}, float32(stats.Frees)) | ||||||
|  | 	m.SetGauge([]string{"runtime", "heap_objects"}, float32(stats.HeapObjects)) | ||||||
|  | 	m.SetGauge([]string{"runtime", "total_gc_pause_ns"}, float32(stats.PauseTotalNs)) | ||||||
|  | 	m.SetGauge([]string{"runtime", "total_gc_runs"}, float32(stats.NumGC)) | ||||||
|  |  | ||||||
|  | 	// Export info about the last few GC runs | ||||||
|  | 	num := stats.NumGC | ||||||
|  |  | ||||||
|  | 	// Handle wrap around | ||||||
|  | 	if num < m.lastNumGC { | ||||||
|  | 		m.lastNumGC = 0 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Ensure we don't scan more than 256 | ||||||
|  | 	if num-m.lastNumGC >= 256 { | ||||||
|  | 		m.lastNumGC = num - 255 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i := m.lastNumGC; i < num; i++ { | ||||||
|  | 		pause := stats.PauseNs[i%256] | ||||||
|  | 		m.AddSample([]string{"runtime", "gc_pause_ns"}, float32(pause)) | ||||||
|  | 	} | ||||||
|  | 	m.lastNumGC = num | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Inserts a string value at an index into the slice | ||||||
|  | func insert(i int, v string, s []string) []string { | ||||||
|  | 	s = append(s, "") | ||||||
|  | 	copy(s[i+1:], s[i:]) | ||||||
|  | 	s[i] = v | ||||||
|  | 	return s | ||||||
|  | } | ||||||
							
								
								
									
										262
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/metrics_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/metrics_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,262 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"runtime" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func mockMetric() (*MockSink, *Metrics) { | ||||||
|  | 	m := &MockSink{} | ||||||
|  | 	met := &Metrics{sink: m} | ||||||
|  | 	return m, met | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMetrics_SetGauge(t *testing.T) { | ||||||
|  | 	m, met := mockMetric() | ||||||
|  | 	met.SetGauge([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.HostName = "test" | ||||||
|  | 	met.EnableHostname = true | ||||||
|  | 	met.SetGauge([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "test" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.EnableTypePrefix = true | ||||||
|  | 	met.SetGauge([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "gauge" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.ServiceName = "service" | ||||||
|  | 	met.SetGauge([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "service" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMetrics_EmitKey(t *testing.T) { | ||||||
|  | 	m, met := mockMetric() | ||||||
|  | 	met.EmitKey([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.EnableTypePrefix = true | ||||||
|  | 	met.EmitKey([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "kv" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.ServiceName = "service" | ||||||
|  | 	met.EmitKey([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "service" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMetrics_IncrCounter(t *testing.T) { | ||||||
|  | 	m, met := mockMetric() | ||||||
|  | 	met.IncrCounter([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.EnableTypePrefix = true | ||||||
|  | 	met.IncrCounter([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "counter" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.ServiceName = "service" | ||||||
|  | 	met.IncrCounter([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "service" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMetrics_AddSample(t *testing.T) { | ||||||
|  | 	m, met := mockMetric() | ||||||
|  | 	met.AddSample([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.EnableTypePrefix = true | ||||||
|  | 	met.AddSample([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "sample" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.ServiceName = "service" | ||||||
|  | 	met.AddSample([]string{"key"}, float32(1)) | ||||||
|  | 	if m.keys[0][0] != "service" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] != 1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMetrics_MeasureSince(t *testing.T) { | ||||||
|  | 	m, met := mockMetric() | ||||||
|  | 	met.TimerGranularity = time.Millisecond | ||||||
|  | 	n := time.Now() | ||||||
|  | 	met.MeasureSince([]string{"key"}, n) | ||||||
|  | 	if m.keys[0][0] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] > 0.1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.TimerGranularity = time.Millisecond | ||||||
|  | 	met.EnableTypePrefix = true | ||||||
|  | 	met.MeasureSince([]string{"key"}, n) | ||||||
|  | 	if m.keys[0][0] != "timer" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] > 0.1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m, met = mockMetric() | ||||||
|  | 	met.TimerGranularity = time.Millisecond | ||||||
|  | 	met.ServiceName = "service" | ||||||
|  | 	met.MeasureSince([]string{"key"}, n) | ||||||
|  | 	if m.keys[0][0] != "service" || m.keys[0][1] != "key" { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] > 0.1 { | ||||||
|  | 		t.Fatalf("") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMetrics_EmitRuntimeStats(t *testing.T) { | ||||||
|  | 	runtime.GC() | ||||||
|  | 	m, met := mockMetric() | ||||||
|  | 	met.emitRuntimeStats() | ||||||
|  |  | ||||||
|  | 	if m.keys[0][0] != "runtime" || m.keys[0][1] != "num_goroutines" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] <= 1 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[1][0] != "runtime" || m.keys[1][1] != "alloc_bytes" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[1] <= 40000 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[2][0] != "runtime" || m.keys[2][1] != "sys_bytes" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[2] <= 100000 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[3][0] != "runtime" || m.keys[3][1] != "malloc_count" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[3] <= 100 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[4][0] != "runtime" || m.keys[4][1] != "free_count" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[4] <= 100 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[5][0] != "runtime" || m.keys[5][1] != "heap_objects" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[5] <= 100 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[6][0] != "runtime" || m.keys[6][1] != "total_gc_pause_ns" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[6] <= 100000 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[7][0] != "runtime" || m.keys[7][1] != "total_gc_runs" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[7] <= 1 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if m.keys[8][0] != "runtime" || m.keys[8][1] != "gc_pause_ns" { | ||||||
|  | 		t.Fatalf("bad key %v", m.keys) | ||||||
|  | 	} | ||||||
|  | 	if m.vals[8] <= 1000 { | ||||||
|  | 		t.Fatalf("bad val: %v", m.vals) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestInsert(t *testing.T) { | ||||||
|  | 	k := []string{"hi", "bob"} | ||||||
|  | 	exp := []string{"hi", "there", "bob"} | ||||||
|  | 	out := insert(1, "there", k) | ||||||
|  | 	if !reflect.DeepEqual(exp, out) { | ||||||
|  | 		t.Fatalf("bad insert %v %v", exp, out) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										88
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/prometheus/prometheus.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/prometheus/prometheus.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | // +build go1.3 | ||||||
|  | package prometheus | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/prometheus/client_golang/prometheus" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type PrometheusSink struct { | ||||||
|  | 	mu        sync.Mutex | ||||||
|  | 	gauges    map[string]prometheus.Gauge | ||||||
|  | 	summaries map[string]prometheus.Summary | ||||||
|  | 	counters  map[string]prometheus.Counter | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewPrometheusSink() (*PrometheusSink, error) { | ||||||
|  | 	return &PrometheusSink{ | ||||||
|  | 		gauges:    make(map[string]prometheus.Gauge), | ||||||
|  | 		summaries: make(map[string]prometheus.Summary), | ||||||
|  | 		counters:  make(map[string]prometheus.Counter), | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *PrometheusSink) flattenKey(parts []string) string { | ||||||
|  | 	joined := strings.Join(parts, "_") | ||||||
|  | 	joined = strings.Replace(joined, " ", "_", -1) | ||||||
|  | 	joined = strings.Replace(joined, ".", "_", -1) | ||||||
|  | 	joined = strings.Replace(joined, "-", "_", -1) | ||||||
|  | 	return joined | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *PrometheusSink) SetGauge(parts []string, val float32) { | ||||||
|  | 	p.mu.Lock() | ||||||
|  | 	defer p.mu.Unlock() | ||||||
|  | 	key := p.flattenKey(parts) | ||||||
|  | 	g, ok := p.gauges[key] | ||||||
|  | 	if !ok { | ||||||
|  | 		g = prometheus.NewGauge(prometheus.GaugeOpts{ | ||||||
|  | 			Name: key, | ||||||
|  | 			Help: key, | ||||||
|  | 		}) | ||||||
|  | 		prometheus.MustRegister(g) | ||||||
|  | 		p.gauges[key] = g | ||||||
|  | 	} | ||||||
|  | 	g.Set(float64(val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *PrometheusSink) AddSample(parts []string, val float32) { | ||||||
|  | 	p.mu.Lock() | ||||||
|  | 	defer p.mu.Unlock() | ||||||
|  | 	key := p.flattenKey(parts) | ||||||
|  | 	g, ok := p.summaries[key] | ||||||
|  | 	if !ok { | ||||||
|  | 		g = prometheus.NewSummary(prometheus.SummaryOpts{ | ||||||
|  | 			Name:   key, | ||||||
|  | 			Help:   key, | ||||||
|  | 			MaxAge: 10 * time.Second, | ||||||
|  | 		}) | ||||||
|  | 		prometheus.MustRegister(g) | ||||||
|  | 		p.summaries[key] = g | ||||||
|  | 	} | ||||||
|  | 	g.Observe(float64(val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EmitKey is not implemented. Prometheus doesn’t offer a type for which an | ||||||
|  | // arbitrary number of values is retained, as Prometheus works with a pull | ||||||
|  | // model, rather than a push model. | ||||||
|  | func (p *PrometheusSink) EmitKey(key []string, val float32) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *PrometheusSink) IncrCounter(parts []string, val float32) { | ||||||
|  | 	p.mu.Lock() | ||||||
|  | 	defer p.mu.Unlock() | ||||||
|  | 	key := p.flattenKey(parts) | ||||||
|  | 	g, ok := p.counters[key] | ||||||
|  | 	if !ok { | ||||||
|  | 		g = prometheus.NewCounter(prometheus.CounterOpts{ | ||||||
|  | 			Name: key, | ||||||
|  | 			Help: key, | ||||||
|  | 		}) | ||||||
|  | 		prometheus.MustRegister(g) | ||||||
|  | 		p.counters[key] = g | ||||||
|  | 	} | ||||||
|  | 	g.Add(float64(val)) | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/sink.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/sink.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | // The MetricSink interface is used to transmit metrics information | ||||||
|  | // to an external system | ||||||
|  | type MetricSink interface { | ||||||
|  | 	// A Gauge should retain the last value it is set to | ||||||
|  | 	SetGauge(key []string, val float32) | ||||||
|  |  | ||||||
|  | 	// Should emit a Key/Value pair for each call | ||||||
|  | 	EmitKey(key []string, val float32) | ||||||
|  |  | ||||||
|  | 	// Counters should accumulate values | ||||||
|  | 	IncrCounter(key []string, val float32) | ||||||
|  |  | ||||||
|  | 	// Samples are for timing information, where quantiles are used | ||||||
|  | 	AddSample(key []string, val float32) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BlackholeSink is used to just blackhole messages | ||||||
|  | type BlackholeSink struct{} | ||||||
|  |  | ||||||
|  | func (*BlackholeSink) SetGauge(key []string, val float32)    {} | ||||||
|  | func (*BlackholeSink) EmitKey(key []string, val float32)     {} | ||||||
|  | func (*BlackholeSink) IncrCounter(key []string, val float32) {} | ||||||
|  | func (*BlackholeSink) AddSample(key []string, val float32)   {} | ||||||
|  |  | ||||||
|  | // FanoutSink is used to sink to fanout values to multiple sinks | ||||||
|  | type FanoutSink []MetricSink | ||||||
|  |  | ||||||
|  | func (fh FanoutSink) SetGauge(key []string, val float32) { | ||||||
|  | 	for _, s := range fh { | ||||||
|  | 		s.SetGauge(key, val) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fh FanoutSink) EmitKey(key []string, val float32) { | ||||||
|  | 	for _, s := range fh { | ||||||
|  | 		s.EmitKey(key, val) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fh FanoutSink) IncrCounter(key []string, val float32) { | ||||||
|  | 	for _, s := range fh { | ||||||
|  | 		s.IncrCounter(key, val) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fh FanoutSink) AddSample(key []string, val float32) { | ||||||
|  | 	for _, s := range fh { | ||||||
|  | 		s.AddSample(key, val) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										120
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/sink_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/sink_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MockSink struct { | ||||||
|  | 	keys [][]string | ||||||
|  | 	vals []float32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockSink) SetGauge(key []string, val float32) { | ||||||
|  | 	m.keys = append(m.keys, key) | ||||||
|  | 	m.vals = append(m.vals, val) | ||||||
|  | } | ||||||
|  | func (m *MockSink) EmitKey(key []string, val float32) { | ||||||
|  | 	m.keys = append(m.keys, key) | ||||||
|  | 	m.vals = append(m.vals, val) | ||||||
|  | } | ||||||
|  | func (m *MockSink) IncrCounter(key []string, val float32) { | ||||||
|  | 	m.keys = append(m.keys, key) | ||||||
|  | 	m.vals = append(m.vals, val) | ||||||
|  | } | ||||||
|  | func (m *MockSink) AddSample(key []string, val float32) { | ||||||
|  | 	m.keys = append(m.keys, key) | ||||||
|  | 	m.vals = append(m.vals, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestFanoutSink_Gauge(t *testing.T) { | ||||||
|  | 	m1 := &MockSink{} | ||||||
|  | 	m2 := &MockSink{} | ||||||
|  | 	fh := &FanoutSink{m1, m2} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	fh.SetGauge(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m1.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m1.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestFanoutSink_Key(t *testing.T) { | ||||||
|  | 	m1 := &MockSink{} | ||||||
|  | 	m2 := &MockSink{} | ||||||
|  | 	fh := &FanoutSink{m1, m2} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	fh.EmitKey(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m1.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m1.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestFanoutSink_Counter(t *testing.T) { | ||||||
|  | 	m1 := &MockSink{} | ||||||
|  | 	m2 := &MockSink{} | ||||||
|  | 	fh := &FanoutSink{m1, m2} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	fh.IncrCounter(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m1.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m1.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestFanoutSink_Sample(t *testing.T) { | ||||||
|  | 	m1 := &MockSink{} | ||||||
|  | 	m2 := &MockSink{} | ||||||
|  | 	fh := &FanoutSink{m1, m2} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	fh.AddSample(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m1.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m1.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m2.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										95
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/start.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/start.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Config is used to configure metrics settings | ||||||
|  | type Config struct { | ||||||
|  | 	ServiceName          string        // Prefixed with keys to seperate services | ||||||
|  | 	HostName             string        // Hostname to use. If not provided and EnableHostname, it will be os.Hostname | ||||||
|  | 	EnableHostname       bool          // Enable prefixing gauge values with hostname | ||||||
|  | 	EnableRuntimeMetrics bool          // Enables profiling of runtime metrics (GC, Goroutines, Memory) | ||||||
|  | 	EnableTypePrefix     bool          // Prefixes key with a type ("counter", "gauge", "timer") | ||||||
|  | 	TimerGranularity     time.Duration // Granularity of timers. | ||||||
|  | 	ProfileInterval      time.Duration // Interval to profile runtime metrics | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Metrics represents an instance of a metrics sink that can | ||||||
|  | // be used to emit | ||||||
|  | type Metrics struct { | ||||||
|  | 	Config | ||||||
|  | 	lastNumGC uint32 | ||||||
|  | 	sink      MetricSink | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Shared global metrics instance | ||||||
|  | var globalMetrics *Metrics | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	// Initialize to a blackhole sink to avoid errors | ||||||
|  | 	globalMetrics = &Metrics{sink: &BlackholeSink{}} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DefaultConfig provides a sane default configuration | ||||||
|  | func DefaultConfig(serviceName string) *Config { | ||||||
|  | 	c := &Config{ | ||||||
|  | 		ServiceName:          serviceName, // Use client provided service | ||||||
|  | 		HostName:             "", | ||||||
|  | 		EnableHostname:       true,             // Enable hostname prefix | ||||||
|  | 		EnableRuntimeMetrics: true,             // Enable runtime profiling | ||||||
|  | 		EnableTypePrefix:     false,            // Disable type prefix | ||||||
|  | 		TimerGranularity:     time.Millisecond, // Timers are in milliseconds | ||||||
|  | 		ProfileInterval:      time.Second,      // Poll runtime every second | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Try to get the hostname | ||||||
|  | 	name, _ := os.Hostname() | ||||||
|  | 	c.HostName = name | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New is used to create a new instance of Metrics | ||||||
|  | func New(conf *Config, sink MetricSink) (*Metrics, error) { | ||||||
|  | 	met := &Metrics{} | ||||||
|  | 	met.Config = *conf | ||||||
|  | 	met.sink = sink | ||||||
|  |  | ||||||
|  | 	// Start the runtime collector | ||||||
|  | 	if conf.EnableRuntimeMetrics { | ||||||
|  | 		go met.collectStats() | ||||||
|  | 	} | ||||||
|  | 	return met, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewGlobal is the same as New, but it assigns the metrics object to be | ||||||
|  | // used globally as well as returning it. | ||||||
|  | func NewGlobal(conf *Config, sink MetricSink) (*Metrics, error) { | ||||||
|  | 	metrics, err := New(conf, sink) | ||||||
|  | 	if err == nil { | ||||||
|  | 		globalMetrics = metrics | ||||||
|  | 	} | ||||||
|  | 	return metrics, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Proxy all the methods to the globalMetrics instance | ||||||
|  | func SetGauge(key []string, val float32) { | ||||||
|  | 	globalMetrics.SetGauge(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EmitKey(key []string, val float32) { | ||||||
|  | 	globalMetrics.EmitKey(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func IncrCounter(key []string, val float32) { | ||||||
|  | 	globalMetrics.IncrCounter(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func AddSample(key []string, val float32) { | ||||||
|  | 	globalMetrics.AddSample(key, val) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func MeasureSince(key []string, start time.Time) { | ||||||
|  | 	globalMetrics.MeasureSince(key, start) | ||||||
|  | } | ||||||
							
								
								
									
										110
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/start_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/start_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestDefaultConfig(t *testing.T) { | ||||||
|  | 	conf := DefaultConfig("service") | ||||||
|  | 	if conf.ServiceName != "service" { | ||||||
|  | 		t.Fatalf("Bad name") | ||||||
|  | 	} | ||||||
|  | 	if conf.HostName == "" { | ||||||
|  | 		t.Fatalf("missing hostname") | ||||||
|  | 	} | ||||||
|  | 	if !conf.EnableHostname || !conf.EnableRuntimeMetrics { | ||||||
|  | 		t.Fatalf("expect true") | ||||||
|  | 	} | ||||||
|  | 	if conf.EnableTypePrefix { | ||||||
|  | 		t.Fatalf("expect false") | ||||||
|  | 	} | ||||||
|  | 	if conf.TimerGranularity != time.Millisecond { | ||||||
|  | 		t.Fatalf("bad granularity") | ||||||
|  | 	} | ||||||
|  | 	if conf.ProfileInterval != time.Second { | ||||||
|  | 		t.Fatalf("bad interval") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Test_GlobalMetrics_SetGauge(t *testing.T) { | ||||||
|  | 	m := &MockSink{} | ||||||
|  | 	globalMetrics = &Metrics{sink: m} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	SetGauge(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Test_GlobalMetrics_EmitKey(t *testing.T) { | ||||||
|  | 	m := &MockSink{} | ||||||
|  | 	globalMetrics = &Metrics{sink: m} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	EmitKey(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Test_GlobalMetrics_IncrCounter(t *testing.T) { | ||||||
|  | 	m := &MockSink{} | ||||||
|  | 	globalMetrics = &Metrics{sink: m} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	IncrCounter(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Test_GlobalMetrics_AddSample(t *testing.T) { | ||||||
|  | 	m := &MockSink{} | ||||||
|  | 	globalMetrics = &Metrics{sink: m} | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	v := float32(42.0) | ||||||
|  | 	AddSample(k, v) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(m.vals[0], v) { | ||||||
|  | 		t.Fatalf("val not equal") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Test_GlobalMetrics_MeasureSince(t *testing.T) { | ||||||
|  | 	m := &MockSink{} | ||||||
|  | 	globalMetrics = &Metrics{sink: m} | ||||||
|  | 	globalMetrics.TimerGranularity = time.Millisecond | ||||||
|  |  | ||||||
|  | 	k := []string{"test"} | ||||||
|  | 	now := time.Now() | ||||||
|  | 	MeasureSince(k, now) | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(m.keys[0], k) { | ||||||
|  | 		t.Fatalf("key not equal") | ||||||
|  | 	} | ||||||
|  | 	if m.vals[0] > 0.1 { | ||||||
|  | 		t.Fatalf("val too large %v", m.vals[0]) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										154
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"net" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// statsdMaxLen is the maximum size of a packet | ||||||
|  | 	// to send to statsd | ||||||
|  | 	statsdMaxLen = 1400 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // StatsdSink provides a MetricSink that can be used | ||||||
|  | // with a statsite or statsd metrics server. It uses | ||||||
|  | // only UDP packets, while StatsiteSink uses TCP. | ||||||
|  | type StatsdSink struct { | ||||||
|  | 	addr        string | ||||||
|  | 	metricQueue chan string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewStatsdSink is used to create a new StatsdSink | ||||||
|  | func NewStatsdSink(addr string) (*StatsdSink, error) { | ||||||
|  | 	s := &StatsdSink{ | ||||||
|  | 		addr:        addr, | ||||||
|  | 		metricQueue: make(chan string, 4096), | ||||||
|  | 	} | ||||||
|  | 	go s.flushMetrics() | ||||||
|  | 	return s, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Close is used to stop flushing to statsd | ||||||
|  | func (s *StatsdSink) Shutdown() { | ||||||
|  | 	close(s.metricQueue) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsdSink) SetGauge(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsdSink) EmitKey(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsdSink) IncrCounter(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsdSink) AddSample(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Flattens the key for formatting, removes spaces | ||||||
|  | func (s *StatsdSink) flattenKey(parts []string) string { | ||||||
|  | 	joined := strings.Join(parts, ".") | ||||||
|  | 	return strings.Map(func(r rune) rune { | ||||||
|  | 		switch r { | ||||||
|  | 		case ':': | ||||||
|  | 			fallthrough | ||||||
|  | 		case ' ': | ||||||
|  | 			return '_' | ||||||
|  | 		default: | ||||||
|  | 			return r | ||||||
|  | 		} | ||||||
|  | 	}, joined) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Does a non-blocking push to the metrics queue | ||||||
|  | func (s *StatsdSink) pushMetric(m string) { | ||||||
|  | 	select { | ||||||
|  | 	case s.metricQueue <- m: | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Flushes metrics | ||||||
|  | func (s *StatsdSink) flushMetrics() { | ||||||
|  | 	var sock net.Conn | ||||||
|  | 	var err error | ||||||
|  | 	var wait <-chan time.Time | ||||||
|  | 	ticker := time.NewTicker(flushInterval) | ||||||
|  | 	defer ticker.Stop() | ||||||
|  |  | ||||||
|  | CONNECT: | ||||||
|  | 	// Create a buffer | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  |  | ||||||
|  | 	// Attempt to connect | ||||||
|  | 	sock, err = net.Dial("udp", s.addr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("[ERR] Error connecting to statsd! Err: %s", err) | ||||||
|  | 		goto WAIT | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case metric, ok := <-s.metricQueue: | ||||||
|  | 			// Get a metric from the queue | ||||||
|  | 			if !ok { | ||||||
|  | 				goto QUIT | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Check if this would overflow the packet size | ||||||
|  | 			if len(metric)+buf.Len() > statsdMaxLen { | ||||||
|  | 				_, err := sock.Write(buf.Bytes()) | ||||||
|  | 				buf.Reset() | ||||||
|  | 				if err != nil { | ||||||
|  | 					log.Printf("[ERR] Error writing to statsd! Err: %s", err) | ||||||
|  | 					goto WAIT | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Append to the buffer | ||||||
|  | 			buf.WriteString(metric) | ||||||
|  |  | ||||||
|  | 		case <-ticker.C: | ||||||
|  | 			if buf.Len() == 0 { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			_, err := sock.Write(buf.Bytes()) | ||||||
|  | 			buf.Reset() | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Printf("[ERR] Error flushing to statsd! Err: %s", err) | ||||||
|  | 				goto WAIT | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | WAIT: | ||||||
|  | 	// Wait for a while | ||||||
|  | 	wait = time.After(time.Duration(5) * time.Second) | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		// Dequeue the messages to avoid backlog | ||||||
|  | 		case _, ok := <-s.metricQueue: | ||||||
|  | 			if !ok { | ||||||
|  | 				goto QUIT | ||||||
|  | 			} | ||||||
|  | 		case <-wait: | ||||||
|  | 			goto CONNECT | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | QUIT: | ||||||
|  | 	s.metricQueue = nil | ||||||
|  | } | ||||||
							
								
								
									
										105
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsd_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsd_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"bytes" | ||||||
|  | 	"net" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestStatsd_Flatten(t *testing.T) { | ||||||
|  | 	s := &StatsdSink{} | ||||||
|  | 	flat := s.flattenKey([]string{"a", "b", "c", "d"}) | ||||||
|  | 	if flat != "a.b.c.d" { | ||||||
|  | 		t.Fatalf("Bad flat") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStatsd_PushFullQueue(t *testing.T) { | ||||||
|  | 	q := make(chan string, 1) | ||||||
|  | 	q <- "full" | ||||||
|  |  | ||||||
|  | 	s := &StatsdSink{metricQueue: q} | ||||||
|  | 	s.pushMetric("omit") | ||||||
|  |  | ||||||
|  | 	out := <-q | ||||||
|  | 	if out != "full" { | ||||||
|  | 		t.Fatalf("bad val %v", out) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	select { | ||||||
|  | 	case v := <-q: | ||||||
|  | 		t.Fatalf("bad val %v", v) | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStatsd_Conn(t *testing.T) { | ||||||
|  | 	addr := "127.0.0.1:7524" | ||||||
|  | 	done := make(chan bool) | ||||||
|  | 	go func() { | ||||||
|  | 		list, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 7524}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  | 		defer list.Close() | ||||||
|  | 		buf := make([]byte, 1500) | ||||||
|  | 		n, err := list.Read(buf) | ||||||
|  | 		if err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  | 		buf = buf[:n] | ||||||
|  | 		reader := bufio.NewReader(bytes.NewReader(buf)) | ||||||
|  |  | ||||||
|  | 		line, err := reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "gauge.val:1.000000|g\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		line, err = reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "key.other:2.000000|kv\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		line, err = reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "counter.me:3.000000|c\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		line, err = reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "sample.slow_thingy:4.000000|ms\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		done <- true | ||||||
|  | 	}() | ||||||
|  | 	s, err := NewStatsdSink(addr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("bad error") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.SetGauge([]string{"gauge", "val"}, float32(1)) | ||||||
|  | 	s.EmitKey([]string{"key", "other"}, float32(2)) | ||||||
|  | 	s.IncrCounter([]string{"counter", "me"}, float32(3)) | ||||||
|  | 	s.AddSample([]string{"sample", "slow thingy"}, float32(4)) | ||||||
|  |  | ||||||
|  | 	select { | ||||||
|  | 	case <-done: | ||||||
|  | 		s.Shutdown() | ||||||
|  | 	case <-time.After(3 * time.Second): | ||||||
|  | 		t.Fatalf("timeout") | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										142
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsite.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsite.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"net" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// We force flush the statsite metrics after this period of | ||||||
|  | 	// inactivity. Prevents stats from getting stuck in a buffer | ||||||
|  | 	// forever. | ||||||
|  | 	flushInterval = 100 * time.Millisecond | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // StatsiteSink provides a MetricSink that can be used with a | ||||||
|  | // statsite metrics server | ||||||
|  | type StatsiteSink struct { | ||||||
|  | 	addr        string | ||||||
|  | 	metricQueue chan string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewStatsiteSink is used to create a new StatsiteSink | ||||||
|  | func NewStatsiteSink(addr string) (*StatsiteSink, error) { | ||||||
|  | 	s := &StatsiteSink{ | ||||||
|  | 		addr:        addr, | ||||||
|  | 		metricQueue: make(chan string, 4096), | ||||||
|  | 	} | ||||||
|  | 	go s.flushMetrics() | ||||||
|  | 	return s, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Close is used to stop flushing to statsite | ||||||
|  | func (s *StatsiteSink) Shutdown() { | ||||||
|  | 	close(s.metricQueue) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsiteSink) SetGauge(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsiteSink) EmitKey(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsiteSink) IncrCounter(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *StatsiteSink) AddSample(key []string, val float32) { | ||||||
|  | 	flatKey := s.flattenKey(key) | ||||||
|  | 	s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Flattens the key for formatting, removes spaces | ||||||
|  | func (s *StatsiteSink) flattenKey(parts []string) string { | ||||||
|  | 	joined := strings.Join(parts, ".") | ||||||
|  | 	return strings.Map(func(r rune) rune { | ||||||
|  | 		switch r { | ||||||
|  | 		case ':': | ||||||
|  | 			fallthrough | ||||||
|  | 		case ' ': | ||||||
|  | 			return '_' | ||||||
|  | 		default: | ||||||
|  | 			return r | ||||||
|  | 		} | ||||||
|  | 	}, joined) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Does a non-blocking push to the metrics queue | ||||||
|  | func (s *StatsiteSink) pushMetric(m string) { | ||||||
|  | 	select { | ||||||
|  | 	case s.metricQueue <- m: | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Flushes metrics | ||||||
|  | func (s *StatsiteSink) flushMetrics() { | ||||||
|  | 	var sock net.Conn | ||||||
|  | 	var err error | ||||||
|  | 	var wait <-chan time.Time | ||||||
|  | 	var buffered *bufio.Writer | ||||||
|  | 	ticker := time.NewTicker(flushInterval) | ||||||
|  | 	defer ticker.Stop() | ||||||
|  |  | ||||||
|  | CONNECT: | ||||||
|  | 	// Attempt to connect | ||||||
|  | 	sock, err = net.Dial("tcp", s.addr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("[ERR] Error connecting to statsite! Err: %s", err) | ||||||
|  | 		goto WAIT | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Create a buffered writer | ||||||
|  | 	buffered = bufio.NewWriter(sock) | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case metric, ok := <-s.metricQueue: | ||||||
|  | 			// Get a metric from the queue | ||||||
|  | 			if !ok { | ||||||
|  | 				goto QUIT | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Try to send to statsite | ||||||
|  | 			_, err := buffered.Write([]byte(metric)) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Printf("[ERR] Error writing to statsite! Err: %s", err) | ||||||
|  | 				goto WAIT | ||||||
|  | 			} | ||||||
|  | 		case <-ticker.C: | ||||||
|  | 			if err := buffered.Flush(); err != nil { | ||||||
|  | 				log.Printf("[ERR] Error flushing to statsite! Err: %s", err) | ||||||
|  | 				goto WAIT | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | WAIT: | ||||||
|  | 	// Wait for a while | ||||||
|  | 	wait = time.After(time.Duration(5) * time.Second) | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		// Dequeue the messages to avoid backlog | ||||||
|  | 		case _, ok := <-s.metricQueue: | ||||||
|  | 			if !ok { | ||||||
|  | 				goto QUIT | ||||||
|  | 			} | ||||||
|  | 		case <-wait: | ||||||
|  | 			goto CONNECT | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | QUIT: | ||||||
|  | 	s.metricQueue = nil | ||||||
|  | } | ||||||
							
								
								
									
										101
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsite_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								Godeps/_workspace/src/github.com/armon/go-metrics/statsite_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | package metrics | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"net" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func acceptConn(addr string) net.Conn { | ||||||
|  | 	ln, _ := net.Listen("tcp", addr) | ||||||
|  | 	conn, _ := ln.Accept() | ||||||
|  | 	return conn | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStatsite_Flatten(t *testing.T) { | ||||||
|  | 	s := &StatsiteSink{} | ||||||
|  | 	flat := s.flattenKey([]string{"a", "b", "c", "d"}) | ||||||
|  | 	if flat != "a.b.c.d" { | ||||||
|  | 		t.Fatalf("Bad flat") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStatsite_PushFullQueue(t *testing.T) { | ||||||
|  | 	q := make(chan string, 1) | ||||||
|  | 	q <- "full" | ||||||
|  |  | ||||||
|  | 	s := &StatsiteSink{metricQueue: q} | ||||||
|  | 	s.pushMetric("omit") | ||||||
|  |  | ||||||
|  | 	out := <-q | ||||||
|  | 	if out != "full" { | ||||||
|  | 		t.Fatalf("bad val %v", out) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	select { | ||||||
|  | 	case v := <-q: | ||||||
|  | 		t.Fatalf("bad val %v", v) | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStatsite_Conn(t *testing.T) { | ||||||
|  | 	addr := "localhost:7523" | ||||||
|  | 	done := make(chan bool) | ||||||
|  | 	go func() { | ||||||
|  | 		conn := acceptConn(addr) | ||||||
|  | 		reader := bufio.NewReader(conn) | ||||||
|  |  | ||||||
|  | 		line, err := reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "gauge.val:1.000000|g\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		line, err = reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "key.other:2.000000|kv\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		line, err = reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "counter.me:3.000000|c\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		line, err = reader.ReadString('\n') | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("unexpected err %s", err) | ||||||
|  | 		} | ||||||
|  | 		if line != "sample.slow_thingy:4.000000|ms\n" { | ||||||
|  | 			t.Fatalf("bad line %s", line) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		conn.Close() | ||||||
|  | 		done <- true | ||||||
|  | 	}() | ||||||
|  | 	s, err := NewStatsiteSink(addr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("bad error") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.SetGauge([]string{"gauge", "val"}, float32(1)) | ||||||
|  | 	s.EmitKey([]string{"key", "other"}, float32(2)) | ||||||
|  | 	s.IncrCounter([]string{"counter", "me"}, float32(3)) | ||||||
|  | 	s.AddSample([]string{"sample", "slow thingy"}, float32(4)) | ||||||
|  |  | ||||||
|  | 	select { | ||||||
|  | 	case <-done: | ||||||
|  | 		s.Shutdown() | ||||||
|  | 	case <-time.After(3 * time.Second): | ||||||
|  | 		t.Fatalf("timeout") | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | # Compiled Object files, Static and Dynamic libs (Shared Objects) | ||||||
|  | *.o | ||||||
|  | *.a | ||||||
|  | *.so | ||||||
|  |  | ||||||
|  | # Folders | ||||||
|  | _obj | ||||||
|  | _test | ||||||
|  |  | ||||||
|  | # Architecture specific extensions/prefixes | ||||||
|  | *.[568vq] | ||||||
|  | [568vq].out | ||||||
|  |  | ||||||
|  | *.cgo1.go | ||||||
|  | *.cgo2.c | ||||||
|  | _cgo_defun.c | ||||||
|  | _cgo_gotypes.go | ||||||
|  | _cgo_export.* | ||||||
|  |  | ||||||
|  | _testmain.go | ||||||
|  |  | ||||||
|  | *.exe | ||||||
							
								
								
									
										3
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | language: go | ||||||
|  | go: | ||||||
|  |   - tip | ||||||
							
								
								
									
										20
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | The MIT License (MIT) | ||||||
|  |  | ||||||
|  | Copyright (c) 2014 Armon Dadgar | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||||
|  | this software and associated documentation files (the "Software"), to deal in | ||||||
|  | the Software without restriction, including without limitation the rights to | ||||||
|  | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||||
|  | the Software, and to permit persons to whom the Software is furnished to do so, | ||||||
|  | subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||||
|  | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||||
|  | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||||
|  | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||||
|  | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
							
								
								
									
										36
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | go-radix [](https://travis-ci.org/armon/go-radix) | ||||||
|  | ========= | ||||||
|  |  | ||||||
|  | Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree). | ||||||
|  | The package only provides a single `Tree` implementation, optimized for sparse nodes. | ||||||
|  |  | ||||||
|  | As a radix tree, it provides the following: | ||||||
|  |  * O(k) operations. In many cases, this can be faster than a hash table since | ||||||
|  |    the hash function is an O(k) operation, and hash tables have very poor cache locality. | ||||||
|  |  * Minimum / Maximum value lookups | ||||||
|  |  * Ordered iteration | ||||||
|  |  | ||||||
|  | Documentation | ||||||
|  | ============= | ||||||
|  |  | ||||||
|  | The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix). | ||||||
|  |  | ||||||
|  | Example | ||||||
|  | ======= | ||||||
|  |  | ||||||
|  | Below is a simple example of usage | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | // Create a tree | ||||||
|  | r := radix.New() | ||||||
|  | r.Insert("foo", 1) | ||||||
|  | r.Insert("bar", 2) | ||||||
|  | r.Insert("foobar", 2) | ||||||
|  |  | ||||||
|  | // Find the longest prefix match | ||||||
|  | m, _, _ := r.LongestPrefix("foozip") | ||||||
|  | if m != "foo" { | ||||||
|  |     panic("should be foo") | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
							
								
								
									
										498
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/radix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										498
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/radix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,498 @@ | |||||||
|  | package radix | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"sort" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // WalkFn is used when walking the tree. Takes a | ||||||
|  | // key and value, returning if iteration should | ||||||
|  | // be terminated. | ||||||
|  | type WalkFn func(s string, v interface{}) bool | ||||||
|  |  | ||||||
|  | // leafNode is used to represent a value | ||||||
|  | type leafNode struct { | ||||||
|  | 	key string | ||||||
|  | 	val interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // edge is used to represent an edge node | ||||||
|  | type edge struct { | ||||||
|  | 	label byte | ||||||
|  | 	node  *node | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type node struct { | ||||||
|  | 	// leaf is used to store possible leaf | ||||||
|  | 	leaf *leafNode | ||||||
|  |  | ||||||
|  | 	// prefix is the common prefix we ignore | ||||||
|  | 	prefix string | ||||||
|  |  | ||||||
|  | 	// Edges should be stored in-order for iteration. | ||||||
|  | 	// We avoid a fully materialized slice to save memory, | ||||||
|  | 	// since in most cases we expect to be sparse | ||||||
|  | 	edges edges | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *node) isLeaf() bool { | ||||||
|  | 	return n.leaf != nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *node) addEdge(e edge) { | ||||||
|  | 	n.edges = append(n.edges, e) | ||||||
|  | 	n.edges.Sort() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *node) replaceEdge(e edge) { | ||||||
|  | 	num := len(n.edges) | ||||||
|  | 	idx := sort.Search(num, func(i int) bool { | ||||||
|  | 		return n.edges[i].label >= e.label | ||||||
|  | 	}) | ||||||
|  | 	if idx < num && n.edges[idx].label == e.label { | ||||||
|  | 		n.edges[idx].node = e.node | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	panic("replacing missing edge") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *node) getEdge(label byte) *node { | ||||||
|  | 	num := len(n.edges) | ||||||
|  | 	idx := sort.Search(num, func(i int) bool { | ||||||
|  | 		return n.edges[i].label >= label | ||||||
|  | 	}) | ||||||
|  | 	if idx < num && n.edges[idx].label == label { | ||||||
|  | 		return n.edges[idx].node | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *node) delEdge(label byte) { | ||||||
|  | 	num := len(n.edges) | ||||||
|  | 	idx := sort.Search(num, func(i int) bool { | ||||||
|  | 		return n.edges[i].label >= label | ||||||
|  | 	}) | ||||||
|  | 	if idx < num && n.edges[idx].label == label { | ||||||
|  | 		copy(n.edges[idx:], n.edges[idx+1:]) | ||||||
|  | 		n.edges[len(n.edges)-1] = edge{} | ||||||
|  | 		n.edges = n.edges[:len(n.edges)-1] | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type edges []edge | ||||||
|  |  | ||||||
|  | func (e edges) Len() int { | ||||||
|  | 	return len(e) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e edges) Less(i, j int) bool { | ||||||
|  | 	return e[i].label < e[j].label | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e edges) Swap(i, j int) { | ||||||
|  | 	e[i], e[j] = e[j], e[i] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e edges) Sort() { | ||||||
|  | 	sort.Sort(e) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Tree implements a radix tree. This can be treated as a | ||||||
|  | // Dictionary abstract data type. The main advantage over | ||||||
|  | // a standard hash map is prefix-based lookups and | ||||||
|  | // ordered iteration, | ||||||
|  | type Tree struct { | ||||||
|  | 	root *node | ||||||
|  | 	size int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New returns an empty Tree | ||||||
|  | func New() *Tree { | ||||||
|  | 	return NewFromMap(nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewFromMap returns a new tree containing the keys | ||||||
|  | // from an existing map | ||||||
|  | func NewFromMap(m map[string]interface{}) *Tree { | ||||||
|  | 	t := &Tree{root: &node{}} | ||||||
|  | 	for k, v := range m { | ||||||
|  | 		t.Insert(k, v) | ||||||
|  | 	} | ||||||
|  | 	return t | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Len is used to return the number of elements in the tree | ||||||
|  | func (t *Tree) Len() int { | ||||||
|  | 	return t.size | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // longestPrefix finds the length of the shared prefix | ||||||
|  | // of two strings | ||||||
|  | func longestPrefix(k1, k2 string) int { | ||||||
|  | 	max := len(k1) | ||||||
|  | 	if l := len(k2); l < max { | ||||||
|  | 		max = l | ||||||
|  | 	} | ||||||
|  | 	var i int | ||||||
|  | 	for i = 0; i < max; i++ { | ||||||
|  | 		if k1[i] != k2[i] { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return i | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Insert is used to add a newentry or update | ||||||
|  | // an existing entry. Returns if updated. | ||||||
|  | func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { | ||||||
|  | 	var parent *node | ||||||
|  | 	n := t.root | ||||||
|  | 	search := s | ||||||
|  | 	for { | ||||||
|  | 		// Handle key exhaution | ||||||
|  | 		if len(search) == 0 { | ||||||
|  | 			if n.isLeaf() { | ||||||
|  | 				old := n.leaf.val | ||||||
|  | 				n.leaf.val = v | ||||||
|  | 				return old, true | ||||||
|  | 			} else { | ||||||
|  | 				n.leaf = &leafNode{ | ||||||
|  | 					key: s, | ||||||
|  | 					val: v, | ||||||
|  | 				} | ||||||
|  | 				t.size++ | ||||||
|  | 				return nil, false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Look for the edge | ||||||
|  | 		parent = n | ||||||
|  | 		n = n.getEdge(search[0]) | ||||||
|  |  | ||||||
|  | 		// No edge, create one | ||||||
|  | 		if n == nil { | ||||||
|  | 			e := edge{ | ||||||
|  | 				label: search[0], | ||||||
|  | 				node: &node{ | ||||||
|  | 					leaf: &leafNode{ | ||||||
|  | 						key: s, | ||||||
|  | 						val: v, | ||||||
|  | 					}, | ||||||
|  | 					prefix: search, | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 			parent.addEdge(e) | ||||||
|  | 			t.size++ | ||||||
|  | 			return nil, false | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Determine longest prefix of the search key on match | ||||||
|  | 		commonPrefix := longestPrefix(search, n.prefix) | ||||||
|  | 		if commonPrefix == len(n.prefix) { | ||||||
|  | 			search = search[commonPrefix:] | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Split the node | ||||||
|  | 		t.size++ | ||||||
|  | 		child := &node{ | ||||||
|  | 			prefix: search[:commonPrefix], | ||||||
|  | 		} | ||||||
|  | 		parent.replaceEdge(edge{ | ||||||
|  | 			label: search[0], | ||||||
|  | 			node:  child, | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		// Restore the existing node | ||||||
|  | 		child.addEdge(edge{ | ||||||
|  | 			label: n.prefix[commonPrefix], | ||||||
|  | 			node:  n, | ||||||
|  | 		}) | ||||||
|  | 		n.prefix = n.prefix[commonPrefix:] | ||||||
|  |  | ||||||
|  | 		// Create a new leaf node | ||||||
|  | 		leaf := &leafNode{ | ||||||
|  | 			key: s, | ||||||
|  | 			val: v, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// If the new key is a subset, add to to this node | ||||||
|  | 		search = search[commonPrefix:] | ||||||
|  | 		if len(search) == 0 { | ||||||
|  | 			child.leaf = leaf | ||||||
|  | 			return nil, false | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Create a new edge for the node | ||||||
|  | 		child.addEdge(edge{ | ||||||
|  | 			label: search[0], | ||||||
|  | 			node: &node{ | ||||||
|  | 				leaf:   leaf, | ||||||
|  | 				prefix: search, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
|  | 	return nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Delete is used to delete a key, returning the previous | ||||||
|  | // value and if it was deleted | ||||||
|  | func (t *Tree) Delete(s string) (interface{}, bool) { | ||||||
|  | 	var parent *node | ||||||
|  | 	var label byte | ||||||
|  | 	n := t.root | ||||||
|  | 	search := s | ||||||
|  | 	for { | ||||||
|  | 		// Check for key exhaution | ||||||
|  | 		if len(search) == 0 { | ||||||
|  | 			if !n.isLeaf() { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			goto DELETE | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Look for an edge | ||||||
|  | 		parent = n | ||||||
|  | 		label = search[0] | ||||||
|  | 		n = n.getEdge(label) | ||||||
|  | 		if n == nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Consume the search prefix | ||||||
|  | 		if strings.HasPrefix(search, n.prefix) { | ||||||
|  | 			search = search[len(n.prefix):] | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, false | ||||||
|  |  | ||||||
|  | DELETE: | ||||||
|  | 	// Delete the leaf | ||||||
|  | 	leaf := n.leaf | ||||||
|  | 	n.leaf = nil | ||||||
|  | 	t.size-- | ||||||
|  |  | ||||||
|  | 	// Check if we should delete this node from the parent | ||||||
|  | 	if parent != nil && len(n.edges) == 0 { | ||||||
|  | 		parent.delEdge(label) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if we should merge this node | ||||||
|  | 	if n != t.root && len(n.edges) == 1 { | ||||||
|  | 		n.mergeChild() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if we should merge the parent's other child | ||||||
|  | 	if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { | ||||||
|  | 		parent.mergeChild() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return leaf.val, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *node) mergeChild() { | ||||||
|  | 	e := n.edges[0] | ||||||
|  | 	child := e.node | ||||||
|  | 	n.prefix = n.prefix + child.prefix | ||||||
|  | 	n.leaf = child.leaf | ||||||
|  | 	n.edges = child.edges | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get is used to lookup a specific key, returning | ||||||
|  | // the value and if it was found | ||||||
|  | func (t *Tree) Get(s string) (interface{}, bool) { | ||||||
|  | 	n := t.root | ||||||
|  | 	search := s | ||||||
|  | 	for { | ||||||
|  | 		// Check for key exhaution | ||||||
|  | 		if len(search) == 0 { | ||||||
|  | 			if n.isLeaf() { | ||||||
|  | 				return n.leaf.val, true | ||||||
|  | 			} | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Look for an edge | ||||||
|  | 		n = n.getEdge(search[0]) | ||||||
|  | 		if n == nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Consume the search prefix | ||||||
|  | 		if strings.HasPrefix(search, n.prefix) { | ||||||
|  | 			search = search[len(n.prefix):] | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LongestPrefix is like Get, but instead of an | ||||||
|  | // exact match, it will return the longest prefix match. | ||||||
|  | func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { | ||||||
|  | 	var last *leafNode | ||||||
|  | 	n := t.root | ||||||
|  | 	search := s | ||||||
|  | 	for { | ||||||
|  | 		// Look for a leaf node | ||||||
|  | 		if n.isLeaf() { | ||||||
|  | 			last = n.leaf | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Check for key exhaution | ||||||
|  | 		if len(search) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Look for an edge | ||||||
|  | 		n = n.getEdge(search[0]) | ||||||
|  | 		if n == nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Consume the search prefix | ||||||
|  | 		if strings.HasPrefix(search, n.prefix) { | ||||||
|  | 			search = search[len(n.prefix):] | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if last != nil { | ||||||
|  | 		return last.key, last.val, true | ||||||
|  | 	} | ||||||
|  | 	return "", nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Minimum is used to return the minimum value in the tree | ||||||
|  | func (t *Tree) Minimum() (string, interface{}, bool) { | ||||||
|  | 	n := t.root | ||||||
|  | 	for { | ||||||
|  | 		if n.isLeaf() { | ||||||
|  | 			return n.leaf.key, n.leaf.val, true | ||||||
|  | 		} | ||||||
|  | 		if len(n.edges) > 0 { | ||||||
|  | 			n = n.edges[0].node | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "", nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Maximum is used to return the maximum value in the tree | ||||||
|  | func (t *Tree) Maximum() (string, interface{}, bool) { | ||||||
|  | 	n := t.root | ||||||
|  | 	for { | ||||||
|  | 		if num := len(n.edges); num > 0 { | ||||||
|  | 			n = n.edges[num-1].node | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if n.isLeaf() { | ||||||
|  | 			return n.leaf.key, n.leaf.val, true | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "", nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Walk is used to walk the tree | ||||||
|  | func (t *Tree) Walk(fn WalkFn) { | ||||||
|  | 	recursiveWalk(t.root, fn) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WalkPrefix is used to walk the tree under a prefix | ||||||
|  | func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { | ||||||
|  | 	n := t.root | ||||||
|  | 	search := prefix | ||||||
|  | 	for { | ||||||
|  | 		// Check for key exhaution | ||||||
|  | 		if len(search) == 0 { | ||||||
|  | 			recursiveWalk(n, fn) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Look for an edge | ||||||
|  | 		n = n.getEdge(search[0]) | ||||||
|  | 		if n == nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Consume the search prefix | ||||||
|  | 		if strings.HasPrefix(search, n.prefix) { | ||||||
|  | 			search = search[len(n.prefix):] | ||||||
|  |  | ||||||
|  | 		} else if strings.HasPrefix(n.prefix, search) { | ||||||
|  | 			// Child may be under our search prefix | ||||||
|  | 			recursiveWalk(n, fn) | ||||||
|  | 			return | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WalkPath is used to walk the tree, but only visiting nodes | ||||||
|  | // from the root down to a given leaf. Where WalkPrefix walks | ||||||
|  | // all the entries *under* the given prefix, this walks the | ||||||
|  | // entries *above* the given prefix. | ||||||
|  | func (t *Tree) WalkPath(path string, fn WalkFn) { | ||||||
|  | 	n := t.root | ||||||
|  | 	search := path | ||||||
|  | 	for { | ||||||
|  | 		// Visit the leaf values if any | ||||||
|  | 		if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Check for key exhaution | ||||||
|  | 		if len(search) == 0 { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Look for an edge | ||||||
|  | 		n = n.getEdge(search[0]) | ||||||
|  | 		if n == nil { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Consume the search prefix | ||||||
|  | 		if strings.HasPrefix(search, n.prefix) { | ||||||
|  | 			search = search[len(n.prefix):] | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // recursiveWalk is used to do a pre-order walk of a node | ||||||
|  | // recursively. Returns true if the walk should be aborted | ||||||
|  | func recursiveWalk(n *node, fn WalkFn) bool { | ||||||
|  | 	// Visit the leaf values if any | ||||||
|  | 	if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Recurse on the children | ||||||
|  | 	for _, e := range n.edges { | ||||||
|  | 		if recursiveWalk(e.node, fn) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ToMap is used to walk the tree and convert it into a map | ||||||
|  | func (t *Tree) ToMap() map[string]interface{} { | ||||||
|  | 	out := make(map[string]interface{}, t.size) | ||||||
|  | 	t.Walk(func(k string, v interface{}) bool { | ||||||
|  | 		out[k] = v | ||||||
|  | 		return false | ||||||
|  | 	}) | ||||||
|  | 	return out | ||||||
|  | } | ||||||
							
								
								
									
										319
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/radix_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								Godeps/_workspace/src/github.com/armon/go-radix/radix_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,319 @@ | |||||||
|  | package radix | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	crand "crypto/rand" | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"sort" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestRadix(t *testing.T) { | ||||||
|  | 	var min, max string | ||||||
|  | 	inp := make(map[string]interface{}) | ||||||
|  | 	for i := 0; i < 1000; i++ { | ||||||
|  | 		gen := generateUUID() | ||||||
|  | 		inp[gen] = i | ||||||
|  | 		if gen < min || i == 0 { | ||||||
|  | 			min = gen | ||||||
|  | 		} | ||||||
|  | 		if gen > max || i == 0 { | ||||||
|  | 			max = gen | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r := NewFromMap(inp) | ||||||
|  | 	if r.Len() != len(inp) { | ||||||
|  | 		t.Fatalf("bad length: %v %v", r.Len(), len(inp)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r.Walk(func(k string, v interface{}) bool { | ||||||
|  | 		println(k) | ||||||
|  | 		return false | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	for k, v := range inp { | ||||||
|  | 		out, ok := r.Get(k) | ||||||
|  | 		if !ok { | ||||||
|  | 			t.Fatalf("missing key: %v", k) | ||||||
|  | 		} | ||||||
|  | 		if out != v { | ||||||
|  | 			t.Fatalf("value mis-match: %v %v", out, v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check min and max | ||||||
|  | 	outMin, _, _ := r.Minimum() | ||||||
|  | 	if outMin != min { | ||||||
|  | 		t.Fatalf("bad minimum: %v %v", outMin, min) | ||||||
|  | 	} | ||||||
|  | 	outMax, _, _ := r.Maximum() | ||||||
|  | 	if outMax != max { | ||||||
|  | 		t.Fatalf("bad maximum: %v %v", outMax, max) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for k, v := range inp { | ||||||
|  | 		out, ok := r.Delete(k) | ||||||
|  | 		if !ok { | ||||||
|  | 			t.Fatalf("missing key: %v", k) | ||||||
|  | 		} | ||||||
|  | 		if out != v { | ||||||
|  | 			t.Fatalf("value mis-match: %v %v", out, v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if r.Len() != 0 { | ||||||
|  | 		t.Fatalf("bad length: %v", r.Len()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestRoot(t *testing.T) { | ||||||
|  | 	r := New() | ||||||
|  | 	_, ok := r.Delete("") | ||||||
|  | 	if ok { | ||||||
|  | 		t.Fatalf("bad") | ||||||
|  | 	} | ||||||
|  | 	_, ok = r.Insert("", true) | ||||||
|  | 	if ok { | ||||||
|  | 		t.Fatalf("bad") | ||||||
|  | 	} | ||||||
|  | 	val, ok := r.Get("") | ||||||
|  | 	if !ok || val != true { | ||||||
|  | 		t.Fatalf("bad: %v", val) | ||||||
|  | 	} | ||||||
|  | 	val, ok = r.Delete("") | ||||||
|  | 	if !ok || val != true { | ||||||
|  | 		t.Fatalf("bad: %v", val) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDelete(t *testing.T) { | ||||||
|  |  | ||||||
|  | 	r := New() | ||||||
|  |  | ||||||
|  | 	s := []string{"", "A", "AB"} | ||||||
|  |  | ||||||
|  | 	for _, ss := range s { | ||||||
|  | 		r.Insert(ss, true) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, ss := range s { | ||||||
|  | 		_, ok := r.Delete(ss) | ||||||
|  | 		if !ok { | ||||||
|  | 			t.Fatalf("bad %q", ss) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestLongestPrefix(t *testing.T) { | ||||||
|  | 	r := New() | ||||||
|  |  | ||||||
|  | 	keys := []string{ | ||||||
|  | 		"", | ||||||
|  | 		"foo", | ||||||
|  | 		"foobar", | ||||||
|  | 		"foobarbaz", | ||||||
|  | 		"foobarbazzip", | ||||||
|  | 		"foozip", | ||||||
|  | 	} | ||||||
|  | 	for _, k := range keys { | ||||||
|  | 		r.Insert(k, nil) | ||||||
|  | 	} | ||||||
|  | 	if r.Len() != len(keys) { | ||||||
|  | 		t.Fatalf("bad len: %v %v", r.Len(), len(keys)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	type exp struct { | ||||||
|  | 		inp string | ||||||
|  | 		out string | ||||||
|  | 	} | ||||||
|  | 	cases := []exp{ | ||||||
|  | 		{"a", ""}, | ||||||
|  | 		{"abc", ""}, | ||||||
|  | 		{"fo", ""}, | ||||||
|  | 		{"foo", "foo"}, | ||||||
|  | 		{"foob", "foo"}, | ||||||
|  | 		{"foobar", "foobar"}, | ||||||
|  | 		{"foobarba", "foobar"}, | ||||||
|  | 		{"foobarbaz", "foobarbaz"}, | ||||||
|  | 		{"foobarbazzi", "foobarbaz"}, | ||||||
|  | 		{"foobarbazzip", "foobarbazzip"}, | ||||||
|  | 		{"foozi", "foo"}, | ||||||
|  | 		{"foozip", "foozip"}, | ||||||
|  | 		{"foozipzap", "foozip"}, | ||||||
|  | 	} | ||||||
|  | 	for _, test := range cases { | ||||||
|  | 		m, _, ok := r.LongestPrefix(test.inp) | ||||||
|  | 		if !ok { | ||||||
|  | 			t.Fatalf("no match: %v", test) | ||||||
|  | 		} | ||||||
|  | 		if m != test.out { | ||||||
|  | 			t.Fatalf("mis-match: %v %v", m, test) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestWalkPrefix(t *testing.T) { | ||||||
|  | 	r := New() | ||||||
|  |  | ||||||
|  | 	keys := []string{ | ||||||
|  | 		"foobar", | ||||||
|  | 		"foo/bar/baz", | ||||||
|  | 		"foo/baz/bar", | ||||||
|  | 		"foo/zip/zap", | ||||||
|  | 		"zipzap", | ||||||
|  | 	} | ||||||
|  | 	for _, k := range keys { | ||||||
|  | 		r.Insert(k, nil) | ||||||
|  | 	} | ||||||
|  | 	if r.Len() != len(keys) { | ||||||
|  | 		t.Fatalf("bad len: %v %v", r.Len(), len(keys)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	type exp struct { | ||||||
|  | 		inp string | ||||||
|  | 		out []string | ||||||
|  | 	} | ||||||
|  | 	cases := []exp{ | ||||||
|  | 		exp{ | ||||||
|  | 			"f", | ||||||
|  | 			[]string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo", | ||||||
|  | 			[]string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foob", | ||||||
|  | 			[]string{"foobar"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/", | ||||||
|  | 			[]string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/b", | ||||||
|  | 			[]string{"foo/bar/baz", "foo/baz/bar"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/ba", | ||||||
|  | 			[]string{"foo/bar/baz", "foo/baz/bar"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/bar", | ||||||
|  | 			[]string{"foo/bar/baz"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/bar/baz", | ||||||
|  | 			[]string{"foo/bar/baz"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/bar/bazoo", | ||||||
|  | 			[]string{}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"z", | ||||||
|  | 			[]string{"zipzap"}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, test := range cases { | ||||||
|  | 		out := []string{} | ||||||
|  | 		fn := func(s string, v interface{}) bool { | ||||||
|  | 			out = append(out, s) | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		r.WalkPrefix(test.inp, fn) | ||||||
|  | 		sort.Strings(out) | ||||||
|  | 		sort.Strings(test.out) | ||||||
|  | 		if !reflect.DeepEqual(out, test.out) { | ||||||
|  | 			t.Fatalf("mis-match: %v %v", out, test.out) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestWalkPath(t *testing.T) { | ||||||
|  | 	r := New() | ||||||
|  |  | ||||||
|  | 	keys := []string{ | ||||||
|  | 		"foo", | ||||||
|  | 		"foo/bar", | ||||||
|  | 		"foo/bar/baz", | ||||||
|  | 		"foo/baz/bar", | ||||||
|  | 		"foo/zip/zap", | ||||||
|  | 		"zipzap", | ||||||
|  | 	} | ||||||
|  | 	for _, k := range keys { | ||||||
|  | 		r.Insert(k, nil) | ||||||
|  | 	} | ||||||
|  | 	if r.Len() != len(keys) { | ||||||
|  | 		t.Fatalf("bad len: %v %v", r.Len(), len(keys)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	type exp struct { | ||||||
|  | 		inp string | ||||||
|  | 		out []string | ||||||
|  | 	} | ||||||
|  | 	cases := []exp{ | ||||||
|  | 		exp{ | ||||||
|  | 			"f", | ||||||
|  | 			[]string{}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo", | ||||||
|  | 			[]string{"foo"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/", | ||||||
|  | 			[]string{"foo"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/ba", | ||||||
|  | 			[]string{"foo"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/bar", | ||||||
|  | 			[]string{"foo", "foo/bar"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/bar/baz", | ||||||
|  | 			[]string{"foo", "foo/bar", "foo/bar/baz"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"foo/bar/bazoo", | ||||||
|  | 			[]string{"foo", "foo/bar", "foo/bar/baz"}, | ||||||
|  | 		}, | ||||||
|  | 		exp{ | ||||||
|  | 			"z", | ||||||
|  | 			[]string{}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, test := range cases { | ||||||
|  | 		out := []string{} | ||||||
|  | 		fn := func(s string, v interface{}) bool { | ||||||
|  | 			out = append(out, s) | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		r.WalkPath(test.inp, fn) | ||||||
|  | 		sort.Strings(out) | ||||||
|  | 		sort.Strings(test.out) | ||||||
|  | 		if !reflect.DeepEqual(out, test.out) { | ||||||
|  | 			t.Fatalf("mis-match: %v %v", out, test.out) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // generateUUID is used to generate a random UUID | ||||||
|  | func generateUUID() string { | ||||||
|  | 	buf := make([]byte, 16) | ||||||
|  | 	if _, err := crand.Read(buf); err != nil { | ||||||
|  | 		panic(fmt.Errorf("failed to read random bytes: %v", err)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", | ||||||
|  | 		buf[0:4], | ||||||
|  | 		buf[4:6], | ||||||
|  | 		buf[6:8], | ||||||
|  | 		buf[8:10], | ||||||
|  | 		buf[10:16]) | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | .DS_Store | ||||||
|  | .DS_Store? | ||||||
|  | ._* | ||||||
|  | .Spotlight-V100 | ||||||
|  | .Trashes | ||||||
|  | Icon? | ||||||
|  | ehthumbs.db | ||||||
|  | Thumbs.db | ||||||
							
								
								
									
										10
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | sudo: false | ||||||
|  | language: go | ||||||
|  | go: | ||||||
|  |   - 1.2 | ||||||
|  |   - 1.3 | ||||||
|  |   - 1.4 | ||||||
|  |   - tip | ||||||
|  |  | ||||||
|  | before_script: | ||||||
|  |   - mysql -e 'create database gotest;' | ||||||
							
								
								
									
										41
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | # This is the official list of Go-MySQL-Driver authors for copyright purposes. | ||||||
|  |  | ||||||
|  | # If you are submitting a patch, please add your name or the name of the | ||||||
|  | # organization which holds the copyright to this list in alphabetical order. | ||||||
|  |  | ||||||
|  | # Names should be added to this file as | ||||||
|  | #	Name <email address> | ||||||
|  | # The email address is not required for organizations. | ||||||
|  | # Please keep the list sorted. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Individual Persons | ||||||
|  |  | ||||||
|  | Aaron Hopkins <go-sql-driver at die.net> | ||||||
|  | Arne Hormann <arnehormann at gmail.com> | ||||||
|  | Carlos Nieto <jose.carlos at menteslibres.net> | ||||||
|  | Chris Moos <chris at tech9computers.com> | ||||||
|  | DisposaBoy <disposaboy at dby.me> | ||||||
|  | Frederick Mayle <frederickmayle at gmail.com> | ||||||
|  | Gustavo Kristic <gkristic at gmail.com> | ||||||
|  | Hanno Braun <mail at hannobraun.com> | ||||||
|  | Henri Yandell <flamefew at gmail.com> | ||||||
|  | INADA Naoki <songofacandy at gmail.com> | ||||||
|  | James Harr <james.harr at gmail.com> | ||||||
|  | Jian Zhen <zhenjl at gmail.com> | ||||||
|  | Julien Schmidt <go-sql-driver at julienschmidt.com> | ||||||
|  | Kamil Dziedzic <kamil at klecza.pl> | ||||||
|  | Leonardo YongUk Kim <dalinaum at gmail.com> | ||||||
|  | Lucas Liu <extrafliu at gmail.com> | ||||||
|  | Luke Scott <luke at webconnex.com> | ||||||
|  | Michael Woolnough <michael.woolnough at gmail.com> | ||||||
|  | Nicola Peduzzi <thenikso at gmail.com> | ||||||
|  | Runrioter Wung <runrioter at gmail.com> | ||||||
|  | Xiaobing Jiang <s7v7nislands at gmail.com> | ||||||
|  | Xiuming Chen <cc at cxm.cc> | ||||||
|  |  | ||||||
|  | # Organizations | ||||||
|  |  | ||||||
|  | Barracuda Networks, Inc. | ||||||
|  | Google Inc. | ||||||
|  | Stripe Inc. | ||||||
							
								
								
									
										92
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | ## HEAD | ||||||
|  |  | ||||||
|  | Changes: | ||||||
|  |  | ||||||
|  |  - Go 1.1 is no longer supported | ||||||
|  |  - Use decimals field from MySQL to format time types (#249) | ||||||
|  |  - Buffer optimizations (#269) | ||||||
|  |  - TLS ServerName defaults to the host (#283) | ||||||
|  |  | ||||||
|  | Bugfixes: | ||||||
|  |  | ||||||
|  |  - Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249) | ||||||
|  |  - Fixed handling of queries without columns and rows (#255) | ||||||
|  |  - Fixed a panic when SetKeepAlive() failed (#298) | ||||||
|  |  | ||||||
|  | New Features: | ||||||
|  |  - Support for returning table alias on Columns() (#289) | ||||||
|  |  - Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Version 1.2 (2014-06-03) | ||||||
|  |  | ||||||
|  | Changes: | ||||||
|  |  | ||||||
|  |  - We switched back to a "rolling release". `go get` installs the current master branch again | ||||||
|  |  - Version v1 of the driver will not be maintained anymore. Go 1.0 is no longer supported by this driver | ||||||
|  |  - Exported errors to allow easy checking from application code | ||||||
|  |  - Enabled TCP Keepalives on TCP connections | ||||||
|  |  - Optimized INFILE handling (better buffer size calculation, lazy init, ...) | ||||||
|  |  - The DSN parser also checks for a missing separating slash | ||||||
|  |  - Faster binary date / datetime to string formatting | ||||||
|  |  - Also exported the MySQLWarning type | ||||||
|  |  - mysqlConn.Close returns the first error encountered instead of ignoring all errors | ||||||
|  |  - writePacket() automatically writes the packet size to the header | ||||||
|  |  - readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets | ||||||
|  |  | ||||||
|  | New Features: | ||||||
|  |  | ||||||
|  |  - `RegisterDial` allows the usage of a custom dial function to establish the network connection | ||||||
|  |  - Setting the connection collation is possible with the `collation` DSN parameter. This parameter should be preferred over the `charset` parameter | ||||||
|  |  - Logging of critical errors is configurable with `SetLogger` | ||||||
|  |  - Google CloudSQL support | ||||||
|  |  | ||||||
|  | Bugfixes: | ||||||
|  |  | ||||||
|  |  - Allow more than 32 parameters in prepared statements | ||||||
|  |  - Various old_password fixes | ||||||
|  |  - Fixed TestConcurrent test to pass Go's race detection | ||||||
|  |  - Fixed appendLengthEncodedInteger for large numbers | ||||||
|  |  - Renamed readLengthEnodedString to readLengthEncodedString and skipLengthEnodedString to skipLengthEncodedString (fixed typo) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Version 1.1 (2013-11-02) | ||||||
|  |  | ||||||
|  | Changes: | ||||||
|  |  | ||||||
|  |   - Go-MySQL-Driver now requires Go 1.1 | ||||||
|  |   - Connections now use the collation `utf8_general_ci` by default. Adding `&charset=UTF8` to the DSN should not be necessary anymore | ||||||
|  |   - Made closing rows and connections error tolerant. This allows for example deferring rows.Close() without checking for errors | ||||||
|  |   - `[]byte(nil)` is now treated as a NULL value. Before, it was treated like an empty string / `[]byte("")` | ||||||
|  |   - DSN parameter values must now be url.QueryEscape'ed. This allows text values to contain special characters, such as '&'. | ||||||
|  |   - Use the IO buffer also for writing. This results in zero allocations (by the driver) for most queries | ||||||
|  |   - Optimized the buffer for reading | ||||||
|  |   - stmt.Query now caches column metadata | ||||||
|  |   - New Logo | ||||||
|  |   - Changed the copyright header to include all contributors | ||||||
|  |   - Improved the LOAD INFILE documentation | ||||||
|  |   - The driver struct is now exported to make the driver directly accessible | ||||||
|  |   - Refactored the driver tests | ||||||
|  |   - Added more benchmarks and moved all to a separate file | ||||||
|  |   - Other small refactoring | ||||||
|  |  | ||||||
|  | New Features: | ||||||
|  |  | ||||||
|  |   - Added *old_passwords* support: Required in some cases, but must be enabled by adding `allowOldPasswords=true` to the DSN since it is insecure | ||||||
|  |   - Added a `clientFoundRows` parameter: Return the number of matching rows instead of the number of rows changed on UPDATEs | ||||||
|  |   - Added TLS/SSL support: Use a TLS/SSL encrypted connection to the server. Custom TLS configs can be registered and used | ||||||
|  |  | ||||||
|  | Bugfixes: | ||||||
|  |  | ||||||
|  |   - Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification | ||||||
|  |   - Convert to DB timezone when inserting `time.Time` | ||||||
|  |   - Splitted packets (more than 16MB) are now merged correctly | ||||||
|  |   - Fixed false positive `io.EOF` errors when the data was fully read | ||||||
|  |   - Avoid panics on reuse of closed connections | ||||||
|  |   - Fixed empty string producing false nil values | ||||||
|  |   - Fixed sign byte for positive TIME fields | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Version 1.0 (2013-05-14) | ||||||
|  |  | ||||||
|  | Initial Release | ||||||
							
								
								
									
										40
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | # Contributing Guidelines | ||||||
|  |  | ||||||
|  | ## Reporting Issues | ||||||
|  |  | ||||||
|  | Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed). | ||||||
|  |  | ||||||
|  | Please provide the following minimum information: | ||||||
|  | * Your Go-MySQL-Driver version (or git SHA) | ||||||
|  | * Your Go version (run `go version` in your console) | ||||||
|  | * A detailed issue description | ||||||
|  | * Error Log if present | ||||||
|  | * If possible, a short example | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Contributing Code | ||||||
|  |  | ||||||
|  | By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file. | ||||||
|  | Don't forget to add yourself to the AUTHORS file. | ||||||
|  |  | ||||||
|  | ### Pull Requests Checklist | ||||||
|  |  | ||||||
|  | Please check the following points before submitting your pull request: | ||||||
|  | - [x] Code compiles correctly | ||||||
|  | - [x] Created tests, if possible | ||||||
|  | - [x] All tests pass | ||||||
|  | - [x] Extended the README / documentation, if necessary | ||||||
|  | - [x] Added yourself to the AUTHORS file | ||||||
|  |  | ||||||
|  | ### Code Review | ||||||
|  |  | ||||||
|  | Everyone is invited to review and comment on pull requests. | ||||||
|  | If it looks fine to you, comment with "LGTM" (Looks good to me). | ||||||
|  |  | ||||||
|  | If changes are required, notice the reviewers with "PTAL" (Please take another look) after committing the fixes. | ||||||
|  |  | ||||||
|  | Before merging the Pull Request, at least one [team member](https://github.com/go-sql-driver?tab=members) must have commented with "LGTM". | ||||||
|  |  | ||||||
|  | ## Development Ideas | ||||||
|  |  | ||||||
|  | If you are looking for ideas for code contributions, please check our [Development Ideas](https://github.com/go-sql-driver/mysql/wiki/Development-Ideas) Wiki page. | ||||||
							
								
								
									
										373
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,373 @@ | |||||||
|  | Mozilla Public License Version 2.0 | ||||||
|  | ================================== | ||||||
|  |  | ||||||
|  | 1. Definitions | ||||||
|  | -------------- | ||||||
|  |  | ||||||
|  | 1.1. "Contributor" | ||||||
|  |     means each individual or legal entity that creates, contributes to | ||||||
|  |     the creation of, or owns Covered Software. | ||||||
|  |  | ||||||
|  | 1.2. "Contributor Version" | ||||||
|  |     means the combination of the Contributions of others (if any) used | ||||||
|  |     by a Contributor and that particular Contributor's Contribution. | ||||||
|  |  | ||||||
|  | 1.3. "Contribution" | ||||||
|  |     means Covered Software of a particular Contributor. | ||||||
|  |  | ||||||
|  | 1.4. "Covered Software" | ||||||
|  |     means Source Code Form to which the initial Contributor has attached | ||||||
|  |     the notice in Exhibit A, the Executable Form of such Source Code | ||||||
|  |     Form, and Modifications of such Source Code Form, in each case | ||||||
|  |     including portions thereof. | ||||||
|  |  | ||||||
|  | 1.5. "Incompatible With Secondary Licenses" | ||||||
|  |     means | ||||||
|  |  | ||||||
|  |     (a) that the initial Contributor has attached the notice described | ||||||
|  |         in Exhibit B to the Covered Software; or | ||||||
|  |  | ||||||
|  |     (b) that the Covered Software was made available under the terms of | ||||||
|  |         version 1.1 or earlier of the License, but not also under the | ||||||
|  |         terms of a Secondary License. | ||||||
|  |  | ||||||
|  | 1.6. "Executable Form" | ||||||
|  |     means any form of the work other than Source Code Form. | ||||||
|  |  | ||||||
|  | 1.7. "Larger Work" | ||||||
|  |     means a work that combines Covered Software with other material, in  | ||||||
|  |     a separate file or files, that is not Covered Software. | ||||||
|  |  | ||||||
|  | 1.8. "License" | ||||||
|  |     means this document. | ||||||
|  |  | ||||||
|  | 1.9. "Licensable" | ||||||
|  |     means having the right to grant, to the maximum extent possible, | ||||||
|  |     whether at the time of the initial grant or subsequently, any and | ||||||
|  |     all of the rights conveyed by this License. | ||||||
|  |  | ||||||
|  | 1.10. "Modifications" | ||||||
|  |     means any of the following: | ||||||
|  |  | ||||||
|  |     (a) any file in Source Code Form that results from an addition to, | ||||||
|  |         deletion from, or modification of the contents of Covered | ||||||
|  |         Software; or | ||||||
|  |  | ||||||
|  |     (b) any new file in Source Code Form that contains any Covered | ||||||
|  |         Software. | ||||||
|  |  | ||||||
|  | 1.11. "Patent Claims" of a Contributor | ||||||
|  |     means any patent claim(s), including without limitation, method, | ||||||
|  |     process, and apparatus claims, in any patent Licensable by such | ||||||
|  |     Contributor that would be infringed, but for the grant of the | ||||||
|  |     License, by the making, using, selling, offering for sale, having | ||||||
|  |     made, import, or transfer of either its Contributions or its | ||||||
|  |     Contributor Version. | ||||||
|  |  | ||||||
|  | 1.12. "Secondary License" | ||||||
|  |     means either the GNU General Public License, Version 2.0, the GNU | ||||||
|  |     Lesser General Public License, Version 2.1, the GNU Affero General | ||||||
|  |     Public License, Version 3.0, or any later versions of those | ||||||
|  |     licenses. | ||||||
|  |  | ||||||
|  | 1.13. "Source Code Form" | ||||||
|  |     means the form of the work preferred for making modifications. | ||||||
|  |  | ||||||
|  | 1.14. "You" (or "Your") | ||||||
|  |     means an individual or a legal entity exercising rights under this | ||||||
|  |     License. For legal entities, "You" includes any entity that | ||||||
|  |     controls, is controlled by, or is under common control with You. For | ||||||
|  |     purposes of this definition, "control" means (a) the power, direct | ||||||
|  |     or indirect, to cause the direction or management of such entity, | ||||||
|  |     whether by contract or otherwise, or (b) ownership of more than | ||||||
|  |     fifty percent (50%) of the outstanding shares or beneficial | ||||||
|  |     ownership of such entity. | ||||||
|  |  | ||||||
|  | 2. License Grants and Conditions | ||||||
|  | -------------------------------- | ||||||
|  |  | ||||||
|  | 2.1. Grants | ||||||
|  |  | ||||||
|  | Each Contributor hereby grants You a world-wide, royalty-free, | ||||||
|  | non-exclusive license: | ||||||
|  |  | ||||||
|  | (a) under intellectual property rights (other than patent or trademark) | ||||||
|  |     Licensable by such Contributor to use, reproduce, make available, | ||||||
|  |     modify, display, perform, distribute, and otherwise exploit its | ||||||
|  |     Contributions, either on an unmodified basis, with Modifications, or | ||||||
|  |     as part of a Larger Work; and | ||||||
|  |  | ||||||
|  | (b) under Patent Claims of such Contributor to make, use, sell, offer | ||||||
|  |     for sale, have made, import, and otherwise transfer either its | ||||||
|  |     Contributions or its Contributor Version. | ||||||
|  |  | ||||||
|  | 2.2. Effective Date | ||||||
|  |  | ||||||
|  | The licenses granted in Section 2.1 with respect to any Contribution | ||||||
|  | become effective for each Contribution on the date the Contributor first | ||||||
|  | distributes such Contribution. | ||||||
|  |  | ||||||
|  | 2.3. Limitations on Grant Scope | ||||||
|  |  | ||||||
|  | The licenses granted in this Section 2 are the only rights granted under | ||||||
|  | this License. No additional rights or licenses will be implied from the | ||||||
|  | distribution or licensing of Covered Software under this License. | ||||||
|  | Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||||||
|  | Contributor: | ||||||
|  |  | ||||||
|  | (a) for any code that a Contributor has removed from Covered Software; | ||||||
|  |     or | ||||||
|  |  | ||||||
|  | (b) for infringements caused by: (i) Your and any other third party's | ||||||
|  |     modifications of Covered Software, or (ii) the combination of its | ||||||
|  |     Contributions with other software (except as part of its Contributor | ||||||
|  |     Version); or | ||||||
|  |  | ||||||
|  | (c) under Patent Claims infringed by Covered Software in the absence of | ||||||
|  |     its Contributions. | ||||||
|  |  | ||||||
|  | This License does not grant any rights in the trademarks, service marks, | ||||||
|  | or logos of any Contributor (except as may be necessary to comply with | ||||||
|  | the notice requirements in Section 3.4). | ||||||
|  |  | ||||||
|  | 2.4. Subsequent Licenses | ||||||
|  |  | ||||||
|  | No Contributor makes additional grants as a result of Your choice to | ||||||
|  | distribute the Covered Software under a subsequent version of this | ||||||
|  | License (see Section 10.2) or under the terms of a Secondary License (if | ||||||
|  | permitted under the terms of Section 3.3). | ||||||
|  |  | ||||||
|  | 2.5. Representation | ||||||
|  |  | ||||||
|  | Each Contributor represents that the Contributor believes its | ||||||
|  | Contributions are its original creation(s) or it has sufficient rights | ||||||
|  | to grant the rights to its Contributions conveyed by this License. | ||||||
|  |  | ||||||
|  | 2.6. Fair Use | ||||||
|  |  | ||||||
|  | This License is not intended to limit any rights You have under | ||||||
|  | applicable copyright doctrines of fair use, fair dealing, or other | ||||||
|  | equivalents. | ||||||
|  |  | ||||||
|  | 2.7. Conditions | ||||||
|  |  | ||||||
|  | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | ||||||
|  | in Section 2.1. | ||||||
|  |  | ||||||
|  | 3. Responsibilities | ||||||
|  | ------------------- | ||||||
|  |  | ||||||
|  | 3.1. Distribution of Source Form | ||||||
|  |  | ||||||
|  | All distribution of Covered Software in Source Code Form, including any | ||||||
|  | Modifications that You create or to which You contribute, must be under | ||||||
|  | the terms of this License. You must inform recipients that the Source | ||||||
|  | Code Form of the Covered Software is governed by the terms of this | ||||||
|  | License, and how they can obtain a copy of this License. You may not | ||||||
|  | attempt to alter or restrict the recipients' rights in the Source Code | ||||||
|  | Form. | ||||||
|  |  | ||||||
|  | 3.2. Distribution of Executable Form | ||||||
|  |  | ||||||
|  | If You distribute Covered Software in Executable Form then: | ||||||
|  |  | ||||||
|  | (a) such Covered Software must also be made available in Source Code | ||||||
|  |     Form, as described in Section 3.1, and You must inform recipients of | ||||||
|  |     the Executable Form how they can obtain a copy of such Source Code | ||||||
|  |     Form by reasonable means in a timely manner, at a charge no more | ||||||
|  |     than the cost of distribution to the recipient; and | ||||||
|  |  | ||||||
|  | (b) You may distribute such Executable Form under the terms of this | ||||||
|  |     License, or sublicense it under different terms, provided that the | ||||||
|  |     license for the Executable Form does not attempt to limit or alter | ||||||
|  |     the recipients' rights in the Source Code Form under this License. | ||||||
|  |  | ||||||
|  | 3.3. Distribution of a Larger Work | ||||||
|  |  | ||||||
|  | You may create and distribute a Larger Work under terms of Your choice, | ||||||
|  | provided that You also comply with the requirements of this License for | ||||||
|  | the Covered Software. If the Larger Work is a combination of Covered | ||||||
|  | Software with a work governed by one or more Secondary Licenses, and the | ||||||
|  | Covered Software is not Incompatible With Secondary Licenses, this | ||||||
|  | License permits You to additionally distribute such Covered Software | ||||||
|  | under the terms of such Secondary License(s), so that the recipient of | ||||||
|  | the Larger Work may, at their option, further distribute the Covered | ||||||
|  | Software under the terms of either this License or such Secondary | ||||||
|  | License(s). | ||||||
|  |  | ||||||
|  | 3.4. Notices | ||||||
|  |  | ||||||
|  | You may not remove or alter the substance of any license notices | ||||||
|  | (including copyright notices, patent notices, disclaimers of warranty, | ||||||
|  | or limitations of liability) contained within the Source Code Form of | ||||||
|  | the Covered Software, except that You may alter any license notices to | ||||||
|  | the extent required to remedy known factual inaccuracies. | ||||||
|  |  | ||||||
|  | 3.5. Application of Additional Terms | ||||||
|  |  | ||||||
|  | You may choose to offer, and to charge a fee for, warranty, support, | ||||||
|  | indemnity or liability obligations to one or more recipients of Covered | ||||||
|  | Software. However, You may do so only on Your own behalf, and not on | ||||||
|  | behalf of any Contributor. You must make it absolutely clear that any | ||||||
|  | such warranty, support, indemnity, or liability obligation is offered by | ||||||
|  | You alone, and You hereby agree to indemnify every Contributor for any | ||||||
|  | liability incurred by such Contributor as a result of warranty, support, | ||||||
|  | indemnity or liability terms You offer. You may include additional | ||||||
|  | disclaimers of warranty and limitations of liability specific to any | ||||||
|  | jurisdiction. | ||||||
|  |  | ||||||
|  | 4. Inability to Comply Due to Statute or Regulation | ||||||
|  | --------------------------------------------------- | ||||||
|  |  | ||||||
|  | If it is impossible for You to comply with any of the terms of this | ||||||
|  | License with respect to some or all of the Covered Software due to | ||||||
|  | statute, judicial order, or regulation then You must: (a) comply with | ||||||
|  | the terms of this License to the maximum extent possible; and (b) | ||||||
|  | describe the limitations and the code they affect. Such description must | ||||||
|  | be placed in a text file included with all distributions of the Covered | ||||||
|  | Software under this License. Except to the extent prohibited by statute | ||||||
|  | or regulation, such description must be sufficiently detailed for a | ||||||
|  | recipient of ordinary skill to be able to understand it. | ||||||
|  |  | ||||||
|  | 5. Termination | ||||||
|  | -------------- | ||||||
|  |  | ||||||
|  | 5.1. The rights granted under this License will terminate automatically | ||||||
|  | if You fail to comply with any of its terms. However, if You become | ||||||
|  | compliant, then the rights granted under this License from a particular | ||||||
|  | Contributor are reinstated (a) provisionally, unless and until such | ||||||
|  | Contributor explicitly and finally terminates Your grants, and (b) on an | ||||||
|  | ongoing basis, if such Contributor fails to notify You of the | ||||||
|  | non-compliance by some reasonable means prior to 60 days after You have | ||||||
|  | come back into compliance. Moreover, Your grants from a particular | ||||||
|  | Contributor are reinstated on an ongoing basis if such Contributor | ||||||
|  | notifies You of the non-compliance by some reasonable means, this is the | ||||||
|  | first time You have received notice of non-compliance with this License | ||||||
|  | from such Contributor, and You become compliant prior to 30 days after | ||||||
|  | Your receipt of the notice. | ||||||
|  |  | ||||||
|  | 5.2. If You initiate litigation against any entity by asserting a patent | ||||||
|  | infringement claim (excluding declaratory judgment actions, | ||||||
|  | counter-claims, and cross-claims) alleging that a Contributor Version | ||||||
|  | directly or indirectly infringes any patent, then the rights granted to | ||||||
|  | You by any and all Contributors for the Covered Software under Section | ||||||
|  | 2.1 of this License shall terminate. | ||||||
|  |  | ||||||
|  | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all | ||||||
|  | end user license agreements (excluding distributors and resellers) which | ||||||
|  | have been validly granted by You or Your distributors under this License | ||||||
|  | prior to termination shall survive termination. | ||||||
|  |  | ||||||
|  | ************************************************************************ | ||||||
|  | *                                                                      * | ||||||
|  | *  6. Disclaimer of Warranty                                           * | ||||||
|  | *  -------------------------                                           * | ||||||
|  | *                                                                      * | ||||||
|  | *  Covered Software is provided under this License on an "as is"       * | ||||||
|  | *  basis, without warranty of any kind, either expressed, implied, or  * | ||||||
|  | *  statutory, including, without limitation, warranties that the       * | ||||||
|  | *  Covered Software is free of defects, merchantable, fit for a        * | ||||||
|  | *  particular purpose or non-infringing. The entire risk as to the     * | ||||||
|  | *  quality and performance of the Covered Software is with You.        * | ||||||
|  | *  Should any Covered Software prove defective in any respect, You     * | ||||||
|  | *  (not any Contributor) assume the cost of any necessary servicing,   * | ||||||
|  | *  repair, or correction. This disclaimer of warranty constitutes an   * | ||||||
|  | *  essential part of this License. No use of any Covered Software is   * | ||||||
|  | *  authorized under this License except under this disclaimer.         * | ||||||
|  | *                                                                      * | ||||||
|  | ************************************************************************ | ||||||
|  |  | ||||||
|  | ************************************************************************ | ||||||
|  | *                                                                      * | ||||||
|  | *  7. Limitation of Liability                                          * | ||||||
|  | *  --------------------------                                          * | ||||||
|  | *                                                                      * | ||||||
|  | *  Under no circumstances and under no legal theory, whether tort      * | ||||||
|  | *  (including negligence), contract, or otherwise, shall any           * | ||||||
|  | *  Contributor, or anyone who distributes Covered Software as          * | ||||||
|  | *  permitted above, be liable to You for any direct, indirect,         * | ||||||
|  | *  special, incidental, or consequential damages of any character      * | ||||||
|  | *  including, without limitation, damages for lost profits, loss of    * | ||||||
|  | *  goodwill, work stoppage, computer failure or malfunction, or any    * | ||||||
|  | *  and all other commercial damages or losses, even if such party      * | ||||||
|  | *  shall have been informed of the possibility of such damages. This   * | ||||||
|  | *  limitation of liability shall not apply to liability for death or   * | ||||||
|  | *  personal injury resulting from such party's negligence to the       * | ||||||
|  | *  extent applicable law prohibits such limitation. Some               * | ||||||
|  | *  jurisdictions do not allow the exclusion or limitation of           * | ||||||
|  | *  incidental or consequential damages, so this exclusion and          * | ||||||
|  | *  limitation may not apply to You.                                    * | ||||||
|  | *                                                                      * | ||||||
|  | ************************************************************************ | ||||||
|  |  | ||||||
|  | 8. Litigation | ||||||
|  | ------------- | ||||||
|  |  | ||||||
|  | Any litigation relating to this License may be brought only in the | ||||||
|  | courts of a jurisdiction where the defendant maintains its principal | ||||||
|  | place of business and such litigation shall be governed by laws of that | ||||||
|  | jurisdiction, without reference to its conflict-of-law provisions. | ||||||
|  | Nothing in this Section shall prevent a party's ability to bring | ||||||
|  | cross-claims or counter-claims. | ||||||
|  |  | ||||||
|  | 9. Miscellaneous | ||||||
|  | ---------------- | ||||||
|  |  | ||||||
|  | This License represents the complete agreement concerning the subject | ||||||
|  | matter hereof. If any provision of this License is held to be | ||||||
|  | unenforceable, such provision shall be reformed only to the extent | ||||||
|  | necessary to make it enforceable. Any law or regulation which provides | ||||||
|  | that the language of a contract shall be construed against the drafter | ||||||
|  | shall not be used to construe this License against a Contributor. | ||||||
|  |  | ||||||
|  | 10. Versions of the License | ||||||
|  | --------------------------- | ||||||
|  |  | ||||||
|  | 10.1. New Versions | ||||||
|  |  | ||||||
|  | Mozilla Foundation is the license steward. Except as provided in Section | ||||||
|  | 10.3, no one other than the license steward has the right to modify or | ||||||
|  | publish new versions of this License. Each version will be given a | ||||||
|  | distinguishing version number. | ||||||
|  |  | ||||||
|  | 10.2. Effect of New Versions | ||||||
|  |  | ||||||
|  | You may distribute the Covered Software under the terms of the version | ||||||
|  | of the License under which You originally received the Covered Software, | ||||||
|  | or under the terms of any subsequent version published by the license | ||||||
|  | steward. | ||||||
|  |  | ||||||
|  | 10.3. Modified Versions | ||||||
|  |  | ||||||
|  | If you create software not governed by this License, and you want to | ||||||
|  | create a new license for such software, you may create and use a | ||||||
|  | modified version of this License if you rename the license and remove | ||||||
|  | any references to the name of the license steward (except to note that | ||||||
|  | such modified license differs from this License). | ||||||
|  |  | ||||||
|  | 10.4. Distributing Source Code Form that is Incompatible With Secondary | ||||||
|  | Licenses | ||||||
|  |  | ||||||
|  | If You choose to distribute Source Code Form that is Incompatible With | ||||||
|  | Secondary Licenses under the terms of this version of the License, the | ||||||
|  | notice described in Exhibit B of this License must be attached. | ||||||
|  |  | ||||||
|  | Exhibit A - Source Code Form License Notice | ||||||
|  | ------------------------------------------- | ||||||
|  |  | ||||||
|  |   This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |   License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |   file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | If it is not possible or desirable to put the notice in a particular | ||||||
|  | file, then You may include the notice in a location (such as a LICENSE | ||||||
|  | file in a relevant directory) where a recipient would be likely to look | ||||||
|  | for such a notice. | ||||||
|  |  | ||||||
|  | You may add additional accurate notices of copyright ownership. | ||||||
|  |  | ||||||
|  | Exhibit B - "Incompatible With Secondary Licenses" Notice | ||||||
|  | --------------------------------------------------------- | ||||||
|  |  | ||||||
|  |   This Source Code Form is "Incompatible With Secondary Licenses", as | ||||||
|  |   defined by the Mozilla Public License, v. 2.0. | ||||||
							
								
								
									
										374
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,374 @@ | |||||||
|  | # Go-MySQL-Driver | ||||||
|  |  | ||||||
|  | A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | **Latest stable Release:** [Version 1.2 (June 03, 2014)](https://github.com/go-sql-driver/mysql/releases) | ||||||
|  |  | ||||||
|  | [](https://travis-ci.org/go-sql-driver/mysql) | ||||||
|  |  | ||||||
|  | --------------------------------------- | ||||||
|  |   * [Features](#features) | ||||||
|  |   * [Requirements](#requirements) | ||||||
|  |   * [Installation](#installation) | ||||||
|  |   * [Usage](#usage) | ||||||
|  |     * [DSN (Data Source Name)](#dsn-data-source-name) | ||||||
|  |       * [Password](#password) | ||||||
|  |       * [Protocol](#protocol) | ||||||
|  |       * [Address](#address) | ||||||
|  |       * [Parameters](#parameters) | ||||||
|  |       * [Examples](#examples) | ||||||
|  |     * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support) | ||||||
|  |     * [time.Time support](#timetime-support) | ||||||
|  |     * [Unicode support](#unicode-support) | ||||||
|  |   * [Testing / Development](#testing--development) | ||||||
|  |   * [License](#license) | ||||||
|  |  | ||||||
|  | --------------------------------------- | ||||||
|  |  | ||||||
|  | ## Features | ||||||
|  |   * Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance") | ||||||
|  |   * Native Go implementation. No C-bindings, just pure Go | ||||||
|  |   * Connections over TCP/IPv4, TCP/IPv6 or Unix domain sockets | ||||||
|  |   * Automatic handling of broken connections | ||||||
|  |   * Automatic Connection Pooling *(by database/sql package)* | ||||||
|  |   * Supports queries larger than 16MB | ||||||
|  |   * Full [`sql.RawBytes`](http://golang.org/pkg/database/sql/#RawBytes) support. | ||||||
|  |   * Intelligent `LONG DATA` handling in prepared statements | ||||||
|  |   * Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support | ||||||
|  |   * Optional `time.Time` parsing | ||||||
|  |   * Optional placeholder interpolation | ||||||
|  |  | ||||||
|  | ## Requirements | ||||||
|  |   * Go 1.2 or higher | ||||||
|  |   * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+) | ||||||
|  |  | ||||||
|  | --------------------------------------- | ||||||
|  |  | ||||||
|  | ## Installation | ||||||
|  | Simple install the package to your [$GOPATH](http://code.google.com/p/go-wiki/wiki/GOPATH "GOPATH") with the [go tool](http://golang.org/cmd/go/ "go command") from shell: | ||||||
|  | ```bash | ||||||
|  | $ go get github.com/go-sql-driver/mysql | ||||||
|  | ``` | ||||||
|  | Make sure [Git is installed](http://git-scm.com/downloads) on your machine and in your system's `PATH`. | ||||||
|  |  | ||||||
|  | ## Usage | ||||||
|  | _Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](http://golang.org/pkg/database/sql) API then. | ||||||
|  |  | ||||||
|  | Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name)  as `dataSourceName`: | ||||||
|  | ```go | ||||||
|  | import "database/sql" | ||||||
|  | import _ "github.com/go-sql-driver/mysql" | ||||||
|  |  | ||||||
|  | db, err := sql.Open("mysql", "user:password@/dbname") | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | [Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples"). | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### DSN (Data Source Name) | ||||||
|  |  | ||||||
|  | The Data Source Name has a common format, like e.g. [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php) uses it, but without type-prefix (optional parts marked by squared brackets): | ||||||
|  | ``` | ||||||
|  | [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | A DSN in its fullest form: | ||||||
|  | ``` | ||||||
|  | username:password@protocol(address)/dbname?param=value | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Except for the databasename, all values are optional. So the minimal DSN is: | ||||||
|  | ``` | ||||||
|  | /dbname | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | If you do not want to preselect a database, leave `dbname` empty: | ||||||
|  | ``` | ||||||
|  | / | ||||||
|  | ``` | ||||||
|  | This has the same effect as an empty DSN string: | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Password | ||||||
|  | Passwords can consist of any character. Escaping is **not** necessary. | ||||||
|  |  | ||||||
|  | #### Protocol | ||||||
|  | See [net.Dial](http://golang.org/pkg/net/#Dial) for more information which networks are available. | ||||||
|  | In general you should use an Unix domain socket if available and TCP otherwise for best performance. | ||||||
|  |  | ||||||
|  | #### Address | ||||||
|  | For TCP and UDP networks, addresses have the form `host:port`. | ||||||
|  | If `host` is a literal IPv6 address, it must be enclosed in square brackets. | ||||||
|  | The functions [net.JoinHostPort](http://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](http://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form. | ||||||
|  |  | ||||||
|  | For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`. | ||||||
|  |  | ||||||
|  | #### Parameters | ||||||
|  | *Parameters are case-sensitive!* | ||||||
|  |  | ||||||
|  | Notice that any of `true`, `TRUE`, `True` or `1` is accepted to stand for a true boolean value. Not surprisingly, false can be specified as any of: `false`, `FALSE`, `False` or `0`. | ||||||
|  |  | ||||||
|  | ##### `allowAllFiles` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool | ||||||
|  | Valid Values:   true, false | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | `allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. | ||||||
|  | [*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html) | ||||||
|  |  | ||||||
|  | ##### `allowOldPasswords` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool | ||||||
|  | Valid Values:   true, false | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  | `allowOldPasswords=true` allows the usage of the insecure old password method. This should be avoided, but is necessary in some cases. See also [the old_passwords wiki page](https://github.com/go-sql-driver/mysql/wiki/old_passwords). | ||||||
|  |  | ||||||
|  | ##### `charset` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           string | ||||||
|  | Valid Values:   <name> | ||||||
|  | Default:        none | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Sets the charset used for client-server interaction (`"SET NAMES <value>"`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`). | ||||||
|  |  | ||||||
|  | Usage of the `charset` parameter is discouraged because it issues additional queries to the server. | ||||||
|  | Unless you need the fallback behavior, please use `collation` instead. | ||||||
|  |  | ||||||
|  | ##### `collation` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           string | ||||||
|  | Valid Values:   <name> | ||||||
|  | Default:        utf8_general_ci | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Sets the collation used for client-server interaction on connection. In contrast to `charset`, `collation` does not issue additional queries. If the specified collation is unavailable on the target server, the connection will fail. | ||||||
|  |  | ||||||
|  | A list of valid charsets for a server is retrievable with `SHOW COLLATION`. | ||||||
|  |  | ||||||
|  | ##### `clientFoundRows` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool | ||||||
|  | Valid Values:   true, false | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | `clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed. | ||||||
|  |  | ||||||
|  | ##### `columnsWithAlias` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool | ||||||
|  | Valid Values:   true, false | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | SELECT u.id FROM users as u | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | will return `u.id` instead of just `id` if `columnsWithAlias=true`. | ||||||
|  |  | ||||||
|  | ##### `interpolateParams` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool | ||||||
|  | Valid Values:   true, false | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`. | ||||||
|  |  | ||||||
|  | *This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!* | ||||||
|  |  | ||||||
|  | ##### `loc` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           string | ||||||
|  | Valid Values:   <escaped name> | ||||||
|  | Default:        UTC | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details. | ||||||
|  |  | ||||||
|  | Please keep in mind, that param values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ##### `parseTime` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool | ||||||
|  | Valid Values:   true, false | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | `parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string` | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ##### `strict` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool | ||||||
|  | Valid Values:   true, false | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | `strict=true` enables the strict mode in which MySQL warnings are treated as errors. | ||||||
|  |  | ||||||
|  | By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_notes) to ignore notes. See the [examples](#examples) for an DSN example. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ##### `timeout` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           decimal number | ||||||
|  | Default:        OS default | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | *Driver* side connection timeout. The value must be a string of decimal numbers, each with optional fraction and a unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout). | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ##### `tls` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Type:           bool / string | ||||||
|  | Valid Values:   true, false, skip-verify, <name> | ||||||
|  | Default:        false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | `tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig). | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ##### System Variables | ||||||
|  |  | ||||||
|  | All other parameters are interpreted as system variables: | ||||||
|  |   * `autocommit`: `"SET autocommit=<value>"` | ||||||
|  |   * `time_zone`: `"SET time_zone=<value>"` | ||||||
|  |   * [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"` | ||||||
|  |   * `param`: `"SET <param>=<value>"` | ||||||
|  |  | ||||||
|  | *The values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!* | ||||||
|  |  | ||||||
|  | #### Examples | ||||||
|  | ``` | ||||||
|  | user@unix(/path/to/socket)/dbname | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | root:pw@unix(/tmp/mysql.sock)/myDatabase?loc=Local | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Use the [strict mode](#strict) but ignore notes: | ||||||
|  | ``` | ||||||
|  | user:password@/dbname?strict=true&sql_notes=false | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | TCP via IPv6: | ||||||
|  | ``` | ||||||
|  | user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname?timeout=90s&collation=utf8mb4_unicode_ci | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | TCP on a remote host, e.g. Amazon RDS: | ||||||
|  | ``` | ||||||
|  | id:password@tcp(your-amazonaws-uri.com:3306)/dbname | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Google Cloud SQL on App Engine: | ||||||
|  | ``` | ||||||
|  | user@cloudsql(project-id:instance-name)/dbname | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | TCP using default port (3306) on localhost: | ||||||
|  | ``` | ||||||
|  | user:password@tcp/dbname?charset=utf8mb4,utf8&sys_var=esc%40ped | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Use the default protocol (tcp) and host (localhost:3306): | ||||||
|  | ``` | ||||||
|  | user:password@/dbname | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | No Database preselected: | ||||||
|  | ``` | ||||||
|  | user:password@/ | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### `LOAD DATA LOCAL INFILE` support | ||||||
|  | For this feature you need direct access to the package. Therefore you must change the import path (no `_`): | ||||||
|  | ```go | ||||||
|  | import "github.com/go-sql-driver/mysql" | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). | ||||||
|  |  | ||||||
|  | To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. | ||||||
|  |  | ||||||
|  | See the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### `time.Time` support | ||||||
|  | The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your programm. | ||||||
|  |  | ||||||
|  | However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](http://golang.org/pkg/time/#Location) with the `loc` DSN parameter. | ||||||
|  |  | ||||||
|  | **Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes). | ||||||
|  |  | ||||||
|  | Alternatively you can use the [`NullTime`](http://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Unicode support | ||||||
|  | Since version 1.1 Go-MySQL-Driver automatically uses the collation `utf8_general_ci` by default. | ||||||
|  |  | ||||||
|  | Other collations / charsets can be set using the [`collation`](#collation) DSN parameter. | ||||||
|  |  | ||||||
|  | Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default. | ||||||
|  |  | ||||||
|  | See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Testing / Development | ||||||
|  | To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details. | ||||||
|  |  | ||||||
|  | Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated. | ||||||
|  | If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls). | ||||||
|  |  | ||||||
|  | See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details. | ||||||
|  |  | ||||||
|  | --------------------------------------- | ||||||
|  |  | ||||||
|  | ## License | ||||||
|  | Go-MySQL-Driver is licensed under the [Mozilla Public License Version 2.0](https://raw.github.com/go-sql-driver/mysql/master/LICENSE) | ||||||
|  |  | ||||||
|  | Mozilla summarizes the license scope as follows: | ||||||
|  | > MPL: The copyleft applies to any files containing MPLed code. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | That means: | ||||||
|  |   * You can **use** the **unchanged** source code both in private and commercially | ||||||
|  |   * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0) | ||||||
|  |   * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged** | ||||||
|  |  | ||||||
|  | Please read the [MPL 2.0 FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license. | ||||||
|  |  | ||||||
|  | You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										19
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | // +build appengine | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"appengine/cloudsql" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	RegisterDial("cloudsql", cloudsql.Dial) | ||||||
|  | } | ||||||
							
								
								
									
										246
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"database/sql" | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"math" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type TB testing.B | ||||||
|  |  | ||||||
|  | func (tb *TB) check(err error) { | ||||||
|  | 	if err != nil { | ||||||
|  | 		tb.Fatal(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB { | ||||||
|  | 	tb.check(err) | ||||||
|  | 	return db | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows { | ||||||
|  | 	tb.check(err) | ||||||
|  | 	return rows | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt { | ||||||
|  | 	tb.check(err) | ||||||
|  | 	return stmt | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func initDB(b *testing.B, queries ...string) *sql.DB { | ||||||
|  | 	tb := (*TB)(b) | ||||||
|  | 	db := tb.checkDB(sql.Open("mysql", dsn)) | ||||||
|  | 	for _, query := range queries { | ||||||
|  | 		if _, err := db.Exec(query); err != nil { | ||||||
|  | 			if w, ok := err.(MySQLWarnings); ok { | ||||||
|  | 				b.Logf("Warning on %q: %v", query, w) | ||||||
|  | 			} else { | ||||||
|  | 				b.Fatalf("Error on %q: %v", query, err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return db | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const concurrencyLevel = 10 | ||||||
|  |  | ||||||
|  | func BenchmarkQuery(b *testing.B) { | ||||||
|  | 	tb := (*TB)(b) | ||||||
|  | 	b.StopTimer() | ||||||
|  | 	b.ReportAllocs() | ||||||
|  | 	db := initDB(b, | ||||||
|  | 		"DROP TABLE IF EXISTS foo", | ||||||
|  | 		"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))", | ||||||
|  | 		`INSERT INTO foo VALUES (1, "one")`, | ||||||
|  | 		`INSERT INTO foo VALUES (2, "two")`, | ||||||
|  | 	) | ||||||
|  | 	db.SetMaxIdleConns(concurrencyLevel) | ||||||
|  | 	defer db.Close() | ||||||
|  |  | ||||||
|  | 	stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?")) | ||||||
|  | 	defer stmt.Close() | ||||||
|  |  | ||||||
|  | 	remain := int64(b.N) | ||||||
|  | 	var wg sync.WaitGroup | ||||||
|  | 	wg.Add(concurrencyLevel) | ||||||
|  | 	defer wg.Wait() | ||||||
|  | 	b.StartTimer() | ||||||
|  |  | ||||||
|  | 	for i := 0; i < concurrencyLevel; i++ { | ||||||
|  | 		go func() { | ||||||
|  | 			for { | ||||||
|  | 				if atomic.AddInt64(&remain, -1) < 0 { | ||||||
|  | 					wg.Done() | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				var got string | ||||||
|  | 				tb.check(stmt.QueryRow(1).Scan(&got)) | ||||||
|  | 				if got != "one" { | ||||||
|  | 					b.Errorf("query = %q; want one", got) | ||||||
|  | 					wg.Done() | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkExec(b *testing.B) { | ||||||
|  | 	tb := (*TB)(b) | ||||||
|  | 	b.StopTimer() | ||||||
|  | 	b.ReportAllocs() | ||||||
|  | 	db := tb.checkDB(sql.Open("mysql", dsn)) | ||||||
|  | 	db.SetMaxIdleConns(concurrencyLevel) | ||||||
|  | 	defer db.Close() | ||||||
|  |  | ||||||
|  | 	stmt := tb.checkStmt(db.Prepare("DO 1")) | ||||||
|  | 	defer stmt.Close() | ||||||
|  |  | ||||||
|  | 	remain := int64(b.N) | ||||||
|  | 	var wg sync.WaitGroup | ||||||
|  | 	wg.Add(concurrencyLevel) | ||||||
|  | 	defer wg.Wait() | ||||||
|  | 	b.StartTimer() | ||||||
|  |  | ||||||
|  | 	for i := 0; i < concurrencyLevel; i++ { | ||||||
|  | 		go func() { | ||||||
|  | 			for { | ||||||
|  | 				if atomic.AddInt64(&remain, -1) < 0 { | ||||||
|  | 					wg.Done() | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if _, err := stmt.Exec(); err != nil { | ||||||
|  | 					b.Fatal(err.Error()) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // data, but no db writes | ||||||
|  | var roundtripSample []byte | ||||||
|  |  | ||||||
|  | func initRoundtripBenchmarks() ([]byte, int, int) { | ||||||
|  | 	if roundtripSample == nil { | ||||||
|  | 		roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024)) | ||||||
|  | 	} | ||||||
|  | 	return roundtripSample, 16, len(roundtripSample) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkRoundtripTxt(b *testing.B) { | ||||||
|  | 	b.StopTimer() | ||||||
|  | 	sample, min, max := initRoundtripBenchmarks() | ||||||
|  | 	sampleString := string(sample) | ||||||
|  | 	b.ReportAllocs() | ||||||
|  | 	tb := (*TB)(b) | ||||||
|  | 	db := tb.checkDB(sql.Open("mysql", dsn)) | ||||||
|  | 	defer db.Close() | ||||||
|  | 	b.StartTimer() | ||||||
|  | 	var result string | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		length := min + i | ||||||
|  | 		if length > max { | ||||||
|  | 			length = max | ||||||
|  | 		} | ||||||
|  | 		test := sampleString[0:length] | ||||||
|  | 		rows := tb.checkRows(db.Query(`SELECT "` + test + `"`)) | ||||||
|  | 		if !rows.Next() { | ||||||
|  | 			rows.Close() | ||||||
|  | 			b.Fatalf("crashed") | ||||||
|  | 		} | ||||||
|  | 		err := rows.Scan(&result) | ||||||
|  | 		if err != nil { | ||||||
|  | 			rows.Close() | ||||||
|  | 			b.Fatalf("crashed") | ||||||
|  | 		} | ||||||
|  | 		if result != test { | ||||||
|  | 			rows.Close() | ||||||
|  | 			b.Errorf("mismatch") | ||||||
|  | 		} | ||||||
|  | 		rows.Close() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkRoundtripBin(b *testing.B) { | ||||||
|  | 	b.StopTimer() | ||||||
|  | 	sample, min, max := initRoundtripBenchmarks() | ||||||
|  | 	b.ReportAllocs() | ||||||
|  | 	tb := (*TB)(b) | ||||||
|  | 	db := tb.checkDB(sql.Open("mysql", dsn)) | ||||||
|  | 	defer db.Close() | ||||||
|  | 	stmt := tb.checkStmt(db.Prepare("SELECT ?")) | ||||||
|  | 	defer stmt.Close() | ||||||
|  | 	b.StartTimer() | ||||||
|  | 	var result sql.RawBytes | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		length := min + i | ||||||
|  | 		if length > max { | ||||||
|  | 			length = max | ||||||
|  | 		} | ||||||
|  | 		test := sample[0:length] | ||||||
|  | 		rows := tb.checkRows(stmt.Query(test)) | ||||||
|  | 		if !rows.Next() { | ||||||
|  | 			rows.Close() | ||||||
|  | 			b.Fatalf("crashed") | ||||||
|  | 		} | ||||||
|  | 		err := rows.Scan(&result) | ||||||
|  | 		if err != nil { | ||||||
|  | 			rows.Close() | ||||||
|  | 			b.Fatalf("crashed") | ||||||
|  | 		} | ||||||
|  | 		if !bytes.Equal(result, test) { | ||||||
|  | 			rows.Close() | ||||||
|  | 			b.Errorf("mismatch") | ||||||
|  | 		} | ||||||
|  | 		rows.Close() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkInterpolation(b *testing.B) { | ||||||
|  | 	mc := &mysqlConn{ | ||||||
|  | 		cfg: &config{ | ||||||
|  | 			interpolateParams: true, | ||||||
|  | 			loc:               time.UTC, | ||||||
|  | 		}, | ||||||
|  | 		maxPacketAllowed: maxPacketSize, | ||||||
|  | 		maxWriteSize:     maxPacketSize - 1, | ||||||
|  | 		buf:              newBuffer(nil), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	args := []driver.Value{ | ||||||
|  | 		int64(42424242), | ||||||
|  | 		float64(math.Pi), | ||||||
|  | 		false, | ||||||
|  | 		time.Unix(1423411542, 807015000), | ||||||
|  | 		[]byte("bytes containing special chars ' \" \a \x00"), | ||||||
|  | 		"string containing special chars ' \" \a \x00", | ||||||
|  | 	} | ||||||
|  | 	q := "SELECT ?, ?, ?, ?, ?, ?" | ||||||
|  |  | ||||||
|  | 	b.ReportAllocs() | ||||||
|  | 	b.ResetTimer() | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		_, err := mc.interpolateParams(q, args) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										136
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import "io" | ||||||
|  |  | ||||||
|  | const defaultBufSize = 4096 | ||||||
|  |  | ||||||
|  | // A buffer which is used for both reading and writing. | ||||||
|  | // This is possible since communication on each connection is synchronous. | ||||||
|  | // In other words, we can't write and read simultaneously on the same connection. | ||||||
|  | // The buffer is similar to bufio.Reader / Writer but zero-copy-ish | ||||||
|  | // Also highly optimized for this particular use case. | ||||||
|  | type buffer struct { | ||||||
|  | 	buf    []byte | ||||||
|  | 	rd     io.Reader | ||||||
|  | 	idx    int | ||||||
|  | 	length int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newBuffer(rd io.Reader) buffer { | ||||||
|  | 	var b [defaultBufSize]byte | ||||||
|  | 	return buffer{ | ||||||
|  | 		buf: b[:], | ||||||
|  | 		rd:  rd, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // fill reads into the buffer until at least _need_ bytes are in it | ||||||
|  | func (b *buffer) fill(need int) error { | ||||||
|  | 	n := b.length | ||||||
|  |  | ||||||
|  | 	// move existing data to the beginning | ||||||
|  | 	if n > 0 && b.idx > 0 { | ||||||
|  | 		copy(b.buf[0:n], b.buf[b.idx:]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// grow buffer if necessary | ||||||
|  | 	// TODO: let the buffer shrink again at some point | ||||||
|  | 	//       Maybe keep the org buf slice and swap back? | ||||||
|  | 	if need > len(b.buf) { | ||||||
|  | 		// Round up to the next multiple of the default size | ||||||
|  | 		newBuf := make([]byte, ((need/defaultBufSize)+1)*defaultBufSize) | ||||||
|  | 		copy(newBuf, b.buf) | ||||||
|  | 		b.buf = newBuf | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.idx = 0 | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		nn, err := b.rd.Read(b.buf[n:]) | ||||||
|  | 		n += nn | ||||||
|  |  | ||||||
|  | 		switch err { | ||||||
|  | 		case nil: | ||||||
|  | 			if n < need { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			b.length = n | ||||||
|  | 			return nil | ||||||
|  |  | ||||||
|  | 		case io.EOF: | ||||||
|  | 			if n >= need { | ||||||
|  | 				b.length = n | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 			return io.ErrUnexpectedEOF | ||||||
|  |  | ||||||
|  | 		default: | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns next N bytes from buffer. | ||||||
|  | // The returned slice is only guaranteed to be valid until the next read | ||||||
|  | func (b *buffer) readNext(need int) ([]byte, error) { | ||||||
|  | 	if b.length < need { | ||||||
|  | 		// refill | ||||||
|  | 		if err := b.fill(need); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	offset := b.idx | ||||||
|  | 	b.idx += need | ||||||
|  | 	b.length -= need | ||||||
|  | 	return b.buf[offset:b.idx], nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns a buffer with the requested size. | ||||||
|  | // If possible, a slice from the existing buffer is returned. | ||||||
|  | // Otherwise a bigger buffer is made. | ||||||
|  | // Only one buffer (total) can be used at a time. | ||||||
|  | func (b *buffer) takeBuffer(length int) []byte { | ||||||
|  | 	if b.length > 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// test (cheap) general case first | ||||||
|  | 	if length <= defaultBufSize || length <= cap(b.buf) { | ||||||
|  | 		return b.buf[:length] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if length < maxPacketSize { | ||||||
|  | 		b.buf = make([]byte, length) | ||||||
|  | 		return b.buf | ||||||
|  | 	} | ||||||
|  | 	return make([]byte, length) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // shortcut which can be used if the requested buffer is guaranteed to be | ||||||
|  | // smaller than defaultBufSize | ||||||
|  | // Only one buffer (total) can be used at a time. | ||||||
|  | func (b *buffer) takeSmallBuffer(length int) []byte { | ||||||
|  | 	if b.length == 0 { | ||||||
|  | 		return b.buf[:length] | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // takeCompleteBuffer returns the complete existing buffer. | ||||||
|  | // This can be used if the necessary buffer size is unknown. | ||||||
|  | // Only one buffer (total) can be used at a time. | ||||||
|  | func (b *buffer) takeCompleteBuffer() []byte { | ||||||
|  | 	if b.length == 0 { | ||||||
|  | 		return b.buf | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										250
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,250 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2014 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | const defaultCollation byte = 33 // utf8_general_ci | ||||||
|  |  | ||||||
|  | // A list of available collations mapped to the internal ID. | ||||||
|  | // To update this map use the following MySQL query: | ||||||
|  | //     SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS | ||||||
|  | var collations = map[string]byte{ | ||||||
|  | 	"big5_chinese_ci":          1, | ||||||
|  | 	"latin2_czech_cs":          2, | ||||||
|  | 	"dec8_swedish_ci":          3, | ||||||
|  | 	"cp850_general_ci":         4, | ||||||
|  | 	"latin1_german1_ci":        5, | ||||||
|  | 	"hp8_english_ci":           6, | ||||||
|  | 	"koi8r_general_ci":         7, | ||||||
|  | 	"latin1_swedish_ci":        8, | ||||||
|  | 	"latin2_general_ci":        9, | ||||||
|  | 	"swe7_swedish_ci":          10, | ||||||
|  | 	"ascii_general_ci":         11, | ||||||
|  | 	"ujis_japanese_ci":         12, | ||||||
|  | 	"sjis_japanese_ci":         13, | ||||||
|  | 	"cp1251_bulgarian_ci":      14, | ||||||
|  | 	"latin1_danish_ci":         15, | ||||||
|  | 	"hebrew_general_ci":        16, | ||||||
|  | 	"tis620_thai_ci":           18, | ||||||
|  | 	"euckr_korean_ci":          19, | ||||||
|  | 	"latin7_estonian_cs":       20, | ||||||
|  | 	"latin2_hungarian_ci":      21, | ||||||
|  | 	"koi8u_general_ci":         22, | ||||||
|  | 	"cp1251_ukrainian_ci":      23, | ||||||
|  | 	"gb2312_chinese_ci":        24, | ||||||
|  | 	"greek_general_ci":         25, | ||||||
|  | 	"cp1250_general_ci":        26, | ||||||
|  | 	"latin2_croatian_ci":       27, | ||||||
|  | 	"gbk_chinese_ci":           28, | ||||||
|  | 	"cp1257_lithuanian_ci":     29, | ||||||
|  | 	"latin5_turkish_ci":        30, | ||||||
|  | 	"latin1_german2_ci":        31, | ||||||
|  | 	"armscii8_general_ci":      32, | ||||||
|  | 	"utf8_general_ci":          33, | ||||||
|  | 	"cp1250_czech_cs":          34, | ||||||
|  | 	"ucs2_general_ci":          35, | ||||||
|  | 	"cp866_general_ci":         36, | ||||||
|  | 	"keybcs2_general_ci":       37, | ||||||
|  | 	"macce_general_ci":         38, | ||||||
|  | 	"macroman_general_ci":      39, | ||||||
|  | 	"cp852_general_ci":         40, | ||||||
|  | 	"latin7_general_ci":        41, | ||||||
|  | 	"latin7_general_cs":        42, | ||||||
|  | 	"macce_bin":                43, | ||||||
|  | 	"cp1250_croatian_ci":       44, | ||||||
|  | 	"utf8mb4_general_ci":       45, | ||||||
|  | 	"utf8mb4_bin":              46, | ||||||
|  | 	"latin1_bin":               47, | ||||||
|  | 	"latin1_general_ci":        48, | ||||||
|  | 	"latin1_general_cs":        49, | ||||||
|  | 	"cp1251_bin":               50, | ||||||
|  | 	"cp1251_general_ci":        51, | ||||||
|  | 	"cp1251_general_cs":        52, | ||||||
|  | 	"macroman_bin":             53, | ||||||
|  | 	"utf16_general_ci":         54, | ||||||
|  | 	"utf16_bin":                55, | ||||||
|  | 	"utf16le_general_ci":       56, | ||||||
|  | 	"cp1256_general_ci":        57, | ||||||
|  | 	"cp1257_bin":               58, | ||||||
|  | 	"cp1257_general_ci":        59, | ||||||
|  | 	"utf32_general_ci":         60, | ||||||
|  | 	"utf32_bin":                61, | ||||||
|  | 	"utf16le_bin":              62, | ||||||
|  | 	"binary":                   63, | ||||||
|  | 	"armscii8_bin":             64, | ||||||
|  | 	"ascii_bin":                65, | ||||||
|  | 	"cp1250_bin":               66, | ||||||
|  | 	"cp1256_bin":               67, | ||||||
|  | 	"cp866_bin":                68, | ||||||
|  | 	"dec8_bin":                 69, | ||||||
|  | 	"greek_bin":                70, | ||||||
|  | 	"hebrew_bin":               71, | ||||||
|  | 	"hp8_bin":                  72, | ||||||
|  | 	"keybcs2_bin":              73, | ||||||
|  | 	"koi8r_bin":                74, | ||||||
|  | 	"koi8u_bin":                75, | ||||||
|  | 	"latin2_bin":               77, | ||||||
|  | 	"latin5_bin":               78, | ||||||
|  | 	"latin7_bin":               79, | ||||||
|  | 	"cp850_bin":                80, | ||||||
|  | 	"cp852_bin":                81, | ||||||
|  | 	"swe7_bin":                 82, | ||||||
|  | 	"utf8_bin":                 83, | ||||||
|  | 	"big5_bin":                 84, | ||||||
|  | 	"euckr_bin":                85, | ||||||
|  | 	"gb2312_bin":               86, | ||||||
|  | 	"gbk_bin":                  87, | ||||||
|  | 	"sjis_bin":                 88, | ||||||
|  | 	"tis620_bin":               89, | ||||||
|  | 	"ucs2_bin":                 90, | ||||||
|  | 	"ujis_bin":                 91, | ||||||
|  | 	"geostd8_general_ci":       92, | ||||||
|  | 	"geostd8_bin":              93, | ||||||
|  | 	"latin1_spanish_ci":        94, | ||||||
|  | 	"cp932_japanese_ci":        95, | ||||||
|  | 	"cp932_bin":                96, | ||||||
|  | 	"eucjpms_japanese_ci":      97, | ||||||
|  | 	"eucjpms_bin":              98, | ||||||
|  | 	"cp1250_polish_ci":         99, | ||||||
|  | 	"utf16_unicode_ci":         101, | ||||||
|  | 	"utf16_icelandic_ci":       102, | ||||||
|  | 	"utf16_latvian_ci":         103, | ||||||
|  | 	"utf16_romanian_ci":        104, | ||||||
|  | 	"utf16_slovenian_ci":       105, | ||||||
|  | 	"utf16_polish_ci":          106, | ||||||
|  | 	"utf16_estonian_ci":        107, | ||||||
|  | 	"utf16_spanish_ci":         108, | ||||||
|  | 	"utf16_swedish_ci":         109, | ||||||
|  | 	"utf16_turkish_ci":         110, | ||||||
|  | 	"utf16_czech_ci":           111, | ||||||
|  | 	"utf16_danish_ci":          112, | ||||||
|  | 	"utf16_lithuanian_ci":      113, | ||||||
|  | 	"utf16_slovak_ci":          114, | ||||||
|  | 	"utf16_spanish2_ci":        115, | ||||||
|  | 	"utf16_roman_ci":           116, | ||||||
|  | 	"utf16_persian_ci":         117, | ||||||
|  | 	"utf16_esperanto_ci":       118, | ||||||
|  | 	"utf16_hungarian_ci":       119, | ||||||
|  | 	"utf16_sinhala_ci":         120, | ||||||
|  | 	"utf16_german2_ci":         121, | ||||||
|  | 	"utf16_croatian_ci":        122, | ||||||
|  | 	"utf16_unicode_520_ci":     123, | ||||||
|  | 	"utf16_vietnamese_ci":      124, | ||||||
|  | 	"ucs2_unicode_ci":          128, | ||||||
|  | 	"ucs2_icelandic_ci":        129, | ||||||
|  | 	"ucs2_latvian_ci":          130, | ||||||
|  | 	"ucs2_romanian_ci":         131, | ||||||
|  | 	"ucs2_slovenian_ci":        132, | ||||||
|  | 	"ucs2_polish_ci":           133, | ||||||
|  | 	"ucs2_estonian_ci":         134, | ||||||
|  | 	"ucs2_spanish_ci":          135, | ||||||
|  | 	"ucs2_swedish_ci":          136, | ||||||
|  | 	"ucs2_turkish_ci":          137, | ||||||
|  | 	"ucs2_czech_ci":            138, | ||||||
|  | 	"ucs2_danish_ci":           139, | ||||||
|  | 	"ucs2_lithuanian_ci":       140, | ||||||
|  | 	"ucs2_slovak_ci":           141, | ||||||
|  | 	"ucs2_spanish2_ci":         142, | ||||||
|  | 	"ucs2_roman_ci":            143, | ||||||
|  | 	"ucs2_persian_ci":          144, | ||||||
|  | 	"ucs2_esperanto_ci":        145, | ||||||
|  | 	"ucs2_hungarian_ci":        146, | ||||||
|  | 	"ucs2_sinhala_ci":          147, | ||||||
|  | 	"ucs2_german2_ci":          148, | ||||||
|  | 	"ucs2_croatian_ci":         149, | ||||||
|  | 	"ucs2_unicode_520_ci":      150, | ||||||
|  | 	"ucs2_vietnamese_ci":       151, | ||||||
|  | 	"ucs2_general_mysql500_ci": 159, | ||||||
|  | 	"utf32_unicode_ci":         160, | ||||||
|  | 	"utf32_icelandic_ci":       161, | ||||||
|  | 	"utf32_latvian_ci":         162, | ||||||
|  | 	"utf32_romanian_ci":        163, | ||||||
|  | 	"utf32_slovenian_ci":       164, | ||||||
|  | 	"utf32_polish_ci":          165, | ||||||
|  | 	"utf32_estonian_ci":        166, | ||||||
|  | 	"utf32_spanish_ci":         167, | ||||||
|  | 	"utf32_swedish_ci":         168, | ||||||
|  | 	"utf32_turkish_ci":         169, | ||||||
|  | 	"utf32_czech_ci":           170, | ||||||
|  | 	"utf32_danish_ci":          171, | ||||||
|  | 	"utf32_lithuanian_ci":      172, | ||||||
|  | 	"utf32_slovak_ci":          173, | ||||||
|  | 	"utf32_spanish2_ci":        174, | ||||||
|  | 	"utf32_roman_ci":           175, | ||||||
|  | 	"utf32_persian_ci":         176, | ||||||
|  | 	"utf32_esperanto_ci":       177, | ||||||
|  | 	"utf32_hungarian_ci":       178, | ||||||
|  | 	"utf32_sinhala_ci":         179, | ||||||
|  | 	"utf32_german2_ci":         180, | ||||||
|  | 	"utf32_croatian_ci":        181, | ||||||
|  | 	"utf32_unicode_520_ci":     182, | ||||||
|  | 	"utf32_vietnamese_ci":      183, | ||||||
|  | 	"utf8_unicode_ci":          192, | ||||||
|  | 	"utf8_icelandic_ci":        193, | ||||||
|  | 	"utf8_latvian_ci":          194, | ||||||
|  | 	"utf8_romanian_ci":         195, | ||||||
|  | 	"utf8_slovenian_ci":        196, | ||||||
|  | 	"utf8_polish_ci":           197, | ||||||
|  | 	"utf8_estonian_ci":         198, | ||||||
|  | 	"utf8_spanish_ci":          199, | ||||||
|  | 	"utf8_swedish_ci":          200, | ||||||
|  | 	"utf8_turkish_ci":          201, | ||||||
|  | 	"utf8_czech_ci":            202, | ||||||
|  | 	"utf8_danish_ci":           203, | ||||||
|  | 	"utf8_lithuanian_ci":       204, | ||||||
|  | 	"utf8_slovak_ci":           205, | ||||||
|  | 	"utf8_spanish2_ci":         206, | ||||||
|  | 	"utf8_roman_ci":            207, | ||||||
|  | 	"utf8_persian_ci":          208, | ||||||
|  | 	"utf8_esperanto_ci":        209, | ||||||
|  | 	"utf8_hungarian_ci":        210, | ||||||
|  | 	"utf8_sinhala_ci":          211, | ||||||
|  | 	"utf8_german2_ci":          212, | ||||||
|  | 	"utf8_croatian_ci":         213, | ||||||
|  | 	"utf8_unicode_520_ci":      214, | ||||||
|  | 	"utf8_vietnamese_ci":       215, | ||||||
|  | 	"utf8_general_mysql500_ci": 223, | ||||||
|  | 	"utf8mb4_unicode_ci":       224, | ||||||
|  | 	"utf8mb4_icelandic_ci":     225, | ||||||
|  | 	"utf8mb4_latvian_ci":       226, | ||||||
|  | 	"utf8mb4_romanian_ci":      227, | ||||||
|  | 	"utf8mb4_slovenian_ci":     228, | ||||||
|  | 	"utf8mb4_polish_ci":        229, | ||||||
|  | 	"utf8mb4_estonian_ci":      230, | ||||||
|  | 	"utf8mb4_spanish_ci":       231, | ||||||
|  | 	"utf8mb4_swedish_ci":       232, | ||||||
|  | 	"utf8mb4_turkish_ci":       233, | ||||||
|  | 	"utf8mb4_czech_ci":         234, | ||||||
|  | 	"utf8mb4_danish_ci":        235, | ||||||
|  | 	"utf8mb4_lithuanian_ci":    236, | ||||||
|  | 	"utf8mb4_slovak_ci":        237, | ||||||
|  | 	"utf8mb4_spanish2_ci":      238, | ||||||
|  | 	"utf8mb4_roman_ci":         239, | ||||||
|  | 	"utf8mb4_persian_ci":       240, | ||||||
|  | 	"utf8mb4_esperanto_ci":     241, | ||||||
|  | 	"utf8mb4_hungarian_ci":     242, | ||||||
|  | 	"utf8mb4_sinhala_ci":       243, | ||||||
|  | 	"utf8mb4_german2_ci":       244, | ||||||
|  | 	"utf8mb4_croatian_ci":      245, | ||||||
|  | 	"utf8mb4_unicode_520_ci":   246, | ||||||
|  | 	"utf8mb4_vietnamese_ci":    247, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A blacklist of collations which is unsafe to interpolate parameters. | ||||||
|  | // These multibyte encodings may contains 0x5c (`\`) in their trailing bytes. | ||||||
|  | var unsafeCollations = map[byte]bool{ | ||||||
|  | 	1:  true, // big5_chinese_ci | ||||||
|  | 	13: true, // sjis_japanese_ci | ||||||
|  | 	28: true, // gbk_chinese_ci | ||||||
|  | 	84: true, // big5_bin | ||||||
|  | 	86: true, // gb2312_bin | ||||||
|  | 	87: true, // gbk_bin | ||||||
|  | 	88: true, // sjis_bin | ||||||
|  | 	95: true, // cp932_japanese_ci | ||||||
|  | 	96: true, // cp932_bin | ||||||
|  | } | ||||||
							
								
								
									
										402
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,402 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/tls" | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"errors" | ||||||
|  | 	"net" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type mysqlConn struct { | ||||||
|  | 	buf              buffer | ||||||
|  | 	netConn          net.Conn | ||||||
|  | 	affectedRows     uint64 | ||||||
|  | 	insertId         uint64 | ||||||
|  | 	cfg              *config | ||||||
|  | 	maxPacketAllowed int | ||||||
|  | 	maxWriteSize     int | ||||||
|  | 	flags            clientFlag | ||||||
|  | 	status           statusFlag | ||||||
|  | 	sequence         uint8 | ||||||
|  | 	parseTime        bool | ||||||
|  | 	strict           bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type config struct { | ||||||
|  | 	user              string | ||||||
|  | 	passwd            string | ||||||
|  | 	net               string | ||||||
|  | 	addr              string | ||||||
|  | 	dbname            string | ||||||
|  | 	params            map[string]string | ||||||
|  | 	loc               *time.Location | ||||||
|  | 	tls               *tls.Config | ||||||
|  | 	timeout           time.Duration | ||||||
|  | 	collation         uint8 | ||||||
|  | 	allowAllFiles     bool | ||||||
|  | 	allowOldPasswords bool | ||||||
|  | 	clientFoundRows   bool | ||||||
|  | 	columnsWithAlias  bool | ||||||
|  | 	interpolateParams bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Handles parameters set in DSN after the connection is established | ||||||
|  | func (mc *mysqlConn) handleParams() (err error) { | ||||||
|  | 	for param, val := range mc.cfg.params { | ||||||
|  | 		switch param { | ||||||
|  | 		// Charset | ||||||
|  | 		case "charset": | ||||||
|  | 			charsets := strings.Split(val, ",") | ||||||
|  | 			for i := range charsets { | ||||||
|  | 				// ignore errors here - a charset may not exist | ||||||
|  | 				err = mc.exec("SET NAMES " + charsets[i]) | ||||||
|  | 				if err == nil { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// time.Time parsing | ||||||
|  | 		case "parseTime": | ||||||
|  | 			var isBool bool | ||||||
|  | 			mc.parseTime, isBool = readBool(val) | ||||||
|  | 			if !isBool { | ||||||
|  | 				return errors.New("Invalid Bool value: " + val) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Strict mode | ||||||
|  | 		case "strict": | ||||||
|  | 			var isBool bool | ||||||
|  | 			mc.strict, isBool = readBool(val) | ||||||
|  | 			if !isBool { | ||||||
|  | 				return errors.New("Invalid Bool value: " + val) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Compression | ||||||
|  | 		case "compress": | ||||||
|  | 			err = errors.New("Compression not implemented yet") | ||||||
|  | 			return | ||||||
|  |  | ||||||
|  | 		// System Vars | ||||||
|  | 		default: | ||||||
|  | 			err = mc.exec("SET " + param + "=" + val + "") | ||||||
|  | 			if err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) Begin() (driver.Tx, error) { | ||||||
|  | 	if mc.netConn == nil { | ||||||
|  | 		errLog.Print(ErrInvalidConn) | ||||||
|  | 		return nil, driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  | 	err := mc.exec("START TRANSACTION") | ||||||
|  | 	if err == nil { | ||||||
|  | 		return &mysqlTx{mc}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) Close() (err error) { | ||||||
|  | 	// Makes Close idempotent | ||||||
|  | 	if mc.netConn != nil { | ||||||
|  | 		err = mc.writeCommandPacket(comQuit) | ||||||
|  | 		if err == nil { | ||||||
|  | 			err = mc.netConn.Close() | ||||||
|  | 		} else { | ||||||
|  | 			mc.netConn.Close() | ||||||
|  | 		} | ||||||
|  | 		mc.netConn = nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mc.cfg = nil | ||||||
|  | 	mc.buf.rd = nil | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { | ||||||
|  | 	if mc.netConn == nil { | ||||||
|  | 		errLog.Print(ErrInvalidConn) | ||||||
|  | 		return nil, driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  | 	// Send command | ||||||
|  | 	err := mc.writeCommandPacketStr(comStmtPrepare, query) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	stmt := &mysqlStmt{ | ||||||
|  | 		mc: mc, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Read Result | ||||||
|  | 	columnCount, err := stmt.readPrepareResultPacket() | ||||||
|  | 	if err == nil { | ||||||
|  | 		if stmt.paramCount > 0 { | ||||||
|  | 			if err = mc.readUntilEOF(); err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if columnCount > 0 { | ||||||
|  | 			err = mc.readUntilEOF() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return stmt, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) { | ||||||
|  | 	buf := mc.buf.takeCompleteBuffer() | ||||||
|  | 	if buf == nil { | ||||||
|  | 		// can not take the buffer. Something must be wrong with the connection | ||||||
|  | 		errLog.Print(ErrBusyBuffer) | ||||||
|  | 		return "", driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  | 	buf = buf[:0] | ||||||
|  | 	argPos := 0 | ||||||
|  |  | ||||||
|  | 	for i := 0; i < len(query); i++ { | ||||||
|  | 		q := strings.IndexByte(query[i:], '?') | ||||||
|  | 		if q == -1 { | ||||||
|  | 			buf = append(buf, query[i:]...) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		buf = append(buf, query[i:i+q]...) | ||||||
|  | 		i += q | ||||||
|  |  | ||||||
|  | 		arg := args[argPos] | ||||||
|  | 		argPos++ | ||||||
|  |  | ||||||
|  | 		if arg == nil { | ||||||
|  | 			buf = append(buf, "NULL"...) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		switch v := arg.(type) { | ||||||
|  | 		case int64: | ||||||
|  | 			buf = strconv.AppendInt(buf, v, 10) | ||||||
|  | 		case float64: | ||||||
|  | 			buf = strconv.AppendFloat(buf, v, 'g', -1, 64) | ||||||
|  | 		case bool: | ||||||
|  | 			if v { | ||||||
|  | 				buf = append(buf, '1') | ||||||
|  | 			} else { | ||||||
|  | 				buf = append(buf, '0') | ||||||
|  | 			} | ||||||
|  | 		case time.Time: | ||||||
|  | 			if v.IsZero() { | ||||||
|  | 				buf = append(buf, "'0000-00-00'"...) | ||||||
|  | 			} else { | ||||||
|  | 				v := v.In(mc.cfg.loc) | ||||||
|  | 				v = v.Add(time.Nanosecond * 500) // To round under microsecond | ||||||
|  | 				year := v.Year() | ||||||
|  | 				year100 := year / 100 | ||||||
|  | 				year1 := year % 100 | ||||||
|  | 				month := v.Month() | ||||||
|  | 				day := v.Day() | ||||||
|  | 				hour := v.Hour() | ||||||
|  | 				minute := v.Minute() | ||||||
|  | 				second := v.Second() | ||||||
|  | 				micro := v.Nanosecond() / 1000 | ||||||
|  |  | ||||||
|  | 				buf = append(buf, []byte{ | ||||||
|  | 					'\'', | ||||||
|  | 					digits10[year100], digits01[year100], | ||||||
|  | 					digits10[year1], digits01[year1], | ||||||
|  | 					'-', | ||||||
|  | 					digits10[month], digits01[month], | ||||||
|  | 					'-', | ||||||
|  | 					digits10[day], digits01[day], | ||||||
|  | 					' ', | ||||||
|  | 					digits10[hour], digits01[hour], | ||||||
|  | 					':', | ||||||
|  | 					digits10[minute], digits01[minute], | ||||||
|  | 					':', | ||||||
|  | 					digits10[second], digits01[second], | ||||||
|  | 				}...) | ||||||
|  |  | ||||||
|  | 				if micro != 0 { | ||||||
|  | 					micro10000 := micro / 10000 | ||||||
|  | 					micro100 := micro / 100 % 100 | ||||||
|  | 					micro1 := micro % 100 | ||||||
|  | 					buf = append(buf, []byte{ | ||||||
|  | 						'.', | ||||||
|  | 						digits10[micro10000], digits01[micro10000], | ||||||
|  | 						digits10[micro100], digits01[micro100], | ||||||
|  | 						digits10[micro1], digits01[micro1], | ||||||
|  | 					}...) | ||||||
|  | 				} | ||||||
|  | 				buf = append(buf, '\'') | ||||||
|  | 			} | ||||||
|  | 		case []byte: | ||||||
|  | 			if v == nil { | ||||||
|  | 				buf = append(buf, "NULL"...) | ||||||
|  | 			} else { | ||||||
|  | 				buf = append(buf, '\'') | ||||||
|  | 				if mc.status&statusNoBackslashEscapes == 0 { | ||||||
|  | 					buf = escapeBytesBackslash(buf, v) | ||||||
|  | 				} else { | ||||||
|  | 					buf = escapeBytesQuotes(buf, v) | ||||||
|  | 				} | ||||||
|  | 				buf = append(buf, '\'') | ||||||
|  | 			} | ||||||
|  | 		case string: | ||||||
|  | 			buf = append(buf, '\'') | ||||||
|  | 			if mc.status&statusNoBackslashEscapes == 0 { | ||||||
|  | 				buf = escapeStringBackslash(buf, v) | ||||||
|  | 			} else { | ||||||
|  | 				buf = escapeStringQuotes(buf, v) | ||||||
|  | 			} | ||||||
|  | 			buf = append(buf, '\'') | ||||||
|  | 		default: | ||||||
|  | 			return "", driver.ErrSkip | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if len(buf)+4 > mc.maxPacketAllowed { | ||||||
|  | 			return "", driver.ErrSkip | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if argPos != len(args) { | ||||||
|  | 		return "", driver.ErrSkip | ||||||
|  | 	} | ||||||
|  | 	return string(buf), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { | ||||||
|  | 	if mc.netConn == nil { | ||||||
|  | 		errLog.Print(ErrInvalidConn) | ||||||
|  | 		return nil, driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  | 	if len(args) != 0 { | ||||||
|  | 		if !mc.cfg.interpolateParams { | ||||||
|  | 			return nil, driver.ErrSkip | ||||||
|  | 		} | ||||||
|  | 		// try to interpolate the parameters to save extra roundtrips for preparing and closing a statement | ||||||
|  | 		prepared, err := mc.interpolateParams(query, args) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		query = prepared | ||||||
|  | 		args = nil | ||||||
|  | 	} | ||||||
|  | 	mc.affectedRows = 0 | ||||||
|  | 	mc.insertId = 0 | ||||||
|  |  | ||||||
|  | 	err := mc.exec(query) | ||||||
|  | 	if err == nil { | ||||||
|  | 		return &mysqlResult{ | ||||||
|  | 			affectedRows: int64(mc.affectedRows), | ||||||
|  | 			insertId:     int64(mc.insertId), | ||||||
|  | 		}, err | ||||||
|  | 	} | ||||||
|  | 	return nil, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Internal function to execute commands | ||||||
|  | func (mc *mysqlConn) exec(query string) error { | ||||||
|  | 	// Send command | ||||||
|  | 	err := mc.writeCommandPacketStr(comQuery, query) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Read Result | ||||||
|  | 	resLen, err := mc.readResultSetHeaderPacket() | ||||||
|  | 	if err == nil && resLen > 0 { | ||||||
|  | 		if err = mc.readUntilEOF(); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = mc.readUntilEOF() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) { | ||||||
|  | 	if mc.netConn == nil { | ||||||
|  | 		errLog.Print(ErrInvalidConn) | ||||||
|  | 		return nil, driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  | 	if len(args) != 0 { | ||||||
|  | 		if !mc.cfg.interpolateParams { | ||||||
|  | 			return nil, driver.ErrSkip | ||||||
|  | 		} | ||||||
|  | 		// try client-side prepare to reduce roundtrip | ||||||
|  | 		prepared, err := mc.interpolateParams(query, args) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		query = prepared | ||||||
|  | 		args = nil | ||||||
|  | 	} | ||||||
|  | 	// Send command | ||||||
|  | 	err := mc.writeCommandPacketStr(comQuery, query) | ||||||
|  | 	if err == nil { | ||||||
|  | 		// Read Result | ||||||
|  | 		var resLen int | ||||||
|  | 		resLen, err = mc.readResultSetHeaderPacket() | ||||||
|  | 		if err == nil { | ||||||
|  | 			rows := new(textRows) | ||||||
|  | 			rows.mc = mc | ||||||
|  |  | ||||||
|  | 			if resLen == 0 { | ||||||
|  | 				// no columns, no more data | ||||||
|  | 				return emptyRows{}, nil | ||||||
|  | 			} | ||||||
|  | 			// Columns | ||||||
|  | 			rows.columns, err = mc.readColumns(resLen) | ||||||
|  | 			return rows, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Gets the value of the given MySQL System Variable | ||||||
|  | // The returned byte slice is only valid until the next read | ||||||
|  | func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) { | ||||||
|  | 	// Send command | ||||||
|  | 	if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Read Result | ||||||
|  | 	resLen, err := mc.readResultSetHeaderPacket() | ||||||
|  | 	if err == nil { | ||||||
|  | 		rows := new(textRows) | ||||||
|  | 		rows.mc = mc | ||||||
|  |  | ||||||
|  | 		if resLen > 0 { | ||||||
|  | 			// Columns | ||||||
|  | 			if err := mc.readUntilEOF(); err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		dest := make([]driver.Value, resLen) | ||||||
|  | 		if err = rows.readRow(dest); err == nil { | ||||||
|  | 			return dest[0].([]byte), mc.readUntilEOF() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, err | ||||||
|  | } | ||||||
							
								
								
									
										154
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	minProtocolVersion byte = 10 | ||||||
|  | 	maxPacketSize           = 1<<24 - 1 | ||||||
|  | 	timeFormat              = "2006-01-02 15:04:05.999999" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // MySQL constants documentation: | ||||||
|  | // http://dev.mysql.com/doc/internals/en/client-server-protocol.html | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	iOK          byte = 0x00 | ||||||
|  | 	iLocalInFile byte = 0xfb | ||||||
|  | 	iEOF         byte = 0xfe | ||||||
|  | 	iERR         byte = 0xff | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type clientFlag uint32 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	clientLongPassword clientFlag = 1 << iota | ||||||
|  | 	clientFoundRows | ||||||
|  | 	clientLongFlag | ||||||
|  | 	clientConnectWithDB | ||||||
|  | 	clientNoSchema | ||||||
|  | 	clientCompress | ||||||
|  | 	clientODBC | ||||||
|  | 	clientLocalFiles | ||||||
|  | 	clientIgnoreSpace | ||||||
|  | 	clientProtocol41 | ||||||
|  | 	clientInteractive | ||||||
|  | 	clientSSL | ||||||
|  | 	clientIgnoreSIGPIPE | ||||||
|  | 	clientTransactions | ||||||
|  | 	clientReserved | ||||||
|  | 	clientSecureConn | ||||||
|  | 	clientMultiStatements | ||||||
|  | 	clientMultiResults | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	comQuit byte = iota + 1 | ||||||
|  | 	comInitDB | ||||||
|  | 	comQuery | ||||||
|  | 	comFieldList | ||||||
|  | 	comCreateDB | ||||||
|  | 	comDropDB | ||||||
|  | 	comRefresh | ||||||
|  | 	comShutdown | ||||||
|  | 	comStatistics | ||||||
|  | 	comProcessInfo | ||||||
|  | 	comConnect | ||||||
|  | 	comProcessKill | ||||||
|  | 	comDebug | ||||||
|  | 	comPing | ||||||
|  | 	comTime | ||||||
|  | 	comDelayedInsert | ||||||
|  | 	comChangeUser | ||||||
|  | 	comBinlogDump | ||||||
|  | 	comTableDump | ||||||
|  | 	comConnectOut | ||||||
|  | 	comRegisterSlave | ||||||
|  | 	comStmtPrepare | ||||||
|  | 	comStmtExecute | ||||||
|  | 	comStmtSendLongData | ||||||
|  | 	comStmtClose | ||||||
|  | 	comStmtReset | ||||||
|  | 	comSetOption | ||||||
|  | 	comStmtFetch | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	fieldTypeDecimal byte = iota | ||||||
|  | 	fieldTypeTiny | ||||||
|  | 	fieldTypeShort | ||||||
|  | 	fieldTypeLong | ||||||
|  | 	fieldTypeFloat | ||||||
|  | 	fieldTypeDouble | ||||||
|  | 	fieldTypeNULL | ||||||
|  | 	fieldTypeTimestamp | ||||||
|  | 	fieldTypeLongLong | ||||||
|  | 	fieldTypeInt24 | ||||||
|  | 	fieldTypeDate | ||||||
|  | 	fieldTypeTime | ||||||
|  | 	fieldTypeDateTime | ||||||
|  | 	fieldTypeYear | ||||||
|  | 	fieldTypeNewDate | ||||||
|  | 	fieldTypeVarChar | ||||||
|  | 	fieldTypeBit | ||||||
|  | ) | ||||||
|  | const ( | ||||||
|  | 	fieldTypeNewDecimal byte = iota + 0xf6 | ||||||
|  | 	fieldTypeEnum | ||||||
|  | 	fieldTypeSet | ||||||
|  | 	fieldTypeTinyBLOB | ||||||
|  | 	fieldTypeMediumBLOB | ||||||
|  | 	fieldTypeLongBLOB | ||||||
|  | 	fieldTypeBLOB | ||||||
|  | 	fieldTypeVarString | ||||||
|  | 	fieldTypeString | ||||||
|  | 	fieldTypeGeometry | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type fieldFlag uint16 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	flagNotNULL fieldFlag = 1 << iota | ||||||
|  | 	flagPriKey | ||||||
|  | 	flagUniqueKey | ||||||
|  | 	flagMultipleKey | ||||||
|  | 	flagBLOB | ||||||
|  | 	flagUnsigned | ||||||
|  | 	flagZeroFill | ||||||
|  | 	flagBinary | ||||||
|  | 	flagEnum | ||||||
|  | 	flagAutoIncrement | ||||||
|  | 	flagTimestamp | ||||||
|  | 	flagSet | ||||||
|  | 	flagUnknown1 | ||||||
|  | 	flagUnknown2 | ||||||
|  | 	flagUnknown3 | ||||||
|  | 	flagUnknown4 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // http://dev.mysql.com/doc/internals/en/status-flags.html | ||||||
|  |  | ||||||
|  | type statusFlag uint16 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	statusInTrans statusFlag = 1 << iota | ||||||
|  | 	statusInAutocommit | ||||||
|  | 	statusReserved // Not in documentation | ||||||
|  | 	statusMoreResultsExists | ||||||
|  | 	statusNoGoodIndexUsed | ||||||
|  | 	statusNoIndexUsed | ||||||
|  | 	statusCursorExists | ||||||
|  | 	statusLastRowSent | ||||||
|  | 	statusDbDropped | ||||||
|  | 	statusNoBackslashEscapes | ||||||
|  | 	statusMetadataChanged | ||||||
|  | 	statusQueryWasSlow | ||||||
|  | 	statusPsOutParams | ||||||
|  | 	statusInTransReadonly | ||||||
|  | 	statusSessionStateChanged | ||||||
|  | ) | ||||||
							
								
								
									
										140
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // The driver should be used via the database/sql package: | ||||||
|  | // | ||||||
|  | //  import "database/sql" | ||||||
|  | //  import _ "github.com/go-sql-driver/mysql" | ||||||
|  | // | ||||||
|  | //  db, err := sql.Open("mysql", "user:password@/dbname") | ||||||
|  | // | ||||||
|  | // See https://github.com/go-sql-driver/mysql#usage for details | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"database/sql" | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"net" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // This struct is exported to make the driver directly accessible. | ||||||
|  | // In general the driver is used via the database/sql package. | ||||||
|  | type MySQLDriver struct{} | ||||||
|  |  | ||||||
|  | // DialFunc is a function which can be used to establish the network connection. | ||||||
|  | // Custom dial functions must be registered with RegisterDial | ||||||
|  | type DialFunc func(addr string) (net.Conn, error) | ||||||
|  |  | ||||||
|  | var dials map[string]DialFunc | ||||||
|  |  | ||||||
|  | // RegisterDial registers a custom dial function. It can then be used by the | ||||||
|  | // network address mynet(addr), where mynet is the registered new network. | ||||||
|  | // addr is passed as a parameter to the dial function. | ||||||
|  | func RegisterDial(net string, dial DialFunc) { | ||||||
|  | 	if dials == nil { | ||||||
|  | 		dials = make(map[string]DialFunc) | ||||||
|  | 	} | ||||||
|  | 	dials[net] = dial | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Open new Connection. | ||||||
|  | // See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how | ||||||
|  | // the DSN string is formated | ||||||
|  | func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	// New mysqlConn | ||||||
|  | 	mc := &mysqlConn{ | ||||||
|  | 		maxPacketAllowed: maxPacketSize, | ||||||
|  | 		maxWriteSize:     maxPacketSize - 1, | ||||||
|  | 	} | ||||||
|  | 	mc.cfg, err = parseDSN(dsn) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Connect to Server | ||||||
|  | 	if dial, ok := dials[mc.cfg.net]; ok { | ||||||
|  | 		mc.netConn, err = dial(mc.cfg.addr) | ||||||
|  | 	} else { | ||||||
|  | 		nd := net.Dialer{Timeout: mc.cfg.timeout} | ||||||
|  | 		mc.netConn, err = nd.Dial(mc.cfg.net, mc.cfg.addr) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Enable TCP Keepalives on TCP connections | ||||||
|  | 	if tc, ok := mc.netConn.(*net.TCPConn); ok { | ||||||
|  | 		if err := tc.SetKeepAlive(true); err != nil { | ||||||
|  | 			// Don't send COM_QUIT before handshake. | ||||||
|  | 			mc.netConn.Close() | ||||||
|  | 			mc.netConn = nil | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mc.buf = newBuffer(mc.netConn) | ||||||
|  |  | ||||||
|  | 	// Reading Handshake Initialization Packet | ||||||
|  | 	cipher, err := mc.readInitPacket() | ||||||
|  | 	if err != nil { | ||||||
|  | 		mc.Close() | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Send Client Authentication Packet | ||||||
|  | 	if err = mc.writeAuthPacket(cipher); err != nil { | ||||||
|  | 		mc.Close() | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Read Result Packet | ||||||
|  | 	err = mc.readResultOK() | ||||||
|  | 	if err != nil { | ||||||
|  | 		// Retry with old authentication method, if allowed | ||||||
|  | 		if mc.cfg != nil && mc.cfg.allowOldPasswords && err == ErrOldPassword { | ||||||
|  | 			if err = mc.writeOldAuthPacket(cipher); err != nil { | ||||||
|  | 				mc.Close() | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			if err = mc.readResultOK(); err != nil { | ||||||
|  | 				mc.Close() | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			mc.Close() | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Get max allowed packet size | ||||||
|  | 	maxap, err := mc.getSystemVar("max_allowed_packet") | ||||||
|  | 	if err != nil { | ||||||
|  | 		mc.Close() | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	mc.maxPacketAllowed = stringToInt(maxap) - 1 | ||||||
|  | 	if mc.maxPacketAllowed < maxPacketSize { | ||||||
|  | 		mc.maxWriteSize = mc.maxPacketAllowed | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Handle DSN Params | ||||||
|  | 	err = mc.handleParams() | ||||||
|  | 	if err != nil { | ||||||
|  | 		mc.Close() | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return mc, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	sql.Register("mysql", &MySQLDriver{}) | ||||||
|  | } | ||||||
							
								
								
									
										1614
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1614
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										129
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Various errors the driver might return. Can change between driver versions. | ||||||
|  | var ( | ||||||
|  | 	ErrInvalidConn = errors.New("Invalid Connection") | ||||||
|  | 	ErrMalformPkt  = errors.New("Malformed Packet") | ||||||
|  | 	ErrNoTLS       = errors.New("TLS encryption requested but server does not support TLS") | ||||||
|  | 	ErrOldPassword = errors.New("This server only supports the insecure old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") | ||||||
|  | 	ErrOldProtocol = errors.New("MySQL-Server does not support required Protocol 41+") | ||||||
|  | 	ErrPktSync     = errors.New("Commands out of sync. You can't run this command now") | ||||||
|  | 	ErrPktSyncMul  = errors.New("Commands out of sync. Did you run multiple statements at once?") | ||||||
|  | 	ErrPktTooLarge = errors.New("Packet for query is too large. You can change this value on the server by adjusting the 'max_allowed_packet' variable.") | ||||||
|  | 	ErrBusyBuffer  = errors.New("Busy buffer") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var errLog Logger = log.New(os.Stderr, "[MySQL] ", log.Ldate|log.Ltime|log.Lshortfile) | ||||||
|  |  | ||||||
|  | // Logger is used to log critical error messages. | ||||||
|  | type Logger interface { | ||||||
|  | 	Print(v ...interface{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetLogger is used to set the logger for critical errors. | ||||||
|  | // The initial logger is os.Stderr. | ||||||
|  | func SetLogger(logger Logger) error { | ||||||
|  | 	if logger == nil { | ||||||
|  | 		return errors.New("logger is nil") | ||||||
|  | 	} | ||||||
|  | 	errLog = logger | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MySQLError is an error type which represents a single MySQL error | ||||||
|  | type MySQLError struct { | ||||||
|  | 	Number  uint16 | ||||||
|  | 	Message string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (me *MySQLError) Error() string { | ||||||
|  | 	return fmt.Sprintf("Error %d: %s", me.Number, me.Message) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MySQLWarnings is an error type which represents a group of one or more MySQL | ||||||
|  | // warnings | ||||||
|  | type MySQLWarnings []MySQLWarning | ||||||
|  |  | ||||||
|  | func (mws MySQLWarnings) Error() string { | ||||||
|  | 	var msg string | ||||||
|  | 	for i, warning := range mws { | ||||||
|  | 		if i > 0 { | ||||||
|  | 			msg += "\r\n" | ||||||
|  | 		} | ||||||
|  | 		msg += fmt.Sprintf( | ||||||
|  | 			"%s %s: %s", | ||||||
|  | 			warning.Level, | ||||||
|  | 			warning.Code, | ||||||
|  | 			warning.Message, | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  | 	return msg | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MySQLWarning is an error type which represents a single MySQL warning. | ||||||
|  | // Warnings are returned in groups only. See MySQLWarnings | ||||||
|  | type MySQLWarning struct { | ||||||
|  | 	Level   string | ||||||
|  | 	Code    string | ||||||
|  | 	Message string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) getWarnings() (err error) { | ||||||
|  | 	rows, err := mc.Query("SHOW WARNINGS", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var warnings = MySQLWarnings{} | ||||||
|  | 	var values = make([]driver.Value, 3) | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		err = rows.Next(values) | ||||||
|  | 		switch err { | ||||||
|  | 		case nil: | ||||||
|  | 			warning := MySQLWarning{} | ||||||
|  |  | ||||||
|  | 			if raw, ok := values[0].([]byte); ok { | ||||||
|  | 				warning.Level = string(raw) | ||||||
|  | 			} else { | ||||||
|  | 				warning.Level = fmt.Sprintf("%s", values[0]) | ||||||
|  | 			} | ||||||
|  | 			if raw, ok := values[1].([]byte); ok { | ||||||
|  | 				warning.Code = string(raw) | ||||||
|  | 			} else { | ||||||
|  | 				warning.Code = fmt.Sprintf("%s", values[1]) | ||||||
|  | 			} | ||||||
|  | 			if raw, ok := values[2].([]byte); ok { | ||||||
|  | 				warning.Message = string(raw) | ||||||
|  | 			} else { | ||||||
|  | 				warning.Message = fmt.Sprintf("%s", values[0]) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			warnings = append(warnings, warning) | ||||||
|  |  | ||||||
|  | 		case io.EOF: | ||||||
|  | 			return warnings | ||||||
|  |  | ||||||
|  | 		default: | ||||||
|  | 			rows.Close() | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"log" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestErrorsSetLogger(t *testing.T) { | ||||||
|  | 	previous := errLog | ||||||
|  | 	defer func() { | ||||||
|  | 		errLog = previous | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	// set up logger | ||||||
|  | 	const expected = "prefix: test\n" | ||||||
|  | 	buffer := bytes.NewBuffer(make([]byte, 0, 64)) | ||||||
|  | 	logger := log.New(buffer, "prefix: ", 0) | ||||||
|  |  | ||||||
|  | 	// print | ||||||
|  | 	SetLogger(logger) | ||||||
|  | 	errLog.Print("test") | ||||||
|  |  | ||||||
|  | 	// check result | ||||||
|  | 	if actual := buffer.String(); actual != expected { | ||||||
|  | 		t.Errorf("expected %q, got %q", expected, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestErrorsStrictIgnoreNotes(t *testing.T) { | ||||||
|  | 	runTests(t, dsn+"&sql_notes=false", func(dbt *DBTest) { | ||||||
|  | 		dbt.mustExec("DROP TABLE IF EXISTS does_not_exist") | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										162
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	fileRegister   map[string]bool | ||||||
|  | 	readerRegister map[string]func() io.Reader | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // RegisterLocalFile adds the given file to the file whitelist, | ||||||
|  | // so that it can be used by "LOAD DATA LOCAL INFILE <filepath>". | ||||||
|  | // Alternatively you can allow the use of all local files with | ||||||
|  | // the DSN parameter 'allowAllFiles=true' | ||||||
|  | // | ||||||
|  | //  filePath := "/home/gopher/data.csv" | ||||||
|  | //  mysql.RegisterLocalFile(filePath) | ||||||
|  | //  err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo") | ||||||
|  | //  if err != nil { | ||||||
|  | //  ... | ||||||
|  | // | ||||||
|  | func RegisterLocalFile(filePath string) { | ||||||
|  | 	// lazy map init | ||||||
|  | 	if fileRegister == nil { | ||||||
|  | 		fileRegister = make(map[string]bool) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fileRegister[strings.Trim(filePath, `"`)] = true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeregisterLocalFile removes the given filepath from the whitelist. | ||||||
|  | func DeregisterLocalFile(filePath string) { | ||||||
|  | 	delete(fileRegister, strings.Trim(filePath, `"`)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RegisterReaderHandler registers a handler function which is used | ||||||
|  | // to receive a io.Reader. | ||||||
|  | // The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>". | ||||||
|  | // If the handler returns a io.ReadCloser Close() is called when the | ||||||
|  | // request is finished. | ||||||
|  | // | ||||||
|  | //  mysql.RegisterReaderHandler("data", func() io.Reader { | ||||||
|  | //  	var csvReader io.Reader // Some Reader that returns CSV data | ||||||
|  | //  	... // Open Reader here | ||||||
|  | //  	return csvReader | ||||||
|  | //  }) | ||||||
|  | //  err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo") | ||||||
|  | //  if err != nil { | ||||||
|  | //  ... | ||||||
|  | // | ||||||
|  | func RegisterReaderHandler(name string, handler func() io.Reader) { | ||||||
|  | 	// lazy map init | ||||||
|  | 	if readerRegister == nil { | ||||||
|  | 		readerRegister = make(map[string]func() io.Reader) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	readerRegister[name] = handler | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeregisterReaderHandler removes the ReaderHandler function with | ||||||
|  | // the given name from the registry. | ||||||
|  | func DeregisterReaderHandler(name string) { | ||||||
|  | 	delete(readerRegister, name) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func deferredClose(err *error, closer io.Closer) { | ||||||
|  | 	closeErr := closer.Close() | ||||||
|  | 	if *err == nil { | ||||||
|  | 		*err = closeErr | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mc *mysqlConn) handleInFileRequest(name string) (err error) { | ||||||
|  | 	var rdr io.Reader | ||||||
|  | 	var data []byte | ||||||
|  |  | ||||||
|  | 	if strings.HasPrefix(name, "Reader::") { // io.Reader | ||||||
|  | 		name = name[8:] | ||||||
|  | 		if handler, inMap := readerRegister[name]; inMap { | ||||||
|  | 			rdr = handler() | ||||||
|  | 			if rdr != nil { | ||||||
|  | 				data = make([]byte, 4+mc.maxWriteSize) | ||||||
|  |  | ||||||
|  | 				if cl, ok := rdr.(io.Closer); ok { | ||||||
|  | 					defer deferredClose(&err, cl) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				err = fmt.Errorf("Reader '%s' is <nil>", name) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			err = fmt.Errorf("Reader '%s' is not registered", name) | ||||||
|  | 		} | ||||||
|  | 	} else { // File | ||||||
|  | 		name = strings.Trim(name, `"`) | ||||||
|  | 		if mc.cfg.allowAllFiles || fileRegister[name] { | ||||||
|  | 			var file *os.File | ||||||
|  | 			var fi os.FileInfo | ||||||
|  |  | ||||||
|  | 			if file, err = os.Open(name); err == nil { | ||||||
|  | 				defer deferredClose(&err, file) | ||||||
|  |  | ||||||
|  | 				// get file size | ||||||
|  | 				if fi, err = file.Stat(); err == nil { | ||||||
|  | 					rdr = file | ||||||
|  | 					if fileSize := int(fi.Size()); fileSize <= mc.maxWriteSize { | ||||||
|  | 						data = make([]byte, 4+fileSize) | ||||||
|  | 					} else if fileSize <= mc.maxPacketAllowed { | ||||||
|  | 						data = make([]byte, 4+mc.maxWriteSize) | ||||||
|  | 					} else { | ||||||
|  | 						err = fmt.Errorf("Local File '%s' too large: Size: %d, Max: %d", name, fileSize, mc.maxPacketAllowed) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			err = fmt.Errorf("Local File '%s' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files", name) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// send content packets | ||||||
|  | 	if err == nil { | ||||||
|  | 		var n int | ||||||
|  | 		for err == nil { | ||||||
|  | 			n, err = rdr.Read(data[4:]) | ||||||
|  | 			if n > 0 { | ||||||
|  | 				if ioErr := mc.writePacket(data[:4+n]); ioErr != nil { | ||||||
|  | 					return ioErr | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if err == io.EOF { | ||||||
|  | 			err = nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// send empty packet (termination) | ||||||
|  | 	if data == nil { | ||||||
|  | 		data = make([]byte, 4) | ||||||
|  | 	} | ||||||
|  | 	if ioErr := mc.writePacket(data[:4]); ioErr != nil { | ||||||
|  | 		return ioErr | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// read OK packet | ||||||
|  | 	if err == nil { | ||||||
|  | 		return mc.readResultOK() | ||||||
|  | 	} else { | ||||||
|  | 		mc.readPacket() | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
							
								
								
									
										1138
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1138
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										22
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | type mysqlResult struct { | ||||||
|  | 	affectedRows int64 | ||||||
|  | 	insertId     int64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (res *mysqlResult) LastInsertId() (int64, error) { | ||||||
|  | 	return res.insertId, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (res *mysqlResult) RowsAffected() (int64, error) { | ||||||
|  | 	return res.affectedRows, nil | ||||||
|  | } | ||||||
							
								
								
									
										102
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"io" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type mysqlField struct { | ||||||
|  | 	tableName string | ||||||
|  | 	name      string | ||||||
|  | 	flags     fieldFlag | ||||||
|  | 	fieldType byte | ||||||
|  | 	decimals  byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type mysqlRows struct { | ||||||
|  | 	mc      *mysqlConn | ||||||
|  | 	columns []mysqlField | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type binaryRows struct { | ||||||
|  | 	mysqlRows | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type textRows struct { | ||||||
|  | 	mysqlRows | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type emptyRows struct{} | ||||||
|  |  | ||||||
|  | func (rows *mysqlRows) Columns() []string { | ||||||
|  | 	columns := make([]string, len(rows.columns)) | ||||||
|  | 	if rows.mc.cfg.columnsWithAlias { | ||||||
|  | 		for i := range columns { | ||||||
|  | 			columns[i] = rows.columns[i].tableName + "." + rows.columns[i].name | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		for i := range columns { | ||||||
|  | 			columns[i] = rows.columns[i].name | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return columns | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (rows *mysqlRows) Close() error { | ||||||
|  | 	mc := rows.mc | ||||||
|  | 	if mc == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if mc.netConn == nil { | ||||||
|  | 		return ErrInvalidConn | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Remove unread packets from stream | ||||||
|  | 	err := mc.readUntilEOF() | ||||||
|  | 	rows.mc = nil | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (rows *binaryRows) Next(dest []driver.Value) error { | ||||||
|  | 	if mc := rows.mc; mc != nil { | ||||||
|  | 		if mc.netConn == nil { | ||||||
|  | 			return ErrInvalidConn | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Fetch next row from stream | ||||||
|  | 		return rows.readRow(dest) | ||||||
|  | 	} | ||||||
|  | 	return io.EOF | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (rows *textRows) Next(dest []driver.Value) error { | ||||||
|  | 	if mc := rows.mc; mc != nil { | ||||||
|  | 		if mc.netConn == nil { | ||||||
|  | 			return ErrInvalidConn | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Fetch next row from stream | ||||||
|  | 		return rows.readRow(dest) | ||||||
|  | 	} | ||||||
|  | 	return io.EOF | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (rows emptyRows) Columns() []string { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (rows emptyRows) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (rows emptyRows) Next(dest []driver.Value) error { | ||||||
|  | 	return io.EOF | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"database/sql/driver" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type mysqlStmt struct { | ||||||
|  | 	mc         *mysqlConn | ||||||
|  | 	id         uint32 | ||||||
|  | 	paramCount int | ||||||
|  | 	columns    []mysqlField // cached from the first query | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (stmt *mysqlStmt) Close() error { | ||||||
|  | 	if stmt.mc == nil || stmt.mc.netConn == nil { | ||||||
|  | 		errLog.Print(ErrInvalidConn) | ||||||
|  | 		return driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id) | ||||||
|  | 	stmt.mc = nil | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (stmt *mysqlStmt) NumInput() int { | ||||||
|  | 	return stmt.paramCount | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { | ||||||
|  | 	if stmt.mc.netConn == nil { | ||||||
|  | 		errLog.Print(ErrInvalidConn) | ||||||
|  | 		return nil, driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  | 	// Send command | ||||||
|  | 	err := stmt.writeExecutePacket(args) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mc := stmt.mc | ||||||
|  |  | ||||||
|  | 	mc.affectedRows = 0 | ||||||
|  | 	mc.insertId = 0 | ||||||
|  |  | ||||||
|  | 	// Read Result | ||||||
|  | 	resLen, err := mc.readResultSetHeaderPacket() | ||||||
|  | 	if err == nil { | ||||||
|  | 		if resLen > 0 { | ||||||
|  | 			// Columns | ||||||
|  | 			err = mc.readUntilEOF() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Rows | ||||||
|  | 			err = mc.readUntilEOF() | ||||||
|  | 		} | ||||||
|  | 		if err == nil { | ||||||
|  | 			return &mysqlResult{ | ||||||
|  | 				affectedRows: int64(mc.affectedRows), | ||||||
|  | 				insertId:     int64(mc.insertId), | ||||||
|  | 			}, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { | ||||||
|  | 	if stmt.mc.netConn == nil { | ||||||
|  | 		errLog.Print(ErrInvalidConn) | ||||||
|  | 		return nil, driver.ErrBadConn | ||||||
|  | 	} | ||||||
|  | 	// Send command | ||||||
|  | 	err := stmt.writeExecutePacket(args) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mc := stmt.mc | ||||||
|  |  | ||||||
|  | 	// Read Result | ||||||
|  | 	resLen, err := mc.readResultSetHeaderPacket() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rows := new(binaryRows) | ||||||
|  | 	rows.mc = mc | ||||||
|  |  | ||||||
|  | 	if resLen > 0 { | ||||||
|  | 		// Columns | ||||||
|  | 		// If not cached, read them and cache them | ||||||
|  | 		if stmt.columns == nil { | ||||||
|  | 			rows.columns, err = mc.readColumns(resLen) | ||||||
|  | 			stmt.columns = rows.columns | ||||||
|  | 		} else { | ||||||
|  | 			rows.columns = stmt.columns | ||||||
|  | 			err = mc.readUntilEOF() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return rows, err | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | type mysqlTx struct { | ||||||
|  | 	mc *mysqlConn | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (tx *mysqlTx) Commit() (err error) { | ||||||
|  | 	if tx.mc == nil || tx.mc.netConn == nil { | ||||||
|  | 		return ErrInvalidConn | ||||||
|  | 	} | ||||||
|  | 	err = tx.mc.exec("COMMIT") | ||||||
|  | 	tx.mc = nil | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (tx *mysqlTx) Rollback() (err error) { | ||||||
|  | 	if tx.mc == nil || tx.mc.netConn == nil { | ||||||
|  | 		return ErrInvalidConn | ||||||
|  | 	} | ||||||
|  | 	err = tx.mc.exec("ROLLBACK") | ||||||
|  | 	tx.mc = nil | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										963
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										963
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,963 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/sha1" | ||||||
|  | 	"crypto/tls" | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"encoding/binary" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"net" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs | ||||||
|  |  | ||||||
|  | 	errInvalidDSNUnescaped       = errors.New("Invalid DSN: Did you forget to escape a param value?") | ||||||
|  | 	errInvalidDSNAddr            = errors.New("Invalid DSN: Network Address not terminated (missing closing brace)") | ||||||
|  | 	errInvalidDSNNoSlash         = errors.New("Invalid DSN: Missing the slash separating the database name") | ||||||
|  | 	errInvalidDSNUnsafeCollation = errors.New("Invalid DSN: interpolateParams can be used with ascii, latin1, utf8 and utf8mb4 charset") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	tlsConfigRegister = make(map[string]*tls.Config) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RegisterTLSConfig registers a custom tls.Config to be used with sql.Open. | ||||||
|  | // Use the key as a value in the DSN where tls=value. | ||||||
|  | // | ||||||
|  | //  rootCertPool := x509.NewCertPool() | ||||||
|  | //  pem, err := ioutil.ReadFile("/path/ca-cert.pem") | ||||||
|  | //  if err != nil { | ||||||
|  | //      log.Fatal(err) | ||||||
|  | //  } | ||||||
|  | //  if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { | ||||||
|  | //      log.Fatal("Failed to append PEM.") | ||||||
|  | //  } | ||||||
|  | //  clientCert := make([]tls.Certificate, 0, 1) | ||||||
|  | //  certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem") | ||||||
|  | //  if err != nil { | ||||||
|  | //      log.Fatal(err) | ||||||
|  | //  } | ||||||
|  | //  clientCert = append(clientCert, certs) | ||||||
|  | //  mysql.RegisterTLSConfig("custom", &tls.Config{ | ||||||
|  | //      RootCAs: rootCertPool, | ||||||
|  | //      Certificates: clientCert, | ||||||
|  | //  }) | ||||||
|  | //  db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom") | ||||||
|  | // | ||||||
|  | func RegisterTLSConfig(key string, config *tls.Config) error { | ||||||
|  | 	if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" { | ||||||
|  | 		return fmt.Errorf("Key '%s' is reserved", key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tlsConfigRegister[key] = config | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeregisterTLSConfig removes the tls.Config associated with key. | ||||||
|  | func DeregisterTLSConfig(key string) { | ||||||
|  | 	delete(tlsConfigRegister, key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // parseDSN parses the DSN string to a config | ||||||
|  | func parseDSN(dsn string) (cfg *config, err error) { | ||||||
|  | 	// New config with some default values | ||||||
|  | 	cfg = &config{ | ||||||
|  | 		loc:       time.UTC, | ||||||
|  | 		collation: defaultCollation, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: use strings.IndexByte when we can depend on Go 1.2 | ||||||
|  |  | ||||||
|  | 	// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN] | ||||||
|  | 	// Find the last '/' (since the password or the net addr might contain a '/') | ||||||
|  | 	foundSlash := false | ||||||
|  | 	for i := len(dsn) - 1; i >= 0; i-- { | ||||||
|  | 		if dsn[i] == '/' { | ||||||
|  | 			foundSlash = true | ||||||
|  | 			var j, k int | ||||||
|  |  | ||||||
|  | 			// left part is empty if i <= 0 | ||||||
|  | 			if i > 0 { | ||||||
|  | 				// [username[:password]@][protocol[(address)]] | ||||||
|  | 				// Find the last '@' in dsn[:i] | ||||||
|  | 				for j = i; j >= 0; j-- { | ||||||
|  | 					if dsn[j] == '@' { | ||||||
|  | 						// username[:password] | ||||||
|  | 						// Find the first ':' in dsn[:j] | ||||||
|  | 						for k = 0; k < j; k++ { | ||||||
|  | 							if dsn[k] == ':' { | ||||||
|  | 								cfg.passwd = dsn[k+1 : j] | ||||||
|  | 								break | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 						cfg.user = dsn[:k] | ||||||
|  |  | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// [protocol[(address)]] | ||||||
|  | 				// Find the first '(' in dsn[j+1:i] | ||||||
|  | 				for k = j + 1; k < i; k++ { | ||||||
|  | 					if dsn[k] == '(' { | ||||||
|  | 						// dsn[i-1] must be == ')' if an address is specified | ||||||
|  | 						if dsn[i-1] != ')' { | ||||||
|  | 							if strings.ContainsRune(dsn[k+1:i], ')') { | ||||||
|  | 								return nil, errInvalidDSNUnescaped | ||||||
|  | 							} | ||||||
|  | 							return nil, errInvalidDSNAddr | ||||||
|  | 						} | ||||||
|  | 						cfg.addr = dsn[k+1 : i-1] | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				cfg.net = dsn[j+1 : k] | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// dbname[?param1=value1&...¶mN=valueN] | ||||||
|  | 			// Find the first '?' in dsn[i+1:] | ||||||
|  | 			for j = i + 1; j < len(dsn); j++ { | ||||||
|  | 				if dsn[j] == '?' { | ||||||
|  | 					if err = parseDSNParams(cfg, dsn[j+1:]); err != nil { | ||||||
|  | 						return | ||||||
|  | 					} | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			cfg.dbname = dsn[i+1 : j] | ||||||
|  |  | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !foundSlash && len(dsn) > 0 { | ||||||
|  | 		return nil, errInvalidDSNNoSlash | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if cfg.interpolateParams && unsafeCollations[cfg.collation] { | ||||||
|  | 		return nil, errInvalidDSNUnsafeCollation | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Set default network if empty | ||||||
|  | 	if cfg.net == "" { | ||||||
|  | 		cfg.net = "tcp" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Set default address if empty | ||||||
|  | 	if cfg.addr == "" { | ||||||
|  | 		switch cfg.net { | ||||||
|  | 		case "tcp": | ||||||
|  | 			cfg.addr = "127.0.0.1:3306" | ||||||
|  | 		case "unix": | ||||||
|  | 			cfg.addr = "/tmp/mysql.sock" | ||||||
|  | 		default: | ||||||
|  | 			return nil, errors.New("Default addr for network '" + cfg.net + "' unknown") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // parseDSNParams parses the DSN "query string" | ||||||
|  | // Values must be url.QueryEscape'ed | ||||||
|  | func parseDSNParams(cfg *config, params string) (err error) { | ||||||
|  | 	for _, v := range strings.Split(params, "&") { | ||||||
|  | 		param := strings.SplitN(v, "=", 2) | ||||||
|  | 		if len(param) != 2 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// cfg params | ||||||
|  | 		switch value := param[1]; param[0] { | ||||||
|  |  | ||||||
|  | 		// Enable client side placeholder substitution | ||||||
|  | 		case "interpolateParams": | ||||||
|  | 			var isBool bool | ||||||
|  | 			cfg.interpolateParams, isBool = readBool(value) | ||||||
|  | 			if !isBool { | ||||||
|  | 				return fmt.Errorf("Invalid Bool value: %s", value) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Disable INFILE whitelist / enable all files | ||||||
|  | 		case "allowAllFiles": | ||||||
|  | 			var isBool bool | ||||||
|  | 			cfg.allowAllFiles, isBool = readBool(value) | ||||||
|  | 			if !isBool { | ||||||
|  | 				return fmt.Errorf("Invalid Bool value: %s", value) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Use old authentication mode (pre MySQL 4.1) | ||||||
|  | 		case "allowOldPasswords": | ||||||
|  | 			var isBool bool | ||||||
|  | 			cfg.allowOldPasswords, isBool = readBool(value) | ||||||
|  | 			if !isBool { | ||||||
|  | 				return fmt.Errorf("Invalid Bool value: %s", value) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Switch "rowsAffected" mode | ||||||
|  | 		case "clientFoundRows": | ||||||
|  | 			var isBool bool | ||||||
|  | 			cfg.clientFoundRows, isBool = readBool(value) | ||||||
|  | 			if !isBool { | ||||||
|  | 				return fmt.Errorf("Invalid Bool value: %s", value) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Collation | ||||||
|  | 		case "collation": | ||||||
|  | 			collation, ok := collations[value] | ||||||
|  | 			if !ok { | ||||||
|  | 				// Note possibility for false negatives: | ||||||
|  | 				// could be triggered  although the collation is valid if the | ||||||
|  | 				// collations map does not contain entries the server supports. | ||||||
|  | 				err = errors.New("unknown collation") | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			cfg.collation = collation | ||||||
|  | 			break | ||||||
|  |  | ||||||
|  | 		case "columnsWithAlias": | ||||||
|  | 			var isBool bool | ||||||
|  | 			cfg.columnsWithAlias, isBool = readBool(value) | ||||||
|  | 			if !isBool { | ||||||
|  | 				return fmt.Errorf("Invalid Bool value: %s", value) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Time Location | ||||||
|  | 		case "loc": | ||||||
|  | 			if value, err = url.QueryUnescape(value); err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			cfg.loc, err = time.LoadLocation(value) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// Dial Timeout | ||||||
|  | 		case "timeout": | ||||||
|  | 			cfg.timeout, err = time.ParseDuration(value) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		// TLS-Encryption | ||||||
|  | 		case "tls": | ||||||
|  | 			boolValue, isBool := readBool(value) | ||||||
|  | 			if isBool { | ||||||
|  | 				if boolValue { | ||||||
|  | 					cfg.tls = &tls.Config{} | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				if strings.ToLower(value) == "skip-verify" { | ||||||
|  | 					cfg.tls = &tls.Config{InsecureSkipVerify: true} | ||||||
|  | 				} else if tlsConfig, ok := tlsConfigRegister[value]; ok { | ||||||
|  | 					if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify { | ||||||
|  | 						host, _, err := net.SplitHostPort(cfg.addr) | ||||||
|  | 						if err == nil { | ||||||
|  | 							tlsConfig.ServerName = host | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					cfg.tls = tlsConfig | ||||||
|  | 				} else { | ||||||
|  | 					return fmt.Errorf("Invalid value / unknown config name: %s", value) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		default: | ||||||
|  | 			// lazy init | ||||||
|  | 			if cfg.params == nil { | ||||||
|  | 				cfg.params = make(map[string]string) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if cfg.params[param[0]], err = url.QueryUnescape(value); err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Returns the bool value of the input. | ||||||
|  | // The 2nd return value indicates if the input was a valid bool value | ||||||
|  | func readBool(input string) (value bool, valid bool) { | ||||||
|  | 	switch input { | ||||||
|  | 	case "1", "true", "TRUE", "True": | ||||||
|  | 		return true, true | ||||||
|  | 	case "0", "false", "FALSE", "False": | ||||||
|  | 		return false, true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Not a valid bool value | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****************************************************************************** | ||||||
|  | *                             Authentication                                  * | ||||||
|  | ******************************************************************************/ | ||||||
|  |  | ||||||
|  | // Encrypt password using 4.1+ method | ||||||
|  | func scramblePassword(scramble, password []byte) []byte { | ||||||
|  | 	if len(password) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// stage1Hash = SHA1(password) | ||||||
|  | 	crypt := sha1.New() | ||||||
|  | 	crypt.Write(password) | ||||||
|  | 	stage1 := crypt.Sum(nil) | ||||||
|  |  | ||||||
|  | 	// scrambleHash = SHA1(scramble + SHA1(stage1Hash)) | ||||||
|  | 	// inner Hash | ||||||
|  | 	crypt.Reset() | ||||||
|  | 	crypt.Write(stage1) | ||||||
|  | 	hash := crypt.Sum(nil) | ||||||
|  |  | ||||||
|  | 	// outer Hash | ||||||
|  | 	crypt.Reset() | ||||||
|  | 	crypt.Write(scramble) | ||||||
|  | 	crypt.Write(hash) | ||||||
|  | 	scramble = crypt.Sum(nil) | ||||||
|  |  | ||||||
|  | 	// token = scrambleHash XOR stage1Hash | ||||||
|  | 	for i := range scramble { | ||||||
|  | 		scramble[i] ^= stage1[i] | ||||||
|  | 	} | ||||||
|  | 	return scramble | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Encrypt password using pre 4.1 (old password) method | ||||||
|  | // https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c | ||||||
|  | type myRnd struct { | ||||||
|  | 	seed1, seed2 uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const myRndMaxVal = 0x3FFFFFFF | ||||||
|  |  | ||||||
|  | // Pseudo random number generator | ||||||
|  | func newMyRnd(seed1, seed2 uint32) *myRnd { | ||||||
|  | 	return &myRnd{ | ||||||
|  | 		seed1: seed1 % myRndMaxVal, | ||||||
|  | 		seed2: seed2 % myRndMaxVal, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Tested to be equivalent to MariaDB's floating point variant | ||||||
|  | // http://play.golang.org/p/QHvhd4qved | ||||||
|  | // http://play.golang.org/p/RG0q4ElWDx | ||||||
|  | func (r *myRnd) NextByte() byte { | ||||||
|  | 	r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal | ||||||
|  | 	r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal | ||||||
|  |  | ||||||
|  | 	return byte(uint64(r.seed1) * 31 / myRndMaxVal) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Generate binary hash from byte string using insecure pre 4.1 method | ||||||
|  | func pwHash(password []byte) (result [2]uint32) { | ||||||
|  | 	var add uint32 = 7 | ||||||
|  | 	var tmp uint32 | ||||||
|  |  | ||||||
|  | 	result[0] = 1345345333 | ||||||
|  | 	result[1] = 0x12345671 | ||||||
|  |  | ||||||
|  | 	for _, c := range password { | ||||||
|  | 		// skip spaces and tabs in password | ||||||
|  | 		if c == ' ' || c == '\t' { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		tmp = uint32(c) | ||||||
|  | 		result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8) | ||||||
|  | 		result[1] += (result[1] << 8) ^ result[0] | ||||||
|  | 		add += tmp | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Remove sign bit (1<<31)-1) | ||||||
|  | 	result[0] &= 0x7FFFFFFF | ||||||
|  | 	result[1] &= 0x7FFFFFFF | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Encrypt password using insecure pre 4.1 method | ||||||
|  | func scrambleOldPassword(scramble, password []byte) []byte { | ||||||
|  | 	if len(password) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	scramble = scramble[:8] | ||||||
|  |  | ||||||
|  | 	hashPw := pwHash(password) | ||||||
|  | 	hashSc := pwHash(scramble) | ||||||
|  |  | ||||||
|  | 	r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1]) | ||||||
|  |  | ||||||
|  | 	var out [8]byte | ||||||
|  | 	for i := range out { | ||||||
|  | 		out[i] = r.NextByte() + 64 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mask := r.NextByte() | ||||||
|  | 	for i := range out { | ||||||
|  | 		out[i] ^= mask | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return out[:] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****************************************************************************** | ||||||
|  | *                           Time related utils                                * | ||||||
|  | ******************************************************************************/ | ||||||
|  |  | ||||||
|  | // NullTime represents a time.Time that may be NULL. | ||||||
|  | // NullTime implements the Scanner interface so | ||||||
|  | // it can be used as a scan destination: | ||||||
|  | // | ||||||
|  | //  var nt NullTime | ||||||
|  | //  err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt) | ||||||
|  | //  ... | ||||||
|  | //  if nt.Valid { | ||||||
|  | //     // use nt.Time | ||||||
|  | //  } else { | ||||||
|  | //     // NULL value | ||||||
|  | //  } | ||||||
|  | // | ||||||
|  | // This NullTime implementation is not driver-specific | ||||||
|  | type NullTime struct { | ||||||
|  | 	Time  time.Time | ||||||
|  | 	Valid bool // Valid is true if Time is not NULL | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Scan implements the Scanner interface. | ||||||
|  | // The value type must be time.Time or string / []byte (formatted time-string), | ||||||
|  | // otherwise Scan fails. | ||||||
|  | func (nt *NullTime) Scan(value interface{}) (err error) { | ||||||
|  | 	if value == nil { | ||||||
|  | 		nt.Time, nt.Valid = time.Time{}, false | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch v := value.(type) { | ||||||
|  | 	case time.Time: | ||||||
|  | 		nt.Time, nt.Valid = v, true | ||||||
|  | 		return | ||||||
|  | 	case []byte: | ||||||
|  | 		nt.Time, err = parseDateTime(string(v), time.UTC) | ||||||
|  | 		nt.Valid = (err == nil) | ||||||
|  | 		return | ||||||
|  | 	case string: | ||||||
|  | 		nt.Time, err = parseDateTime(v, time.UTC) | ||||||
|  | 		nt.Valid = (err == nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	nt.Valid = false | ||||||
|  | 	return fmt.Errorf("Can't convert %T to time.Time", value) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Value implements the driver Valuer interface. | ||||||
|  | func (nt NullTime) Value() (driver.Value, error) { | ||||||
|  | 	if !nt.Valid { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 	return nt.Time, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { | ||||||
|  | 	base := "0000-00-00 00:00:00.0000000" | ||||||
|  | 	switch len(str) { | ||||||
|  | 	case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" | ||||||
|  | 		if str == base[:len(str)] { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		t, err = time.Parse(timeFormat[:len(str)], str) | ||||||
|  | 	default: | ||||||
|  | 		err = fmt.Errorf("Invalid Time-String: %s", str) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Adjust location | ||||||
|  | 	if err == nil && loc != time.UTC { | ||||||
|  | 		y, mo, d := t.Date() | ||||||
|  | 		h, mi, s := t.Clock() | ||||||
|  | 		t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) { | ||||||
|  | 	switch num { | ||||||
|  | 	case 0: | ||||||
|  | 		return time.Time{}, nil | ||||||
|  | 	case 4: | ||||||
|  | 		return time.Date( | ||||||
|  | 			int(binary.LittleEndian.Uint16(data[:2])), // year | ||||||
|  | 			time.Month(data[2]),                       // month | ||||||
|  | 			int(data[3]),                              // day | ||||||
|  | 			0, 0, 0, 0, | ||||||
|  | 			loc, | ||||||
|  | 		), nil | ||||||
|  | 	case 7: | ||||||
|  | 		return time.Date( | ||||||
|  | 			int(binary.LittleEndian.Uint16(data[:2])), // year | ||||||
|  | 			time.Month(data[2]),                       // month | ||||||
|  | 			int(data[3]),                              // day | ||||||
|  | 			int(data[4]),                              // hour | ||||||
|  | 			int(data[5]),                              // minutes | ||||||
|  | 			int(data[6]),                              // seconds | ||||||
|  | 			0, | ||||||
|  | 			loc, | ||||||
|  | 		), nil | ||||||
|  | 	case 11: | ||||||
|  | 		return time.Date( | ||||||
|  | 			int(binary.LittleEndian.Uint16(data[:2])), // year | ||||||
|  | 			time.Month(data[2]),                       // month | ||||||
|  | 			int(data[3]),                              // day | ||||||
|  | 			int(data[4]),                              // hour | ||||||
|  | 			int(data[5]),                              // minutes | ||||||
|  | 			int(data[6]),                              // seconds | ||||||
|  | 			int(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds | ||||||
|  | 			loc, | ||||||
|  | 		), nil | ||||||
|  | 	} | ||||||
|  | 	return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // zeroDateTime is used in formatBinaryDateTime to avoid an allocation | ||||||
|  | // if the DATE or DATETIME has the zero value. | ||||||
|  | // It must never be changed. | ||||||
|  | // The current behavior depends on database/sql copying the result. | ||||||
|  | var zeroDateTime = []byte("0000-00-00 00:00:00.000000") | ||||||
|  |  | ||||||
|  | const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" | ||||||
|  | const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" | ||||||
|  |  | ||||||
|  | func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) { | ||||||
|  | 	// length expects the deterministic length of the zero value, | ||||||
|  | 	// negative time and 100+ hours are automatically added if needed | ||||||
|  | 	if len(src) == 0 { | ||||||
|  | 		if justTime { | ||||||
|  | 			return zeroDateTime[11 : 11+length], nil | ||||||
|  | 		} | ||||||
|  | 		return zeroDateTime[:length], nil | ||||||
|  | 	} | ||||||
|  | 	var dst []byte          // return value | ||||||
|  | 	var pt, p1, p2, p3 byte // current digit pair | ||||||
|  | 	var zOffs byte          // offset of value in zeroDateTime | ||||||
|  | 	if justTime { | ||||||
|  | 		switch length { | ||||||
|  | 		case | ||||||
|  | 			8,                      // time (can be up to 10 when negative and 100+ hours) | ||||||
|  | 			10, 11, 12, 13, 14, 15: // time with fractional seconds | ||||||
|  | 		default: | ||||||
|  | 			return nil, fmt.Errorf("illegal TIME length %d", length) | ||||||
|  | 		} | ||||||
|  | 		switch len(src) { | ||||||
|  | 		case 8, 12: | ||||||
|  | 		default: | ||||||
|  | 			return nil, fmt.Errorf("Invalid TIME-packet length %d", len(src)) | ||||||
|  | 		} | ||||||
|  | 		// +2 to enable negative time and 100+ hours | ||||||
|  | 		dst = make([]byte, 0, length+2) | ||||||
|  | 		if src[0] == 1 { | ||||||
|  | 			dst = append(dst, '-') | ||||||
|  | 		} | ||||||
|  | 		if src[1] != 0 { | ||||||
|  | 			hour := uint16(src[1])*24 + uint16(src[5]) | ||||||
|  | 			pt = byte(hour / 100) | ||||||
|  | 			p1 = byte(hour - 100*uint16(pt)) | ||||||
|  | 			dst = append(dst, digits01[pt]) | ||||||
|  | 		} else { | ||||||
|  | 			p1 = src[5] | ||||||
|  | 		} | ||||||
|  | 		zOffs = 11 | ||||||
|  | 		src = src[6:] | ||||||
|  | 	} else { | ||||||
|  | 		switch length { | ||||||
|  | 		case 10, 19, 21, 22, 23, 24, 25, 26: | ||||||
|  | 		default: | ||||||
|  | 			t := "DATE" | ||||||
|  | 			if length > 10 { | ||||||
|  | 				t += "TIME" | ||||||
|  | 			} | ||||||
|  | 			return nil, fmt.Errorf("illegal %s length %d", t, length) | ||||||
|  | 		} | ||||||
|  | 		switch len(src) { | ||||||
|  | 		case 4, 7, 11: | ||||||
|  | 		default: | ||||||
|  | 			t := "DATE" | ||||||
|  | 			if length > 10 { | ||||||
|  | 				t += "TIME" | ||||||
|  | 			} | ||||||
|  | 			return nil, fmt.Errorf("illegal %s-packet length %d", t, len(src)) | ||||||
|  | 		} | ||||||
|  | 		dst = make([]byte, 0, length) | ||||||
|  | 		// start with the date | ||||||
|  | 		year := binary.LittleEndian.Uint16(src[:2]) | ||||||
|  | 		pt = byte(year / 100) | ||||||
|  | 		p1 = byte(year - 100*uint16(pt)) | ||||||
|  | 		p2, p3 = src[2], src[3] | ||||||
|  | 		dst = append(dst, | ||||||
|  | 			digits10[pt], digits01[pt], | ||||||
|  | 			digits10[p1], digits01[p1], '-', | ||||||
|  | 			digits10[p2], digits01[p2], '-', | ||||||
|  | 			digits10[p3], digits01[p3], | ||||||
|  | 		) | ||||||
|  | 		if length == 10 { | ||||||
|  | 			return dst, nil | ||||||
|  | 		} | ||||||
|  | 		if len(src) == 4 { | ||||||
|  | 			return append(dst, zeroDateTime[10:length]...), nil | ||||||
|  | 		} | ||||||
|  | 		dst = append(dst, ' ') | ||||||
|  | 		p1 = src[4] // hour | ||||||
|  | 		src = src[5:] | ||||||
|  | 	} | ||||||
|  | 	// p1 is 2-digit hour, src is after hour | ||||||
|  | 	p2, p3 = src[0], src[1] | ||||||
|  | 	dst = append(dst, | ||||||
|  | 		digits10[p1], digits01[p1], ':', | ||||||
|  | 		digits10[p2], digits01[p2], ':', | ||||||
|  | 		digits10[p3], digits01[p3], | ||||||
|  | 	) | ||||||
|  | 	if length <= byte(len(dst)) { | ||||||
|  | 		return dst, nil | ||||||
|  | 	} | ||||||
|  | 	src = src[2:] | ||||||
|  | 	if len(src) == 0 { | ||||||
|  | 		return append(dst, zeroDateTime[19:zOffs+length]...), nil | ||||||
|  | 	} | ||||||
|  | 	microsecs := binary.LittleEndian.Uint32(src[:4]) | ||||||
|  | 	p1 = byte(microsecs / 10000) | ||||||
|  | 	microsecs -= 10000 * uint32(p1) | ||||||
|  | 	p2 = byte(microsecs / 100) | ||||||
|  | 	microsecs -= 100 * uint32(p2) | ||||||
|  | 	p3 = byte(microsecs) | ||||||
|  | 	switch decimals := zOffs + length - 20; decimals { | ||||||
|  | 	default: | ||||||
|  | 		return append(dst, '.', | ||||||
|  | 			digits10[p1], digits01[p1], | ||||||
|  | 			digits10[p2], digits01[p2], | ||||||
|  | 			digits10[p3], digits01[p3], | ||||||
|  | 		), nil | ||||||
|  | 	case 1: | ||||||
|  | 		return append(dst, '.', | ||||||
|  | 			digits10[p1], | ||||||
|  | 		), nil | ||||||
|  | 	case 2: | ||||||
|  | 		return append(dst, '.', | ||||||
|  | 			digits10[p1], digits01[p1], | ||||||
|  | 		), nil | ||||||
|  | 	case 3: | ||||||
|  | 		return append(dst, '.', | ||||||
|  | 			digits10[p1], digits01[p1], | ||||||
|  | 			digits10[p2], | ||||||
|  | 		), nil | ||||||
|  | 	case 4: | ||||||
|  | 		return append(dst, '.', | ||||||
|  | 			digits10[p1], digits01[p1], | ||||||
|  | 			digits10[p2], digits01[p2], | ||||||
|  | 		), nil | ||||||
|  | 	case 5: | ||||||
|  | 		return append(dst, '.', | ||||||
|  | 			digits10[p1], digits01[p1], | ||||||
|  | 			digits10[p2], digits01[p2], | ||||||
|  | 			digits10[p3], | ||||||
|  | 		), nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****************************************************************************** | ||||||
|  | *                       Convert from and to bytes                             * | ||||||
|  | ******************************************************************************/ | ||||||
|  |  | ||||||
|  | func uint64ToBytes(n uint64) []byte { | ||||||
|  | 	return []byte{ | ||||||
|  | 		byte(n), | ||||||
|  | 		byte(n >> 8), | ||||||
|  | 		byte(n >> 16), | ||||||
|  | 		byte(n >> 24), | ||||||
|  | 		byte(n >> 32), | ||||||
|  | 		byte(n >> 40), | ||||||
|  | 		byte(n >> 48), | ||||||
|  | 		byte(n >> 56), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func uint64ToString(n uint64) []byte { | ||||||
|  | 	var a [20]byte | ||||||
|  | 	i := 20 | ||||||
|  |  | ||||||
|  | 	// U+0030 = 0 | ||||||
|  | 	// ... | ||||||
|  | 	// U+0039 = 9 | ||||||
|  |  | ||||||
|  | 	var q uint64 | ||||||
|  | 	for n >= 10 { | ||||||
|  | 		i-- | ||||||
|  | 		q = n / 10 | ||||||
|  | 		a[i] = uint8(n-q*10) + 0x30 | ||||||
|  | 		n = q | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	i-- | ||||||
|  | 	a[i] = uint8(n) + 0x30 | ||||||
|  |  | ||||||
|  | 	return a[i:] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // treats string value as unsigned integer representation | ||||||
|  | func stringToInt(b []byte) int { | ||||||
|  | 	val := 0 | ||||||
|  | 	for i := range b { | ||||||
|  | 		val *= 10 | ||||||
|  | 		val += int(b[i] - 0x30) | ||||||
|  | 	} | ||||||
|  | 	return val | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns the string read as a bytes slice, wheter the value is NULL, | ||||||
|  | // the number of bytes read and an error, in case the string is longer than | ||||||
|  | // the input slice | ||||||
|  | func readLengthEncodedString(b []byte) ([]byte, bool, int, error) { | ||||||
|  | 	// Get length | ||||||
|  | 	num, isNull, n := readLengthEncodedInteger(b) | ||||||
|  | 	if num < 1 { | ||||||
|  | 		return b[n:n], isNull, n, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n += int(num) | ||||||
|  |  | ||||||
|  | 	// Check data length | ||||||
|  | 	if len(b) >= n { | ||||||
|  | 		return b[n-int(num) : n], false, n, nil | ||||||
|  | 	} | ||||||
|  | 	return nil, false, n, io.EOF | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns the number of bytes skipped and an error, in case the string is | ||||||
|  | // longer than the input slice | ||||||
|  | func skipLengthEncodedString(b []byte) (int, error) { | ||||||
|  | 	// Get length | ||||||
|  | 	num, _, n := readLengthEncodedInteger(b) | ||||||
|  | 	if num < 1 { | ||||||
|  | 		return n, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n += int(num) | ||||||
|  |  | ||||||
|  | 	// Check data length | ||||||
|  | 	if len(b) >= n { | ||||||
|  | 		return n, nil | ||||||
|  | 	} | ||||||
|  | 	return n, io.EOF | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns the number read, whether the value is NULL and the number of bytes read | ||||||
|  | func readLengthEncodedInteger(b []byte) (uint64, bool, int) { | ||||||
|  | 	switch b[0] { | ||||||
|  |  | ||||||
|  | 	// 251: NULL | ||||||
|  | 	case 0xfb: | ||||||
|  | 		return 0, true, 1 | ||||||
|  |  | ||||||
|  | 	// 252: value of following 2 | ||||||
|  | 	case 0xfc: | ||||||
|  | 		return uint64(b[1]) | uint64(b[2])<<8, false, 3 | ||||||
|  |  | ||||||
|  | 	// 253: value of following 3 | ||||||
|  | 	case 0xfd: | ||||||
|  | 		return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16, false, 4 | ||||||
|  |  | ||||||
|  | 	// 254: value of following 8 | ||||||
|  | 	case 0xfe: | ||||||
|  | 		return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 | | ||||||
|  | 				uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 | | ||||||
|  | 				uint64(b[7])<<48 | uint64(b[8])<<56, | ||||||
|  | 			false, 9 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 0-250: value of first byte | ||||||
|  | 	return uint64(b[0]), false, 1 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // encodes a uint64 value and appends it to the given bytes slice | ||||||
|  | func appendLengthEncodedInteger(b []byte, n uint64) []byte { | ||||||
|  | 	switch { | ||||||
|  | 	case n <= 250: | ||||||
|  | 		return append(b, byte(n)) | ||||||
|  |  | ||||||
|  | 	case n <= 0xffff: | ||||||
|  | 		return append(b, 0xfc, byte(n), byte(n>>8)) | ||||||
|  |  | ||||||
|  | 	case n <= 0xffffff: | ||||||
|  | 		return append(b, 0xfd, byte(n), byte(n>>8), byte(n>>16)) | ||||||
|  | 	} | ||||||
|  | 	return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24), | ||||||
|  | 		byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize. | ||||||
|  | // If cap(buf) is not enough, reallocate new buffer. | ||||||
|  | func reserveBuffer(buf []byte, appendSize int) []byte { | ||||||
|  | 	newSize := len(buf) + appendSize | ||||||
|  | 	if cap(buf) < newSize { | ||||||
|  | 		// Grow buffer exponentially | ||||||
|  | 		newBuf := make([]byte, len(buf)*2+appendSize) | ||||||
|  | 		copy(newBuf, buf) | ||||||
|  | 		buf = newBuf | ||||||
|  | 	} | ||||||
|  | 	return buf[:newSize] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // escapeBytesBackslash escapes []byte with backslashes (\) | ||||||
|  | // This escapes the contents of a string (provided as []byte) by adding backslashes before special | ||||||
|  | // characters, and turning others into specific escape sequences, such as | ||||||
|  | // turning newlines into \n and null bytes into \0. | ||||||
|  | // https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932 | ||||||
|  | func escapeBytesBackslash(buf, v []byte) []byte { | ||||||
|  | 	pos := len(buf) | ||||||
|  | 	buf = reserveBuffer(buf, len(v)*2) | ||||||
|  |  | ||||||
|  | 	for _, c := range v { | ||||||
|  | 		switch c { | ||||||
|  | 		case '\x00': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '0' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\n': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = 'n' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\r': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = 'r' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\x1a': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = 'Z' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\'': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '\'' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '"': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '"' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\\': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '\\' | ||||||
|  | 			pos += 2 | ||||||
|  | 		default: | ||||||
|  | 			buf[pos] = c | ||||||
|  | 			pos += 1 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf[:pos] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // escapeStringBackslash is similar to escapeBytesBackslash but for string. | ||||||
|  | func escapeStringBackslash(buf []byte, v string) []byte { | ||||||
|  | 	pos := len(buf) | ||||||
|  | 	buf = reserveBuffer(buf, len(v)*2) | ||||||
|  |  | ||||||
|  | 	for i := 0; i < len(v); i++ { | ||||||
|  | 		c := v[i] | ||||||
|  | 		switch c { | ||||||
|  | 		case '\x00': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '0' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\n': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = 'n' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\r': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = 'r' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\x1a': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = 'Z' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\'': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '\'' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '"': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '"' | ||||||
|  | 			pos += 2 | ||||||
|  | 		case '\\': | ||||||
|  | 			buf[pos] = '\\' | ||||||
|  | 			buf[pos+1] = '\\' | ||||||
|  | 			pos += 2 | ||||||
|  | 		default: | ||||||
|  | 			buf[pos] = c | ||||||
|  | 			pos += 1 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf[:pos] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // escapeBytesQuotes escapes apostrophes in []byte by doubling them up. | ||||||
|  | // This escapes the contents of a string by doubling up any apostrophes that | ||||||
|  | // it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in | ||||||
|  | // effect on the server. | ||||||
|  | // https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038 | ||||||
|  | func escapeBytesQuotes(buf, v []byte) []byte { | ||||||
|  | 	pos := len(buf) | ||||||
|  | 	buf = reserveBuffer(buf, len(v)*2) | ||||||
|  |  | ||||||
|  | 	for _, c := range v { | ||||||
|  | 		if c == '\'' { | ||||||
|  | 			buf[pos] = '\'' | ||||||
|  | 			buf[pos+1] = '\'' | ||||||
|  | 			pos += 2 | ||||||
|  | 		} else { | ||||||
|  | 			buf[pos] = c | ||||||
|  | 			pos++ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf[:pos] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // escapeStringQuotes is similar to escapeBytesQuotes but for string. | ||||||
|  | func escapeStringQuotes(buf []byte, v string) []byte { | ||||||
|  | 	pos := len(buf) | ||||||
|  | 	buf = reserveBuffer(buf, len(v)*2) | ||||||
|  |  | ||||||
|  | 	for i := 0; i < len(v); i++ { | ||||||
|  | 		c := v[i] | ||||||
|  | 		if c == '\'' { | ||||||
|  | 			buf[pos] = '\'' | ||||||
|  | 			buf[pos+1] = '\'' | ||||||
|  | 			pos += 2 | ||||||
|  | 		} else { | ||||||
|  | 			buf[pos] = c | ||||||
|  | 			pos++ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf[:pos] | ||||||
|  | } | ||||||
							
								
								
									
										346
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,346 @@ | |||||||
|  | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package | ||||||
|  | // | ||||||
|  | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. | ||||||
|  | // | ||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||||||
|  | // You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | package mysql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"crypto/tls" | ||||||
|  | 	"encoding/binary" | ||||||
|  | 	"fmt" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var testDSNs = []struct { | ||||||
|  | 	in  string | ||||||
|  | 	out string | ||||||
|  | 	loc *time.Location | ||||||
|  | }{ | ||||||
|  | 	{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:true interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.Local}, | ||||||
|  | 	{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | 	{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDSNParser(t *testing.T) { | ||||||
|  | 	var cfg *config | ||||||
|  | 	var err error | ||||||
|  | 	var res string | ||||||
|  |  | ||||||
|  | 	for i, tst := range testDSNs { | ||||||
|  | 		cfg, err = parseDSN(tst.in) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Error(err.Error()) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// pointer not static | ||||||
|  | 		cfg.tls = nil | ||||||
|  |  | ||||||
|  | 		res = fmt.Sprintf("%+v", cfg) | ||||||
|  | 		if res != fmt.Sprintf(tst.out, tst.loc) { | ||||||
|  | 			t.Errorf("%d. parseDSN(%q) => %q, want %q", i, tst.in, res, fmt.Sprintf(tst.out, tst.loc)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDSNParserInvalid(t *testing.T) { | ||||||
|  | 	var invalidDSNs = []string{ | ||||||
|  | 		"@net(addr/",                  // no closing brace | ||||||
|  | 		"@tcp(/",                      // no closing brace | ||||||
|  | 		"tcp(/",                       // no closing brace | ||||||
|  | 		"(/",                          // no closing brace | ||||||
|  | 		"net(addr)//",                 // unescaped | ||||||
|  | 		"user:pass@tcp(1.2.3.4:3306)", // no trailing slash | ||||||
|  | 		//"/dbname?arg=/some/unescaped/path", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i, tst := range invalidDSNs { | ||||||
|  | 		if _, err := parseDSN(tst); err == nil { | ||||||
|  | 			t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDSNWithCustomTLS(t *testing.T) { | ||||||
|  | 	baseDSN := "user:password@tcp(localhost:5555)/dbname?tls=" | ||||||
|  | 	tlsCfg := tls.Config{} | ||||||
|  |  | ||||||
|  | 	RegisterTLSConfig("utils_test", &tlsCfg) | ||||||
|  |  | ||||||
|  | 	// Custom TLS is missing | ||||||
|  | 	tst := baseDSN + "invalid_tls" | ||||||
|  | 	cfg, err := parseDSN(tst) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Invalid custom TLS in DSN (%s) but did not error.  Got config: %#v", tst, cfg) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tst = baseDSN + "utils_test" | ||||||
|  |  | ||||||
|  | 	// Custom TLS with a server name | ||||||
|  | 	name := "foohost" | ||||||
|  | 	tlsCfg.ServerName = name | ||||||
|  | 	cfg, err = parseDSN(tst) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error(err.Error()) | ||||||
|  | 	} else if cfg.tls.ServerName != name { | ||||||
|  | 		t.Errorf("Did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Custom TLS without a server name | ||||||
|  | 	name = "localhost" | ||||||
|  | 	tlsCfg.ServerName = "" | ||||||
|  | 	cfg, err = parseDSN(tst) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error(err.Error()) | ||||||
|  | 	} else if cfg.tls.ServerName != name { | ||||||
|  | 		t.Errorf("Did not get the correct ServerName (%s) parsing DSN (%s).", name, tst) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	DeregisterTLSConfig("utils_test") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDSNUnsafeCollation(t *testing.T) { | ||||||
|  | 	_, err := parseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true") | ||||||
|  | 	if err != errInvalidDSNUnsafeCollation { | ||||||
|  | 		t.Error("Expected %v, Got %v", errInvalidDSNUnsafeCollation, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = parseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error("Expected %v, Got %v", nil, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = parseDSN("/dbname?collation=gbk_chinese_ci") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error("Expected %v, Got %v", nil, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = parseDSN("/dbname?collation=ascii_bin&interpolateParams=true") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error("Expected %v, Got %v", nil, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = parseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error("Expected %v, Got %v", nil, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = parseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error("Expected %v, Got %v", nil, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = parseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error("Expected %v, Got %v", nil, err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkParseDSN(b *testing.B) { | ||||||
|  | 	b.ReportAllocs() | ||||||
|  |  | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		for _, tst := range testDSNs { | ||||||
|  | 			if _, err := parseDSN(tst.in); err != nil { | ||||||
|  | 				b.Error(err.Error()) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestScanNullTime(t *testing.T) { | ||||||
|  | 	var scanTests = []struct { | ||||||
|  | 		in    interface{} | ||||||
|  | 		error bool | ||||||
|  | 		valid bool | ||||||
|  | 		time  time.Time | ||||||
|  | 	}{ | ||||||
|  | 		{tDate, false, true, tDate}, | ||||||
|  | 		{sDate, false, true, tDate}, | ||||||
|  | 		{[]byte(sDate), false, true, tDate}, | ||||||
|  | 		{tDateTime, false, true, tDateTime}, | ||||||
|  | 		{sDateTime, false, true, tDateTime}, | ||||||
|  | 		{[]byte(sDateTime), false, true, tDateTime}, | ||||||
|  | 		{tDate0, false, true, tDate0}, | ||||||
|  | 		{sDate0, false, true, tDate0}, | ||||||
|  | 		{[]byte(sDate0), false, true, tDate0}, | ||||||
|  | 		{sDateTime0, false, true, tDate0}, | ||||||
|  | 		{[]byte(sDateTime0), false, true, tDate0}, | ||||||
|  | 		{"", true, false, tDate0}, | ||||||
|  | 		{"1234", true, false, tDate0}, | ||||||
|  | 		{0, true, false, tDate0}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var nt = NullTime{} | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	for _, tst := range scanTests { | ||||||
|  | 		err = nt.Scan(tst.in) | ||||||
|  | 		if (err != nil) != tst.error { | ||||||
|  | 			t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil)) | ||||||
|  | 		} | ||||||
|  | 		if nt.Valid != tst.valid { | ||||||
|  | 			t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid) | ||||||
|  | 		} | ||||||
|  | 		if nt.Time != tst.time { | ||||||
|  | 			t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestLengthEncodedInteger(t *testing.T) { | ||||||
|  | 	var integerTests = []struct { | ||||||
|  | 		num     uint64 | ||||||
|  | 		encoded []byte | ||||||
|  | 	}{ | ||||||
|  | 		{0x0000000000000000, []byte{0x00}}, | ||||||
|  | 		{0x0000000000000012, []byte{0x12}}, | ||||||
|  | 		{0x00000000000000fa, []byte{0xfa}}, | ||||||
|  | 		{0x0000000000000100, []byte{0xfc, 0x00, 0x01}}, | ||||||
|  | 		{0x0000000000001234, []byte{0xfc, 0x34, 0x12}}, | ||||||
|  | 		{0x000000000000ffff, []byte{0xfc, 0xff, 0xff}}, | ||||||
|  | 		{0x0000000000010000, []byte{0xfd, 0x00, 0x00, 0x01}}, | ||||||
|  | 		{0x0000000000123456, []byte{0xfd, 0x56, 0x34, 0x12}}, | ||||||
|  | 		{0x0000000000ffffff, []byte{0xfd, 0xff, 0xff, 0xff}}, | ||||||
|  | 		{0x0000000001000000, []byte{0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}}, | ||||||
|  | 		{0x123456789abcdef0, []byte{0xfe, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12}}, | ||||||
|  | 		{0xffffffffffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tst := range integerTests { | ||||||
|  | 		num, isNull, numLen := readLengthEncodedInteger(tst.encoded) | ||||||
|  | 		if isNull { | ||||||
|  | 			t.Errorf("%x: expected %d, got NULL", tst.encoded, tst.num) | ||||||
|  | 		} | ||||||
|  | 		if num != tst.num { | ||||||
|  | 			t.Errorf("%x: expected %d, got %d", tst.encoded, tst.num, num) | ||||||
|  | 		} | ||||||
|  | 		if numLen != len(tst.encoded) { | ||||||
|  | 			t.Errorf("%x: expected size %d, got %d", tst.encoded, len(tst.encoded), numLen) | ||||||
|  | 		} | ||||||
|  | 		encoded := appendLengthEncodedInteger(nil, num) | ||||||
|  | 		if !bytes.Equal(encoded, tst.encoded) { | ||||||
|  | 			t.Errorf("%v: expected %x, got %x", num, tst.encoded, encoded) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOldPass(t *testing.T) { | ||||||
|  | 	scramble := []byte{9, 8, 7, 6, 5, 4, 3, 2} | ||||||
|  | 	vectors := []struct { | ||||||
|  | 		pass string | ||||||
|  | 		out  string | ||||||
|  | 	}{ | ||||||
|  | 		{" pass", "47575c5a435b4251"}, | ||||||
|  | 		{"pass ", "47575c5a435b4251"}, | ||||||
|  | 		{"123\t456", "575c47505b5b5559"}, | ||||||
|  | 		{"C0mpl!ca ted#PASS123", "5d5d554849584a45"}, | ||||||
|  | 	} | ||||||
|  | 	for _, tuple := range vectors { | ||||||
|  | 		ours := scrambleOldPassword(scramble, []byte(tuple.pass)) | ||||||
|  | 		if tuple.out != fmt.Sprintf("%x", ours) { | ||||||
|  | 			t.Errorf("Failed old password %q", tuple.pass) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestFormatBinaryDateTime(t *testing.T) { | ||||||
|  | 	rawDate := [11]byte{} | ||||||
|  | 	binary.LittleEndian.PutUint16(rawDate[:2], 1978)   // years | ||||||
|  | 	rawDate[2] = 12                                    // months | ||||||
|  | 	rawDate[3] = 30                                    // days | ||||||
|  | 	rawDate[4] = 15                                    // hours | ||||||
|  | 	rawDate[5] = 46                                    // minutes | ||||||
|  | 	rawDate[6] = 23                                    // seconds | ||||||
|  | 	binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds | ||||||
|  | 	expect := func(expected string, inlen, outlen uint8) { | ||||||
|  | 		actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen, false) | ||||||
|  | 		bytes, ok := actual.([]byte) | ||||||
|  | 		if !ok { | ||||||
|  | 			t.Errorf("formatBinaryDateTime must return []byte, was %T", actual) | ||||||
|  | 		} | ||||||
|  | 		if string(bytes) != expected { | ||||||
|  | 			t.Errorf( | ||||||
|  | 				"expected %q, got %q for length in %d, out %d", | ||||||
|  | 				bytes, actual, inlen, outlen, | ||||||
|  | 			) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	expect("0000-00-00", 0, 10) | ||||||
|  | 	expect("0000-00-00 00:00:00", 0, 19) | ||||||
|  | 	expect("1978-12-30", 4, 10) | ||||||
|  | 	expect("1978-12-30 15:46:23", 7, 19) | ||||||
|  | 	expect("1978-12-30 15:46:23.987654", 11, 26) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestEscapeBackslash(t *testing.T) { | ||||||
|  | 	expect := func(expected, value string) { | ||||||
|  | 		actual := string(escapeBytesBackslash([]byte{}, []byte(value))) | ||||||
|  | 		if actual != expected { | ||||||
|  | 			t.Errorf( | ||||||
|  | 				"expected %s, got %s", | ||||||
|  | 				expected, actual, | ||||||
|  | 			) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		actual = string(escapeStringBackslash([]byte{}, value)) | ||||||
|  | 		if actual != expected { | ||||||
|  | 			t.Errorf( | ||||||
|  | 				"expected %s, got %s", | ||||||
|  | 				expected, actual, | ||||||
|  | 			) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	expect("foo\\0bar", "foo\x00bar") | ||||||
|  | 	expect("foo\\nbar", "foo\nbar") | ||||||
|  | 	expect("foo\\rbar", "foo\rbar") | ||||||
|  | 	expect("foo\\Zbar", "foo\x1abar") | ||||||
|  | 	expect("foo\\\"bar", "foo\"bar") | ||||||
|  | 	expect("foo\\\\bar", "foo\\bar") | ||||||
|  | 	expect("foo\\'bar", "foo'bar") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestEscapeQuotes(t *testing.T) { | ||||||
|  | 	expect := func(expected, value string) { | ||||||
|  | 		actual := string(escapeBytesQuotes([]byte{}, []byte(value))) | ||||||
|  | 		if actual != expected { | ||||||
|  | 			t.Errorf( | ||||||
|  | 				"expected %s, got %s", | ||||||
|  | 				expected, actual, | ||||||
|  | 			) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		actual = string(escapeStringQuotes([]byte{}, value)) | ||||||
|  | 		if actual != expected { | ||||||
|  | 			t.Errorf( | ||||||
|  | 				"expected %s, got %s", | ||||||
|  | 				expected, actual, | ||||||
|  | 			) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	expect("foo\x00bar", "foo\x00bar") // not affected | ||||||
|  | 	expect("foo\nbar", "foo\nbar")     // not affected | ||||||
|  | 	expect("foo\rbar", "foo\rbar")     // not affected | ||||||
|  | 	expect("foo\x1abar", "foo\x1abar") // not affected | ||||||
|  | 	expect("foo''bar", "foo'bar")      // affected | ||||||
|  | 	expect("foo\"bar", "foo\"bar")     // not affected | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | // ActivityService handles communication with the activity related | ||||||
|  | // methods of the GitHub API. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/ | ||||||
|  | type ActivityService struct { | ||||||
|  | 	client *Client | ||||||
|  | } | ||||||
							
								
								
									
										305
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,305 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Event represents a GitHub event. | ||||||
|  | type Event struct { | ||||||
|  | 	Type       *string          `json:"type,omitempty"` | ||||||
|  | 	Public     *bool            `json:"public"` | ||||||
|  | 	RawPayload *json.RawMessage `json:"payload,omitempty"` | ||||||
|  | 	Repo       *Repository      `json:"repo,omitempty"` | ||||||
|  | 	Actor      *User            `json:"actor,omitempty"` | ||||||
|  | 	Org        *Organization    `json:"org,omitempty"` | ||||||
|  | 	CreatedAt  *time.Time       `json:"created_at,omitempty"` | ||||||
|  | 	ID         *string          `json:"id,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e Event) String() string { | ||||||
|  | 	return Stringify(e) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Payload returns the parsed event payload. For recognized event types | ||||||
|  | // (PushEvent), a value of the corresponding struct type will be returned. | ||||||
|  | func (e *Event) Payload() (payload interface{}) { | ||||||
|  | 	switch *e.Type { | ||||||
|  | 	case "PushEvent": | ||||||
|  | 		payload = &PushEvent{} | ||||||
|  | 	} | ||||||
|  | 	if err := json.Unmarshal(*e.RawPayload, &payload); err != nil { | ||||||
|  | 		panic(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return payload | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PushEvent represents a git push to a GitHub repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/types/#pushevent | ||||||
|  | type PushEvent struct { | ||||||
|  | 	PushID  *int              `json:"push_id,omitempty"` | ||||||
|  | 	Head    *string           `json:"head,omitempty"` | ||||||
|  | 	Ref     *string           `json:"ref,omitempty"` | ||||||
|  | 	Size    *int              `json:"size,omitempty"` | ||||||
|  | 	Commits []PushEventCommit `json:"commits,omitempty"` | ||||||
|  | 	Repo    *Repository       `json:"repository,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p PushEvent) String() string { | ||||||
|  | 	return Stringify(p) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PushEventCommit represents a git commit in a GitHub PushEvent. | ||||||
|  | type PushEventCommit struct { | ||||||
|  | 	SHA      *string       `json:"sha,omitempty"` | ||||||
|  | 	Message  *string       `json:"message,omitempty"` | ||||||
|  | 	Author   *CommitAuthor `json:"author,omitempty"` | ||||||
|  | 	URL      *string       `json:"url,omitempty"` | ||||||
|  | 	Distinct *bool         `json:"distinct,omitempty"` | ||||||
|  | 	Added    []string      `json:"added,omitempty"` | ||||||
|  | 	Removed  []string      `json:"removed,omitempty"` | ||||||
|  | 	Modified []string      `json:"modified,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p PushEventCommit) String() string { | ||||||
|  | 	return Stringify(p) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //PullRequestEvent represents the payload delivered by PullRequestEvent webhook | ||||||
|  | type PullRequestEvent struct { | ||||||
|  | 	Action      *string      `json:"action,omitempty"` | ||||||
|  | 	Number      *int         `json:"number,omitempty"` | ||||||
|  | 	PullRequest *PullRequest `json:"pull_request,omitempty"` | ||||||
|  | 	Repo        *Repository  `json:"repository,omitempty"` | ||||||
|  | 	Sender      *User        `json:"sender,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IssueActivityEvent represents the payload delivered by Issue webhook | ||||||
|  | type IssueActivityEvent struct { | ||||||
|  | 	Action *string     `json:"action,omitempty"` | ||||||
|  | 	Issue  *Issue      `json:"issue,omitempty"` | ||||||
|  | 	Repo   *Repository `json:"repository,omitempty"` | ||||||
|  | 	Sender *User       `json:"sender,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IssueCommentEvent represents the payload delivered by IssueComment webhook | ||||||
|  | // | ||||||
|  | // This webhook also gets fired for comments on pull requests | ||||||
|  | type IssueCommentEvent struct { | ||||||
|  | 	Action  *string       `json:"action,omitempty"` | ||||||
|  | 	Issue   *Issue        `json:"issue,omitempty"` | ||||||
|  | 	Comment *IssueComment `json:"comment,omitempty"` | ||||||
|  | 	Repo    *Repository   `json:"repository,omitempty"` | ||||||
|  | 	Sender  *User         `json:"sender,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListEvents drinks from the firehose of all public events across GitHub. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events | ||||||
|  | func (s *ActivityService) ListEvents(opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	u, err := addOptions("events", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListRepositoryEvents lists events for a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-repository-events | ||||||
|  | func (s *ActivityService) ListRepositoryEvents(owner, repo string, opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/events", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListIssueEventsForRepository lists issue events for a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository | ||||||
|  | func (s *ActivityService) ListIssueEventsForRepository(owner, repo string, opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListEventsForRepoNetwork lists public events for a network of repositories. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories | ||||||
|  | func (s *ActivityService) ListEventsForRepoNetwork(owner, repo string, opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("networks/%v/%v/events", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListEventsForOrganization lists public events for an organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events-for-an-organization | ||||||
|  | func (s *ActivityService) ListEventsForOrganization(org string, opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/events", org) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListEventsPerformedByUser lists the events performed by a user. If publicOnly is | ||||||
|  | // true, only public events will be returned. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-performed-by-a-user | ||||||
|  | func (s *ActivityService) ListEventsPerformedByUser(user string, publicOnly bool, opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if publicOnly { | ||||||
|  | 		u = fmt.Sprintf("users/%v/events/public", user) | ||||||
|  | 	} else { | ||||||
|  | 		u = fmt.Sprintf("users/%v/events", user) | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListEventsRecievedByUser lists the events recieved by a user. If publicOnly is | ||||||
|  | // true, only public events will be returned. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received | ||||||
|  | func (s *ActivityService) ListEventsRecievedByUser(user string, publicOnly bool, opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if publicOnly { | ||||||
|  | 		u = fmt.Sprintf("users/%v/received_events/public", user) | ||||||
|  | 	} else { | ||||||
|  | 		u = fmt.Sprintf("users/%v/received_events", user) | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListUserEventsForOrganization provides the user’s organization dashboard. You | ||||||
|  | // must be authenticated as the user to view this. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-for-an-organization | ||||||
|  | func (s *ActivityService) ListUserEventsForOrganization(org, user string, opt *ListOptions) ([]Event, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("users/%v/events/orgs/%v", user, org) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events := new([]Event) | ||||||
|  | 	resp, err := s.client.Do(req, events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *events, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										305
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_events_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_events_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,305 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEvents(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListEvents(opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activities.ListEvents returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Activities.ListEvents returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListRepositoryEvents(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListRepositoryEvents("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activities.ListRepositoryEvents returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Activities.ListRepositoryEvents returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListRepositoryEvents_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.ListRepositoryEvents("%", "%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListIssueEventsForRepository(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListIssueEventsForRepository("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activities.ListIssueEventsForRepository returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Activities.ListIssueEventsForRepository returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListIssueEventsForRepository_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.ListIssueEventsForRepository("%", "%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsForRepoNetwork(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/networks/o/r/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListEventsForRepoNetwork("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activities.ListEventsForRepoNetwork returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Activities.ListEventsForRepoNetwork returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsForRepoNetwork_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.ListEventsForRepoNetwork("%", "%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsForOrganization(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListEventsForOrganization("o", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activities.ListEventsForOrganization returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Activities.ListEventsForOrganization returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsForOrganization_invalidOrg(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.ListEventsForOrganization("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListEventsPerformedByUser("u", false, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Events.ListPerformedByUser returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsPerformedByUser_publicOnly(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/events/public", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	events, _, err := client.Activity.ListEventsPerformedByUser("u", true, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Events.ListPerformedByUser returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsPerformedByUser_invalidUser(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.ListEventsPerformedByUser("%", false, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsRecievedByUser_all(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/received_events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListEventsRecievedByUser("u", false, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Events.ListRecievedByUser returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Events.ListRecievedUser returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsRecievedByUser_publicOnly(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/received_events/public", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	events, _, err := client.Activity.ListEventsRecievedByUser("u", true, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Events.ListRecievedByUser returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Events.ListRecievedByUser returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListEventsRecievedByUser_invalidUser(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.ListEventsRecievedByUser("%", false, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListUserEventsForOrganization(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/events/orgs/o", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	events, _, err := client.Activity.ListUserEventsForOrganization("o", "u", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activities.ListUserEventsForOrganization returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Event{{ID: String("1")}, {ID: String("2")}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Activities.ListUserEventsForOrganization returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivity_EventPayload_typed(t *testing.T) { | ||||||
|  | 	raw := []byte(`{"type": "PushEvent","payload":{"push_id": 1}}`) | ||||||
|  | 	var event *Event | ||||||
|  | 	if err := json.Unmarshal(raw, &event); err != nil { | ||||||
|  | 		t.Fatalf("Unmarshal Event returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &PushEvent{PushID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(event.Payload(), want) { | ||||||
|  | 		t.Errorf("Event Payload returned %+v, want %+v", event.Payload(), want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestEvent_Payload_untyped checks that unrecognized events are parsed to an | ||||||
|  | // interface{} value (instead of being discarded or throwing an error), for | ||||||
|  | // forward compatibility with new event types. | ||||||
|  | func TestActivity_EventPayload_untyped(t *testing.T) { | ||||||
|  | 	raw := []byte(`{"type": "UnrecognizedEvent","payload":{"field": "val"}}`) | ||||||
|  | 	var event *Event | ||||||
|  | 	if err := json.Unmarshal(raw, &event); err != nil { | ||||||
|  | 		t.Fatalf("Unmarshal Event returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := map[string]interface{}{"field": "val"} | ||||||
|  | 	if !reflect.DeepEqual(event.Payload(), want) { | ||||||
|  | 		t.Errorf("Event Payload returned %+v, want %+v", event.Payload(), want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										224
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_notifications.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_notifications.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Notification identifies a GitHub notification for a user. | ||||||
|  | type Notification struct { | ||||||
|  | 	ID         *string              `json:"id,omitempty"` | ||||||
|  | 	Repository *Repository          `json:"repository,omitempty"` | ||||||
|  | 	Subject    *NotificationSubject `json:"subject,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Reason identifies the event that triggered the notification. | ||||||
|  | 	// | ||||||
|  | 	// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#notification-reasons | ||||||
|  | 	Reason *string `json:"reason,omitempty"` | ||||||
|  |  | ||||||
|  | 	Unread     *bool      `json:"unread,omitempty"` | ||||||
|  | 	UpdatedAt  *time.Time `json:"updated_at,omitempty"` | ||||||
|  | 	LastReadAt *time.Time `json:"last_read_at,omitempty"` | ||||||
|  | 	URL        *string    `json:"url,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotificationSubject identifies the subject of a notification. | ||||||
|  | type NotificationSubject struct { | ||||||
|  | 	Title            *string `json:"title,omitempty"` | ||||||
|  | 	URL              *string `json:"url,omitempty"` | ||||||
|  | 	LatestCommentURL *string `json:"latest_comment_url,omitempty"` | ||||||
|  | 	Type             *string `json:"type,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotificationListOptions specifies the optional parameters to the | ||||||
|  | // ActivityService.ListNotifications method. | ||||||
|  | type NotificationListOptions struct { | ||||||
|  | 	All           bool      `url:"all,omitempty"` | ||||||
|  | 	Participating bool      `url:"participating,omitempty"` | ||||||
|  | 	Since         time.Time `url:"since,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListNotifications lists all notifications for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications | ||||||
|  | func (s *ActivityService) ListNotifications(opt *NotificationListOptions) ([]Notification, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("notifications") | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var notifications []Notification | ||||||
|  | 	resp, err := s.client.Do(req, ¬ifications) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return notifications, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListRepositoryNotifications lists all notifications in a given repository | ||||||
|  | // for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository | ||||||
|  | func (s *ActivityService) ListRepositoryNotifications(owner, repo string, opt *NotificationListOptions) ([]Notification, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var notifications []Notification | ||||||
|  | 	resp, err := s.client.Do(req, ¬ifications) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return notifications, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type markReadOptions struct { | ||||||
|  | 	LastReadAt time.Time `url:"last_read_at,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MarkNotificationsRead marks all notifications up to lastRead as read. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#mark-as-read | ||||||
|  | func (s *ActivityService) MarkNotificationsRead(lastRead time.Time) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("notifications") | ||||||
|  | 	u, err := addOptions(u, markReadOptions{lastRead}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MarkRepositoryNotificationsRead marks all notifications up to lastRead in | ||||||
|  | // the specified repository as read. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository | ||||||
|  | func (s *ActivityService) MarkRepositoryNotificationsRead(owner, repo string, lastRead time.Time) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) | ||||||
|  | 	u, err := addOptions(u, markReadOptions{lastRead}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetThread gets the specified notification thread. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread | ||||||
|  | func (s *ActivityService) GetThread(id string) (*Notification, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("notifications/threads/%v", id) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	notification := new(Notification) | ||||||
|  | 	resp, err := s.client.Do(req, notification) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return notification, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MarkThreadRead marks the specified thread as read. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read | ||||||
|  | func (s *ActivityService) MarkThreadRead(id string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("notifications/threads/%v", id) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetThreadSubscription checks to see if the authenticated user is subscribed | ||||||
|  | // to a thread. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription | ||||||
|  | func (s *ActivityService) GetThreadSubscription(id string) (*Subscription, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("notifications/threads/%v/subscription", id) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sub := new(Subscription) | ||||||
|  | 	resp, err := s.client.Do(req, sub) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return sub, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetThreadSubscription sets the subscription for the specified thread for the | ||||||
|  | // authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription | ||||||
|  | func (s *ActivityService) SetThreadSubscription(id string, subscription *Subscription) (*Subscription, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("notifications/threads/%v/subscription", id) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, subscription) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sub := new(Subscription) | ||||||
|  | 	resp, err := s.client.Do(req, sub) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return sub, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteThreadSubscription deletes the subscription for the specified thread | ||||||
|  | // for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription | ||||||
|  | func (s *ActivityService) DeleteThreadSubscription(id string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("notifications/threads/%v/subscription", id) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
							
								
								
									
										203
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_notifications_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_notifications_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,203 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestActivityService_ListNotification(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"all":           "true", | ||||||
|  | 			"participating": "true", | ||||||
|  | 			"since":         "2006-01-02T15:04:05Z", | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1", "subject":{"title":"t"}}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &NotificationListOptions{ | ||||||
|  | 		All:           true, | ||||||
|  | 		Participating: true, | ||||||
|  | 		Since:         time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC), | ||||||
|  | 	} | ||||||
|  | 	notifications, _, err := client.Activity.ListNotifications(opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListNotifications returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Notification{{ID: String("1"), Subject: &NotificationSubject{Title: String("t")}}} | ||||||
|  | 	if !reflect.DeepEqual(notifications, want) { | ||||||
|  | 		t.Errorf("Activity.ListNotifications returned %+v, want %+v", notifications, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListRepositoryNotification(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":"1"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	notifications, _, err := client.Activity.ListRepositoryNotifications("o", "r", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListRepositoryNotifications returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Notification{{ID: String("1")}} | ||||||
|  | 	if !reflect.DeepEqual(notifications, want) { | ||||||
|  | 		t.Errorf("Activity.ListRepositoryNotifications returned %+v, want %+v", notifications, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_MarkNotificationsRead(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "PUT") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"last_read_at": "2006-01-02T15:04:05Z", | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		w.WriteHeader(http.StatusResetContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Activity.MarkNotificationsRead(time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.MarkNotificationsRead returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "PUT") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"last_read_at": "2006-01-02T15:04:05Z", | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		w.WriteHeader(http.StatusResetContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Activity.MarkRepositoryNotificationsRead("o", "r", time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_GetThread(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"id":"1"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	notification, _, err := client.Activity.GetThread("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.GetThread returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Notification{ID: String("1")} | ||||||
|  | 	if !reflect.DeepEqual(notification, want) { | ||||||
|  | 		t.Errorf("Activity.GetThread returned %+v, want %+v", notification, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_MarkThreadRead(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		w.WriteHeader(http.StatusResetContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Activity.MarkThreadRead("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.MarkThreadRead returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_GetThreadSubscription(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"subscribed":true}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	sub, _, err := client.Activity.GetThreadSubscription("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.GetThreadSubscription returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Subscription{Subscribed: Bool(true)} | ||||||
|  | 	if !reflect.DeepEqual(sub, want) { | ||||||
|  | 		t.Errorf("Activity.GetThreadSubscription returned %+v, want %+v", sub, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_SetThreadSubscription(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Subscription{Subscribed: Bool(true)} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Subscription) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PUT") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"ignored":true}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	sub, _, err := client.Activity.SetThreadSubscription("1", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.SetThreadSubscription returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Subscription{Ignored: Bool(true)} | ||||||
|  | 	if !reflect.DeepEqual(sub, want) { | ||||||
|  | 		t.Errorf("Activity.SetThreadSubscription returned %+v, want %+v", sub, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_DeleteThreadSubscription(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 		w.WriteHeader(http.StatusNoContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Activity.DeleteThreadSubscription("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.DeleteThreadSubscription returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										114
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_star.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_star.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // ListStargazers lists people who have starred the specified repo. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/starring/#list-stargazers | ||||||
|  | func (s *ActivityService) ListStargazers(owner, repo string, opt *ListOptions) ([]User, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	stargazers := new([]User) | ||||||
|  | 	resp, err := s.client.Do(req, stargazers) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *stargazers, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ActivityListStarredOptions specifies the optional parameters to the | ||||||
|  | // ActivityService.ListStarred method. | ||||||
|  | type ActivityListStarredOptions struct { | ||||||
|  | 	// How to sort the repository list.  Possible values are: created, updated, | ||||||
|  | 	// pushed, full_name.  Default is "full_name". | ||||||
|  | 	Sort string `url:"sort,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Direction in which to sort repositories.  Possible values are: asc, desc. | ||||||
|  | 	// Default is "asc" when sort is "full_name", otherwise default is "desc". | ||||||
|  | 	Direction string `url:"direction,omitempty"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListStarred lists all the repos starred by a user.  Passing the empty string | ||||||
|  | // will list the starred repositories for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/activity/starring/#list-repositories-being-starred | ||||||
|  | func (s *ActivityService) ListStarred(user string, opt *ActivityListStarredOptions) ([]Repository, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if user != "" { | ||||||
|  | 		u = fmt.Sprintf("users/%v/starred", user) | ||||||
|  | 	} else { | ||||||
|  | 		u = "user/starred" | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	repos := new([]Repository) | ||||||
|  | 	resp, err := s.client.Do(req, repos) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *repos, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsStarred checks if a repository is starred by authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository | ||||||
|  | func (s *ActivityService) IsStarred(owner, repo string) (bool, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("user/starred/%v/%v", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, nil, err | ||||||
|  | 	} | ||||||
|  | 	resp, err := s.client.Do(req, nil) | ||||||
|  | 	starred, err := parseBoolResponse(err) | ||||||
|  | 	return starred, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Star a repository as the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository | ||||||
|  | func (s *ActivityService) Star(owner, repo string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("user/starred/%v/%v", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unstar a repository as the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository | ||||||
|  | func (s *ActivityService) Unstar(owner, repo string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("user/starred/%v/%v", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
							
								
								
									
										167
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_star_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_star_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestActivityService_ListStargazers(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	stargazers, _, err := client.Activity.ListStargazers("o", "r", &ListOptions{Page: 2}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListStargazers returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []User{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(stargazers, want) { | ||||||
|  | 		t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListStarred_authenticatedUser(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	repos, _, err := client.Activity.ListStarred("", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListStarred returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Repository{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(repos, want) { | ||||||
|  | 		t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListStarred_specifiedUser(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"sort":      "created", | ||||||
|  | 			"direction": "asc", | ||||||
|  | 			"page":      "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":2}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ActivityListStarredOptions{"created", "asc", ListOptions{Page: 2}} | ||||||
|  | 	repos, _, err := client.Activity.ListStarred("u", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListStarred returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Repository{{ID: Int(2)}} | ||||||
|  | 	if !reflect.DeepEqual(repos, want) { | ||||||
|  | 		t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListStarred_invalidUser(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.ListStarred("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_IsStarred_hasStar(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNoContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	star, _, err := client.Activity.IsStarred("o", "r") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.IsStarred returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := true; star != want { | ||||||
|  | 		t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_IsStarred_noStar(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	star, _, err := client.Activity.IsStarred("o", "r") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.IsStarred returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := false; star != want { | ||||||
|  | 		t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_IsStarred_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Activity.IsStarred("%", "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_Star(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "PUT") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Activity.Star("o", "r") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.Star returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_Star_invalidID(t *testing.T) { | ||||||
|  | 	_, err := client.Activity.Star("%", "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_Unstar(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Activity.Unstar("o", "r") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.Unstar returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_Unstar_invalidID(t *testing.T) { | ||||||
|  | 	_, err := client.Activity.Unstar("%", "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										131
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_watching.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_watching.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // Subscription identifies a repository or thread subscription. | ||||||
|  | type Subscription struct { | ||||||
|  | 	Subscribed *bool      `json:"subscribed,omitempty"` | ||||||
|  | 	Ignored    *bool      `json:"ignored,omitempty"` | ||||||
|  | 	Reason     *string    `json:"reason,omitempty"` | ||||||
|  | 	CreatedAt  *Timestamp `json:"created_at,omitempty"` | ||||||
|  | 	URL        *string    `json:"url,omitempty"` | ||||||
|  |  | ||||||
|  | 	// only populated for repository subscriptions | ||||||
|  | 	RepositoryURL *string `json:"repository_url,omitempty"` | ||||||
|  |  | ||||||
|  | 	// only populated for thread subscriptions | ||||||
|  | 	ThreadURL *string `json:"thread_url,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListWatchers lists watchers of a particular repo. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: http://developer.github.com/v3/activity/watching/#list-watchers | ||||||
|  | func (s *ActivityService) ListWatchers(owner, repo string, opt *ListOptions) ([]User, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	watchers := new([]User) | ||||||
|  | 	resp, err := s.client.Do(req, watchers) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *watchers, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListWatched lists the repositories the specified user is watching.  Passing | ||||||
|  | // the empty string will fetch watched repos for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched | ||||||
|  | func (s *ActivityService) ListWatched(user string) ([]Repository, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if user != "" { | ||||||
|  | 		u = fmt.Sprintf("users/%v/subscriptions", user) | ||||||
|  | 	} else { | ||||||
|  | 		u = "user/subscriptions" | ||||||
|  | 	} | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	watched := new([]Repository) | ||||||
|  | 	resp, err := s.client.Do(req, watched) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *watched, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetRepositorySubscription returns the subscription for the specified | ||||||
|  | // repository for the authenticated user.  If the authenticated user is not | ||||||
|  | // watching the repository, a nil Subscription is returned. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription | ||||||
|  | func (s *ActivityService) GetRepositorySubscription(owner, repo string) (*Subscription, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sub := new(Subscription) | ||||||
|  | 	resp, err := s.client.Do(req, sub) | ||||||
|  | 	if err != nil { | ||||||
|  | 		// if it's just a 404, don't return that as an error | ||||||
|  | 		_, err = parseBoolResponse(err) | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return sub, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetRepositorySubscription sets the subscription for the specified repository | ||||||
|  | // for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription | ||||||
|  | func (s *ActivityService) SetRepositorySubscription(owner, repo string, subscription *Subscription) (*Subscription, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, subscription) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sub := new(Subscription) | ||||||
|  | 	resp, err := s.client.Do(req, sub) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return sub, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteRepositorySubscription deletes the subscription for the specified | ||||||
|  | // repository for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API Docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription | ||||||
|  | func (s *ActivityService) DeleteRepositorySubscription(owner, repo string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
							
								
								
									
										177
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_watching_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/activity_watching_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestActivityService_ListWatchers(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/subscribers", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page": "2", | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	watchers, _, err := client.Activity.ListWatchers("o", "r", &ListOptions{Page: 2}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListWatchers returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []User{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(watchers, want) { | ||||||
|  | 		t.Errorf("Activity.ListWatchers returned %+v, want %+v", watchers, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListWatched_authenticatedUser(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/subscriptions", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	watched, _, err := client.Activity.ListWatched("") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListWatched returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Repository{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(watched, want) { | ||||||
|  | 		t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_ListWatched_specifiedUser(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/subscriptions", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	watched, _, err := client.Activity.ListWatched("u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.ListWatched returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Repository{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(watched, want) { | ||||||
|  | 		t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_GetRepositorySubscription_true(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"subscribed":true}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	sub, _, err := client.Activity.GetRepositorySubscription("o", "r") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Subscription{Subscribed: Bool(true)} | ||||||
|  | 	if !reflect.DeepEqual(sub, want) { | ||||||
|  | 		t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_GetRepositorySubscription_false(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	sub, _, err := client.Activity.GetRepositorySubscription("o", "r") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var want *Subscription | ||||||
|  | 	if !reflect.DeepEqual(sub, want) { | ||||||
|  | 		t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_GetRepositorySubscription_error(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusBadRequest) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, _, err := client.Activity.GetRepositorySubscription("o", "r") | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected HTTP 400 response") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_SetRepositorySubscription(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Subscription{Subscribed: Bool(true)} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Subscription) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PUT") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"ignored":true}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	sub, _, err := client.Activity.SetRepositorySubscription("o", "r", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.SetRepositorySubscription returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Subscription{Ignored: Bool(true)} | ||||||
|  | 	if !reflect.DeepEqual(sub, want) { | ||||||
|  | 		t.Errorf("Activity.SetRepositorySubscription returned %+v, want %+v", sub, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivityService_DeleteRepositorySubscription(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 		w.WriteHeader(http.StatusNoContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Activity.DeleteRepositorySubscription("o", "r") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Activity.DeleteRepositorySubscription returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										137
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Package github provides a client for using the GitHub API. | ||||||
|  |  | ||||||
|  | Construct a new GitHub client, then use the various services on the client to | ||||||
|  | access different parts of the GitHub API. For example: | ||||||
|  |  | ||||||
|  | 	client := github.NewClient(nil) | ||||||
|  |  | ||||||
|  | 	// list all organizations for user "willnorris" | ||||||
|  | 	orgs, _, err := client.Organizations.List("willnorris", nil) | ||||||
|  |  | ||||||
|  | Set optional parameters for an API method by passing an Options object. | ||||||
|  |  | ||||||
|  | 	// list recently updated repositories for org "github" | ||||||
|  | 	opt := &github.RepositoryListByOrgOptions{Sort: "updated"} | ||||||
|  | 	repos, _, err := client.Repositories.ListByOrg("github", opt) | ||||||
|  |  | ||||||
|  | The services of a client divide the API into logical chunks and correspond to | ||||||
|  | the structure of the GitHub API documentation at | ||||||
|  | http://developer.github.com/v3/. | ||||||
|  |  | ||||||
|  | Authentication | ||||||
|  |  | ||||||
|  | The go-github library does not directly handle authentication. Instead, when | ||||||
|  | creating a new client, pass an http.Client that can handle authentication for | ||||||
|  | you. The easiest and recommended way to do this is using the golang.org/x/oauth2 | ||||||
|  | library, but you can always use any other library that provides an http.Client. | ||||||
|  | If you have an OAuth2 access token (for example, a personal API token), you can | ||||||
|  | use it with the oauth2 library using: | ||||||
|  |  | ||||||
|  | 	import "golang.org/x/oauth2" | ||||||
|  |  | ||||||
|  | 	// tokenSource is an oauth2.TokenSource which returns a static access token | ||||||
|  | 	type tokenSource struct { | ||||||
|  | 		token *oauth2.Token | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Token implements the oauth2.TokenSource interface | ||||||
|  | 	func (t *tokenSource) Token() (*oauth2.Token, error){ | ||||||
|  | 		return t.token, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	func main() { | ||||||
|  | 		ts := &tokenSource{ | ||||||
|  | 			&oauth2.Token{AccessToken: "... your access token ..."}, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		tc := oauth2.NewClient(oauth2.NoContext, ts) | ||||||
|  |  | ||||||
|  | 		client := github.NewClient(tc) | ||||||
|  |  | ||||||
|  | 		// list all repositories for the authenticated user | ||||||
|  | 		repos, _, err := client.Repositories.List("", nil) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | Note that when using an authenticated Client, all calls made by the client will | ||||||
|  | include the specified OAuth token. Therefore, authenticated clients should | ||||||
|  | almost never be shared between different users. | ||||||
|  |  | ||||||
|  | Rate Limiting | ||||||
|  |  | ||||||
|  | GitHub imposes a rate limit on all API clients.  Unauthenticated clients are | ||||||
|  | limited to 60 requests per hour, while authenticated clients can make up to | ||||||
|  | 5,000 requests per hour.  To receive the higher rate limit when making calls | ||||||
|  | that are not issued on behalf of a user, use the | ||||||
|  | UnauthenticatedRateLimitedTransport. | ||||||
|  |  | ||||||
|  | The Rate field on a client tracks the rate limit information based on the most | ||||||
|  | recent API call.  This is updated on every call, but may be out of date if it's | ||||||
|  | been some time since the last API call and other clients have made subsequent | ||||||
|  | requests since then.  You can always call RateLimit() directly to get the most | ||||||
|  | up-to-date rate limit data for the client. | ||||||
|  |  | ||||||
|  | Learn more about GitHub rate limiting at | ||||||
|  | http://developer.github.com/v3/#rate-limiting. | ||||||
|  |  | ||||||
|  | Conditional Requests | ||||||
|  |  | ||||||
|  | The GitHub API has good support for conditional requests which will help | ||||||
|  | prevent you from burning through your rate limit, as well as help speed up your | ||||||
|  | application.  go-github does not handle conditional requests directly, but is | ||||||
|  | instead designed to work with a caching http.Transport.  We recommend using | ||||||
|  | https://github.com/gregjones/httpcache, which can be used in conjuction with | ||||||
|  | https://github.com/sourcegraph/apiproxy to provide additional flexibility and | ||||||
|  | control of caching rules. | ||||||
|  |  | ||||||
|  | Learn more about GitHub conditional requests at | ||||||
|  | https://developer.github.com/v3/#conditional-requests. | ||||||
|  |  | ||||||
|  | Creating and Updating Resources | ||||||
|  |  | ||||||
|  | All structs for GitHub resources use pointer values for all non-repeated fields. | ||||||
|  | This allows distinguishing between unset fields and those set to a zero-value. | ||||||
|  | Helper functions have been provided to easily create these pointers for string, | ||||||
|  | bool, and int values.  For example: | ||||||
|  |  | ||||||
|  | 	// create a new private repository named "foo" | ||||||
|  | 	repo := &github.Repository{ | ||||||
|  | 		Name:    github.String("foo"), | ||||||
|  | 		Private: github.Bool(true), | ||||||
|  | 	} | ||||||
|  | 	client.Repositories.Create("", repo) | ||||||
|  |  | ||||||
|  | Users who have worked with protocol buffers should find this pattern familiar. | ||||||
|  |  | ||||||
|  | Pagination | ||||||
|  |  | ||||||
|  | All requests for resource collections (repos, pull requests, issues, etc) | ||||||
|  | support pagination. Pagination options are described in the | ||||||
|  | ListOptions struct and passed to the list methods directly or as an | ||||||
|  | embedded type of a more specific list options struct (for example | ||||||
|  | PullRequestListOptions).  Pages information is available via Response struct. | ||||||
|  |  | ||||||
|  | 	opt := &github.RepositoryListByOrgOptions{ | ||||||
|  | 		ListOptions: github.ListOptions{PerPage: 10}, | ||||||
|  | 	} | ||||||
|  | 	// get all pages of results | ||||||
|  | 	var allRepos []github.Repository | ||||||
|  | 	for { | ||||||
|  | 		repos, resp, err := client.Repositories.ListByOrg("github", opt) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		allRepos = append(allRepos, repos...) | ||||||
|  | 		if resp.NextPage == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		opt.ListOptions.Page = resp.NextPage | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | */ | ||||||
|  | package github | ||||||
							
								
								
									
										263
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // GistsService handles communication with the Gist related | ||||||
|  | // methods of the GitHub API. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/ | ||||||
|  | type GistsService struct { | ||||||
|  | 	client *Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Gist represents a GitHub's gist. | ||||||
|  | type Gist struct { | ||||||
|  | 	ID          *string                   `json:"id,omitempty"` | ||||||
|  | 	Description *string                   `json:"description,omitempty"` | ||||||
|  | 	Public      *bool                     `json:"public,omitempty"` | ||||||
|  | 	Owner       *User                     `json:"owner,omitempty"` | ||||||
|  | 	Files       map[GistFilename]GistFile `json:"files,omitempty"` | ||||||
|  | 	Comments    *int                      `json:"comments,omitempty"` | ||||||
|  | 	HTMLURL     *string                   `json:"html_url,omitempty"` | ||||||
|  | 	GitPullURL  *string                   `json:"git_pull_url,omitempty"` | ||||||
|  | 	GitPushURL  *string                   `json:"git_push_url,omitempty"` | ||||||
|  | 	CreatedAt   *time.Time                `json:"created_at,omitempty"` | ||||||
|  | 	UpdatedAt   *time.Time                `json:"updated_at,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g Gist) String() string { | ||||||
|  | 	return Stringify(g) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GistFilename represents filename on a gist. | ||||||
|  | type GistFilename string | ||||||
|  |  | ||||||
|  | // GistFile represents a file on a gist. | ||||||
|  | type GistFile struct { | ||||||
|  | 	Size     *int    `json:"size,omitempty"` | ||||||
|  | 	Filename *string `json:"filename,omitempty"` | ||||||
|  | 	RawURL   *string `json:"raw_url,omitempty"` | ||||||
|  | 	Content  *string `json:"content,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g GistFile) String() string { | ||||||
|  | 	return Stringify(g) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GistListOptions specifies the optional parameters to the | ||||||
|  | // GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods. | ||||||
|  | type GistListOptions struct { | ||||||
|  | 	// Since filters Gists by time. | ||||||
|  | 	Since time.Time `url:"since,omitempty"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List gists for a user. Passing the empty string will list | ||||||
|  | // all public gists if called anonymously. However, if the call | ||||||
|  | // is authenticated, it will returns all gists for the authenticated | ||||||
|  | // user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#list-gists | ||||||
|  | func (s *GistsService) List(user string, opt *GistListOptions) ([]Gist, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if user != "" { | ||||||
|  | 		u = fmt.Sprintf("users/%v/gists", user) | ||||||
|  | 	} else { | ||||||
|  | 		u = "gists" | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gists := new([]Gist) | ||||||
|  | 	resp, err := s.client.Do(req, gists) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *gists, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListAll lists all public gists. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#list-gists | ||||||
|  | func (s *GistsService) ListAll(opt *GistListOptions) ([]Gist, *Response, error) { | ||||||
|  | 	u, err := addOptions("gists/public", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gists := new([]Gist) | ||||||
|  | 	resp, err := s.client.Do(req, gists) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *gists, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListStarred lists starred gists of authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#list-gists | ||||||
|  | func (s *GistsService) ListStarred(opt *GistListOptions) ([]Gist, *Response, error) { | ||||||
|  | 	u, err := addOptions("gists/starred", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gists := new([]Gist) | ||||||
|  | 	resp, err := s.client.Do(req, gists) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *gists, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get a single gist. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#get-a-single-gist | ||||||
|  | func (s *GistsService) Get(id string) (*Gist, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v", id) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	gist := new(Gist) | ||||||
|  | 	resp, err := s.client.Do(req, gist) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return gist, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create a gist for authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#create-a-gist | ||||||
|  | func (s *GistsService) Create(gist *Gist) (*Gist, *Response, error) { | ||||||
|  | 	u := "gists" | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, gist) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	g := new(Gist) | ||||||
|  | 	resp, err := s.client.Do(req, g) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return g, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Edit a gist. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#edit-a-gist | ||||||
|  | func (s *GistsService) Edit(id string, gist *Gist) (*Gist, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v", id) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, gist) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	g := new(Gist) | ||||||
|  | 	resp, err := s.client.Do(req, g) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return g, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Delete a gist. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#delete-a-gist | ||||||
|  | func (s *GistsService) Delete(id string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v", id) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Star a gist on behalf of authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#star-a-gist | ||||||
|  | func (s *GistsService) Star(id string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/star", id) | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unstar a gist on a behalf of authenticated user. | ||||||
|  | // | ||||||
|  | // Github API docs: http://developer.github.com/v3/gists/#unstar-a-gist | ||||||
|  | func (s *GistsService) Unstar(id string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/star", id) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsStarred checks if a gist is starred by authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#check-if-a-gist-is-starred | ||||||
|  | func (s *GistsService) IsStarred(id string) (bool, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/star", id) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, nil, err | ||||||
|  | 	} | ||||||
|  | 	resp, err := s.client.Do(req, nil) | ||||||
|  | 	starred, err := parseBoolResponse(err) | ||||||
|  | 	return starred, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fork a gist. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/#fork-a-gist | ||||||
|  | func (s *GistsService) Fork(id string) (*Gist, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/forks", id) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	g := new(Gist) | ||||||
|  | 	resp, err := s.client.Do(req, g) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return g, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										118
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists_comments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists_comments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // GistComment represents a Gist comment. | ||||||
|  | type GistComment struct { | ||||||
|  | 	ID        *int       `json:"id,omitempty"` | ||||||
|  | 	URL       *string    `json:"url,omitempty"` | ||||||
|  | 	Body      *string    `json:"body,omitempty"` | ||||||
|  | 	User      *User      `json:"user,omitempty"` | ||||||
|  | 	CreatedAt *time.Time `json:"created_at,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g GistComment) String() string { | ||||||
|  | 	return Stringify(g) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListComments lists all comments for a gist. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/comments/#list-comments-on-a-gist | ||||||
|  | func (s *GistsService) ListComments(gistID string, opt *ListOptions) ([]GistComment, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/comments", gistID) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	comments := new([]GistComment) | ||||||
|  | 	resp, err := s.client.Do(req, comments) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *comments, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetComment retrieves a single comment from a gist. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/comments/#get-a-single-comment | ||||||
|  | func (s *GistsService) GetComment(gistID string, commentID int) (*GistComment, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c := new(GistComment) | ||||||
|  | 	resp, err := s.client.Do(req, c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateComment creates a comment for a gist. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/comments/#create-a-comment | ||||||
|  | func (s *GistsService) CreateComment(gistID string, comment *GistComment) (*GistComment, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/comments", gistID) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, comment) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c := new(GistComment) | ||||||
|  | 	resp, err := s.client.Do(req, c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EditComment edits an existing gist comment. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/comments/#edit-a-comment | ||||||
|  | func (s *GistsService) EditComment(gistID string, commentID int, comment *GistComment) (*GistComment, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, comment) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c := new(GistComment) | ||||||
|  | 	resp, err := s.client.Do(req, c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteComment deletes a gist comment. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gists/comments/#delete-a-comment | ||||||
|  | func (s *GistsService) DeleteComment(gistID string, commentID int) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
							
								
								
									
										155
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists_comments_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists_comments_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGistsService_ListComments(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{"page": "2"}) | ||||||
|  | 		fmt.Fprint(w, `[{"id": 1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	comments, _, err := client.Gists.ListComments("1", opt) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Comments returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []GistComment{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(comments, want) { | ||||||
|  | 		t.Errorf("Gists.ListComments returned %+v, want %+v", comments, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_ListComments_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.ListComments("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_GetComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"id": 1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	comment, _, err := client.Gists.GetComment("1", 2) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.GetComment returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &GistComment{ID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(comment, want) { | ||||||
|  | 		t.Errorf("Gists.GetComment returned %+v, want %+v", comment, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_GetComment_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.GetComment("%", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_CreateComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &GistComment{ID: Int(1), Body: String("b")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(GistComment) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"id":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	comment, _, err := client.Gists.CreateComment("1", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.CreateComment returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &GistComment{ID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(comment, want) { | ||||||
|  | 		t.Errorf("Gists.CreateComment returned %+v, want %+v", comment, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_CreateComment_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.CreateComment("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_EditComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &GistComment{ID: Int(1), Body: String("b")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(GistComment) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"id":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	comment, _, err := client.Gists.EditComment("1", 2, input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.EditComment returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &GistComment{ID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(comment, want) { | ||||||
|  | 		t.Errorf("Gists.EditComment returned %+v, want %+v", comment, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_EditComment_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.EditComment("%", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_DeleteComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Gists.DeleteComment("1", 2) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Delete returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_DeleteComment_invalidID(t *testing.T) { | ||||||
|  | 	_, err := client.Gists.DeleteComment("%", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										385
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gists_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,385 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGistsService_List_specifiedUser(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	since := "2013-01-01T00:00:00Z" | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/users/u/gists", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"since": since, | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id": "1"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} | ||||||
|  | 	gists, _, err := client.Gists.List("u", opt) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.List returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Gist{{ID: String("1")}} | ||||||
|  | 	if !reflect.DeepEqual(gists, want) { | ||||||
|  | 		t.Errorf("Gists.List returned %+v, want %+v", gists, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_List_authenticatedUser(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id": "1"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	gists, _, err := client.Gists.List("", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.List returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Gist{{ID: String("1")}} | ||||||
|  | 	if !reflect.DeepEqual(gists, want) { | ||||||
|  | 		t.Errorf("Gists.List returned %+v, want %+v", gists, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_List_invalidUser(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.List("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_ListAll(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	since := "2013-01-01T00:00:00Z" | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/public", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"since": since, | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id": "1"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} | ||||||
|  | 	gists, _, err := client.Gists.ListAll(opt) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.ListAll returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Gist{{ID: String("1")}} | ||||||
|  | 	if !reflect.DeepEqual(gists, want) { | ||||||
|  | 		t.Errorf("Gists.ListAll returned %+v, want %+v", gists, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_ListStarred(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	since := "2013-01-01T00:00:00Z" | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/starred", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"since": since, | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id": "1"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} | ||||||
|  | 	gists, _, err := client.Gists.ListStarred(opt) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.ListStarred returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Gist{{ID: String("1")}} | ||||||
|  | 	if !reflect.DeepEqual(gists, want) { | ||||||
|  | 		t.Errorf("Gists.ListStarred returned %+v, want %+v", gists, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Get(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"id": "1"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	gist, _, err := client.Gists.Get("1") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Get returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Gist{ID: String("1")} | ||||||
|  | 	if !reflect.DeepEqual(gist, want) { | ||||||
|  | 		t.Errorf("Gists.Get returned %+v, want %+v", gist, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Get_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.Get("%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Create(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Gist{ | ||||||
|  | 		Description: String("Gist description"), | ||||||
|  | 		Public:      Bool(false), | ||||||
|  | 		Files: map[GistFilename]GistFile{ | ||||||
|  | 			"test.txt": {Content: String("Gist file content")}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Gist) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, | ||||||
|  | 			` | ||||||
|  | 			{ | ||||||
|  | 				"id": "1", | ||||||
|  | 				"description": "Gist description", | ||||||
|  | 				"public": false, | ||||||
|  | 				"files": { | ||||||
|  | 					"test.txt": { | ||||||
|  | 						"filename": "test.txt" | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	gist, _, err := client.Gists.Create(input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Create returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Gist{ | ||||||
|  | 		ID:          String("1"), | ||||||
|  | 		Description: String("Gist description"), | ||||||
|  | 		Public:      Bool(false), | ||||||
|  | 		Files: map[GistFilename]GistFile{ | ||||||
|  | 			"test.txt": {Filename: String("test.txt")}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(gist, want) { | ||||||
|  | 		t.Errorf("Gists.Create returned %+v, want %+v", gist, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Edit(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Gist{ | ||||||
|  | 		Description: String("New description"), | ||||||
|  | 		Files: map[GistFilename]GistFile{ | ||||||
|  | 			"new.txt": {Content: String("new file content")}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Gist) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, | ||||||
|  | 			` | ||||||
|  | 			{ | ||||||
|  | 				"id": "1", | ||||||
|  | 				"description": "new description", | ||||||
|  | 				"public": false, | ||||||
|  | 				"files": { | ||||||
|  | 					"test.txt": { | ||||||
|  | 						"filename": "test.txt" | ||||||
|  | 					}, | ||||||
|  | 					"new.txt": { | ||||||
|  | 						"filename": "new.txt" | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	gist, _, err := client.Gists.Edit("1", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Edit returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Gist{ | ||||||
|  | 		ID:          String("1"), | ||||||
|  | 		Description: String("new description"), | ||||||
|  | 		Public:      Bool(false), | ||||||
|  | 		Files: map[GistFilename]GistFile{ | ||||||
|  | 			"test.txt": {Filename: String("test.txt")}, | ||||||
|  | 			"new.txt":  {Filename: String("new.txt")}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(gist, want) { | ||||||
|  | 		t.Errorf("Gists.Edit returned %+v, want %+v", gist, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Edit_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.Edit("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Delete(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Gists.Delete("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Delete returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Delete_invalidID(t *testing.T) { | ||||||
|  | 	_, err := client.Gists.Delete("%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Star(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "PUT") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Gists.Star("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Star returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Star_invalidID(t *testing.T) { | ||||||
|  | 	_, err := client.Gists.Star("%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Unstar(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Gists.Unstar("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Unstar returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Unstar_invalidID(t *testing.T) { | ||||||
|  | 	_, err := client.Gists.Unstar("%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_IsStarred_hasStar(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNoContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	star, _, err := client.Gists.IsStarred("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Starred returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := true; star != want { | ||||||
|  | 		t.Errorf("Gists.Starred returned %+v, want %+v", star, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_IsStarred_noStar(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	star, _, err := client.Gists.IsStarred("1") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Starred returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := false; star != want { | ||||||
|  | 		t.Errorf("Gists.Starred returned %+v, want %+v", star, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_IsStarred_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.IsStarred("%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Fork(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		fmt.Fprint(w, `{"id": "2"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	gist, _, err := client.Gists.Fork("1") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gists.Fork returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Gist{ID: String("2")} | ||||||
|  | 	if !reflect.DeepEqual(gist, want) { | ||||||
|  | 		t.Errorf("Gists.Fork returned %+v, want %+v", gist, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGistsService_Fork_invalidID(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gists.Fork("%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | // GitService handles communication with the git data related | ||||||
|  | // methods of the GitHub API. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/ | ||||||
|  | type GitService struct { | ||||||
|  | 	client *Client | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_blobs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_blobs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // Blob represents a blob object. | ||||||
|  | type Blob struct { | ||||||
|  | 	Content  *string `json:"content,omitempty"` | ||||||
|  | 	Encoding *string `json:"encoding,omitempty"` | ||||||
|  | 	SHA      *string `json:"sha,omitempty"` | ||||||
|  | 	Size     *int    `json:"size,omitempty"` | ||||||
|  | 	URL      *string `json:"url,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetBlob fetchs a blob from a repo given a SHA. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/blobs/#get-a-blob | ||||||
|  | func (s *GitService) GetBlob(owner string, repo string, sha string) (*Blob, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	blob := new(Blob) | ||||||
|  | 	resp, err := s.client.Do(req, blob) | ||||||
|  | 	return blob, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateBlob creates a blob object. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/blobs/#create-a-blob | ||||||
|  | func (s *GitService) CreateBlob(owner string, repo string, blob *Blob) (*Blob, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, blob) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t := new(Blob) | ||||||
|  | 	resp, err := s.client.Do(req, t) | ||||||
|  | 	return t, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										92
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_blobs_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_blobs_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGitService_GetBlob(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/blobs/s", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		if m := "GET"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, `{ | ||||||
|  | 			  "sha": "s", | ||||||
|  | 			  "content": "blob content" | ||||||
|  | 			}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	blob, _, err := client.Git.GetBlob("o", "r", "s") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.GetBlob returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := Blob{ | ||||||
|  | 		SHA:     String("s"), | ||||||
|  | 		Content: String("blob content"), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(*blob, want) { | ||||||
|  | 		t.Errorf("Blob.Get returned %+v, want %+v", *blob, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_GetBlob_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Git.GetBlob("%", "%", "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateBlob(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Blob{ | ||||||
|  | 		SHA:      String("s"), | ||||||
|  | 		Content:  String("blob content"), | ||||||
|  | 		Encoding: String("utf-8"), | ||||||
|  | 		Size:     Int(12), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/blobs", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Blob) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		if m := "POST"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		want := input | ||||||
|  | 		if !reflect.DeepEqual(v, want) { | ||||||
|  | 			t.Errorf("Git.CreateBlob request body: %+v, want %+v", v, want) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{ | ||||||
|  | 		 "sha": "s", | ||||||
|  | 		 "content": "blob content", | ||||||
|  | 		 "encoding": "utf-8", | ||||||
|  | 		 "size": 12 | ||||||
|  | 		}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	blob, _, err := client.Git.CreateBlob("o", "r", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.CreateBlob returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := input | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(*blob, *want) { | ||||||
|  | 		t.Errorf("Git.CreateBlob returned %+v, want %+v", *blob, *want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateBlob_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Git.CreateBlob("%", "%", &Blob{}) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_commits.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_commits.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Commit represents a GitHub commit. | ||||||
|  | type Commit struct { | ||||||
|  | 	SHA       *string       `json:"sha,omitempty"` | ||||||
|  | 	Author    *CommitAuthor `json:"author,omitempty"` | ||||||
|  | 	Committer *CommitAuthor `json:"committer,omitempty"` | ||||||
|  | 	Message   *string       `json:"message,omitempty"` | ||||||
|  | 	Tree      *Tree         `json:"tree,omitempty"` | ||||||
|  | 	Parents   []Commit      `json:"parents,omitempty"` | ||||||
|  | 	Stats     *CommitStats  `json:"stats,omitempty"` | ||||||
|  | 	URL       *string       `json:"url,omitempty"` | ||||||
|  |  | ||||||
|  | 	// CommentCount is the number of GitHub comments on the commit.  This | ||||||
|  | 	// is only populated for requests that fetch GitHub data like | ||||||
|  | 	// Pulls.ListCommits, Repositories.ListCommits, etc. | ||||||
|  | 	CommentCount *int `json:"comment_count,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c Commit) String() string { | ||||||
|  | 	return Stringify(c) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CommitAuthor represents the author or committer of a commit.  The commit | ||||||
|  | // author may not correspond to a GitHub User. | ||||||
|  | type CommitAuthor struct { | ||||||
|  | 	Date  *time.Time `json:"date,omitempty"` | ||||||
|  | 	Name  *string    `json:"name,omitempty"` | ||||||
|  | 	Email *string    `json:"email,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c CommitAuthor) String() string { | ||||||
|  | 	return Stringify(c) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetCommit fetchs the Commit object for a given SHA. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/commits/#get-a-commit | ||||||
|  | func (s *GitService) GetCommit(owner string, repo string, sha string) (*Commit, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c := new(Commit) | ||||||
|  | 	resp, err := s.client.Do(req, c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createCommit represents the body of a CreateCommit request. | ||||||
|  | type createCommit struct { | ||||||
|  | 	Author    *CommitAuthor `json:"author,omitempty"` | ||||||
|  | 	Committer *CommitAuthor `json:"committer,omitempty"` | ||||||
|  | 	Message   *string       `json:"message,omitempty"` | ||||||
|  | 	Tree      *string       `json:"tree,omitempty"` | ||||||
|  | 	Parents   []string      `json:"parents,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateCommit creates a new commit in a repository. | ||||||
|  | // | ||||||
|  | // The commit.Committer is optional and will be filled with the commit.Author | ||||||
|  | // data if omitted. If the commit.Author is omitted, it will be filled in with | ||||||
|  | // the authenticated user’s information and the current date. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/commits/#create-a-commit | ||||||
|  | func (s *GitService) CreateCommit(owner string, repo string, commit *Commit) (*Commit, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo) | ||||||
|  |  | ||||||
|  | 	body := &createCommit{} | ||||||
|  | 	if commit != nil { | ||||||
|  | 		parents := make([]string, len(commit.Parents)) | ||||||
|  | 		for i, parent := range commit.Parents { | ||||||
|  | 			parents[i] = *parent.SHA | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		body = &createCommit{ | ||||||
|  | 			Author:    commit.Author, | ||||||
|  | 			Committer: commit.Committer, | ||||||
|  | 			Message:   commit.Message, | ||||||
|  | 			Tree:      commit.Tree.SHA, | ||||||
|  | 			Parents:   parents, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c := new(Commit) | ||||||
|  | 	resp, err := s.client.Do(req, c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_commits_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_commits_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGitService_GetCommit(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/commits/s", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"sha":"s","message":"m","author":{"name":"n"}}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	commit, _, err := client.Git.GetCommit("o", "r", "s") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.GetCommit returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Commit{SHA: String("s"), Message: String("m"), Author: &CommitAuthor{Name: String("n")}} | ||||||
|  | 	if !reflect.DeepEqual(commit, want) { | ||||||
|  | 		t.Errorf("Git.GetCommit returned %+v, want %+v", commit, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_GetCommit_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Git.GetCommit("%", "%", "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateCommit(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Commit{ | ||||||
|  | 		Message: String("m"), | ||||||
|  | 		Tree:    &Tree{SHA: String("t")}, | ||||||
|  | 		Parents: []Commit{{SHA: String("p")}}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/commits", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(createCommit) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  |  | ||||||
|  | 		want := &createCommit{ | ||||||
|  | 			Message: input.Message, | ||||||
|  | 			Tree:    String("t"), | ||||||
|  | 			Parents: []string{"p"}, | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(v, want) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, want) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, `{"sha":"s"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	commit, _, err := client.Git.CreateCommit("o", "r", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.CreateCommit returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Commit{SHA: String("s")} | ||||||
|  | 	if !reflect.DeepEqual(commit, want) { | ||||||
|  | 		t.Errorf("Git.CreateCommit returned %+v, want %+v", commit, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateCommit_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Git.CreateCommit("%", "%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										162
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_refs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_refs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Reference represents a GitHub reference. | ||||||
|  | type Reference struct { | ||||||
|  | 	Ref    *string    `json:"ref"` | ||||||
|  | 	URL    *string    `json:"url"` | ||||||
|  | 	Object *GitObject `json:"object"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r Reference) String() string { | ||||||
|  | 	return Stringify(r) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GitObject represents a Git object. | ||||||
|  | type GitObject struct { | ||||||
|  | 	Type *string `json:"type"` | ||||||
|  | 	SHA  *string `json:"sha"` | ||||||
|  | 	URL  *string `json:"url"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o GitObject) String() string { | ||||||
|  | 	return Stringify(o) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createRefRequest represents the payload for creating a reference. | ||||||
|  | type createRefRequest struct { | ||||||
|  | 	Ref *string `json:"ref"` | ||||||
|  | 	SHA *string `json:"sha"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // updateRefRequest represents the payload for updating a reference. | ||||||
|  | type updateRefRequest struct { | ||||||
|  | 	SHA   *string `json:"sha"` | ||||||
|  | 	Force *bool   `json:"force"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetRef fetches the Reference object for a given Git ref. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/refs/#get-a-reference | ||||||
|  | func (s *GitService) GetRef(owner string, repo string, ref string) (*Reference, *Response, error) { | ||||||
|  | 	ref = strings.TrimPrefix(ref, "refs/") | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r := new(Reference) | ||||||
|  | 	resp, err := s.client.Do(req, r) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return r, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ReferenceListOptions specifies optional parameters to the | ||||||
|  | // GitService.ListRefs method. | ||||||
|  | type ReferenceListOptions struct { | ||||||
|  | 	Type string `url:"-"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListRefs lists all refs in a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/refs/#get-all-references | ||||||
|  | func (s *GitService) ListRefs(owner, repo string, opt *ReferenceListOptions) ([]Reference, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if opt != nil && opt.Type != "" { | ||||||
|  | 		u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type) | ||||||
|  | 	} else { | ||||||
|  | 		u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var rs []Reference | ||||||
|  | 	resp, err := s.client.Do(req, &rs) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return rs, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateRef creates a new ref in a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/refs/#create-a-reference | ||||||
|  | func (s *GitService) CreateRef(owner string, repo string, ref *Reference) (*Reference, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, &createRefRequest{ | ||||||
|  | 		// back-compat with previous behavior that didn't require 'refs/' prefix | ||||||
|  | 		Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")), | ||||||
|  | 		SHA: ref.Object.SHA, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r := new(Reference) | ||||||
|  | 	resp, err := s.client.Do(req, r) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return r, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateRef updates an existing ref in a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/refs/#update-a-reference | ||||||
|  | func (s *GitService) UpdateRef(owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) { | ||||||
|  | 	refPath := strings.TrimPrefix(*ref.Ref, "refs/") | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{ | ||||||
|  | 		SHA:   ref.Object.SHA, | ||||||
|  | 		Force: &force, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r := new(Reference) | ||||||
|  | 	resp, err := s.client.Do(req, r) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return r, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteRef deletes a ref from a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/refs/#delete-a-reference | ||||||
|  | func (s *GitService) DeleteRef(owner string, repo string, ref string) (*Response, error) { | ||||||
|  | 	ref = strings.TrimPrefix(ref, "refs/") | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
							
								
								
									
										280
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_refs_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_refs_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGitService_GetRef(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, ` | ||||||
|  | 		  { | ||||||
|  | 		    "ref": "refs/heads/b", | ||||||
|  | 		    "url": "https://api.github.com/repos/o/r/git/refs/heads/b", | ||||||
|  | 		    "object": { | ||||||
|  | 		      "type": "commit", | ||||||
|  | 		      "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", | ||||||
|  | 		      "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" | ||||||
|  | 		    } | ||||||
|  | 		  }`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	ref, _, err := client.Git.GetRef("o", "r", "refs/heads/b") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.GetRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Reference{ | ||||||
|  | 		Ref: String("refs/heads/b"), | ||||||
|  | 		URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), | ||||||
|  | 		Object: &GitObject{ | ||||||
|  | 			Type: String("commit"), | ||||||
|  | 			SHA:  String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 			URL:  String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(ref, want) { | ||||||
|  | 		t.Errorf("Git.GetRef returned %+v, want %+v", ref, want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// without 'refs/' prefix | ||||||
|  | 	if _, _, err := client.Git.GetRef("o", "r", "heads/b"); err != nil { | ||||||
|  | 		t.Errorf("Git.GetRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_ListRefs(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, ` | ||||||
|  | 		  [ | ||||||
|  | 		    { | ||||||
|  | 		      "ref": "refs/heads/branchA", | ||||||
|  | 		      "url": "https://api.github.com/repos/o/r/git/refs/heads/branchA", | ||||||
|  | 		      "object": { | ||||||
|  | 			"type": "commit", | ||||||
|  | 			"sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", | ||||||
|  | 			"url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" | ||||||
|  | 		      } | ||||||
|  | 		    }, | ||||||
|  | 		    { | ||||||
|  | 		      "ref": "refs/heads/branchB", | ||||||
|  | 		      "url": "https://api.github.com/repos/o/r/git/refs/heads/branchB", | ||||||
|  | 		      "object": { | ||||||
|  | 			"type": "commit", | ||||||
|  | 			"sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", | ||||||
|  | 			"url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" | ||||||
|  | 		      } | ||||||
|  | 		    } | ||||||
|  | 		  ]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	refs, _, err := client.Git.ListRefs("o", "r", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.ListRefs returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Reference{ | ||||||
|  | 		{ | ||||||
|  | 			Ref: String("refs/heads/branchA"), | ||||||
|  | 			URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchA"), | ||||||
|  | 			Object: &GitObject{ | ||||||
|  | 				Type: String("commit"), | ||||||
|  | 				SHA:  String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 				URL:  String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Ref: String("refs/heads/branchB"), | ||||||
|  | 			URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchB"), | ||||||
|  | 			Object: &GitObject{ | ||||||
|  | 				Type: String("commit"), | ||||||
|  | 				SHA:  String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 				URL:  String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(refs, want) { | ||||||
|  | 		t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_ListRefs_options(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/refs/t", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{"page": "2"}) | ||||||
|  | 		fmt.Fprint(w, `[{"ref": "r"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ReferenceListOptions{Type: "t", ListOptions: ListOptions{Page: 2}} | ||||||
|  | 	refs, _, err := client.Git.ListRefs("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.ListRefs returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Reference{{Ref: String("r")}} | ||||||
|  | 	if !reflect.DeepEqual(refs, want) { | ||||||
|  | 		t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateRef(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	args := &createRefRequest{ | ||||||
|  | 		Ref: String("refs/heads/b"), | ||||||
|  | 		SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(createRefRequest) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, args) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, args) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, ` | ||||||
|  | 		  { | ||||||
|  | 		    "ref": "refs/heads/b", | ||||||
|  | 		    "url": "https://api.github.com/repos/o/r/git/refs/heads/b", | ||||||
|  | 		    "object": { | ||||||
|  | 		      "type": "commit", | ||||||
|  | 		      "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", | ||||||
|  | 		      "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" | ||||||
|  | 		    } | ||||||
|  | 		  }`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	ref, _, err := client.Git.CreateRef("o", "r", &Reference{ | ||||||
|  | 		Ref: String("refs/heads/b"), | ||||||
|  | 		Object: &GitObject{ | ||||||
|  | 			SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.CreateRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Reference{ | ||||||
|  | 		Ref: String("refs/heads/b"), | ||||||
|  | 		URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), | ||||||
|  | 		Object: &GitObject{ | ||||||
|  | 			Type: String("commit"), | ||||||
|  | 			SHA:  String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 			URL:  String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(ref, want) { | ||||||
|  | 		t.Errorf("Git.CreateRef returned %+v, want %+v", ref, want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// without 'refs/' prefix | ||||||
|  | 	_, _, err = client.Git.CreateRef("o", "r", &Reference{ | ||||||
|  | 		Ref: String("heads/b"), | ||||||
|  | 		Object: &GitObject{ | ||||||
|  | 			SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.CreateRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_UpdateRef(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	args := &updateRefRequest{ | ||||||
|  | 		SHA:   String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 		Force: Bool(true), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(updateRefRequest) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		if !reflect.DeepEqual(v, args) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, args) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, ` | ||||||
|  | 		  { | ||||||
|  | 		    "ref": "refs/heads/b", | ||||||
|  | 		    "url": "https://api.github.com/repos/o/r/git/refs/heads/b", | ||||||
|  | 		    "object": { | ||||||
|  | 		      "type": "commit", | ||||||
|  | 		      "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", | ||||||
|  | 		      "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" | ||||||
|  | 		    } | ||||||
|  | 		  }`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	ref, _, err := client.Git.UpdateRef("o", "r", &Reference{ | ||||||
|  | 		Ref:    String("refs/heads/b"), | ||||||
|  | 		Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, | ||||||
|  | 	}, true) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.UpdateRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Reference{ | ||||||
|  | 		Ref: String("refs/heads/b"), | ||||||
|  | 		URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), | ||||||
|  | 		Object: &GitObject{ | ||||||
|  | 			Type: String("commit"), | ||||||
|  | 			SHA:  String("aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 			URL:  String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(ref, want) { | ||||||
|  | 		t.Errorf("Git.UpdateRef returned %+v, want %+v", ref, want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// without 'refs/' prefix | ||||||
|  | 	_, _, err = client.Git.UpdateRef("o", "r", &Reference{ | ||||||
|  | 		Ref:    String("heads/b"), | ||||||
|  | 		Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, | ||||||
|  | 	}, true) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.UpdateRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_DeleteRef(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Git.DeleteRef("o", "r", "refs/heads/b") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.DeleteRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// without 'refs/' prefix | ||||||
|  | 	if _, err := client.Git.DeleteRef("o", "r", "heads/b"); err != nil { | ||||||
|  | 		t.Errorf("Git.DeleteRef returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_tags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_tags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Tag represents a tag object. | ||||||
|  | type Tag struct { | ||||||
|  | 	Tag     *string       `json:"tag,omitempty"` | ||||||
|  | 	SHA     *string       `json:"sha,omitempty"` | ||||||
|  | 	URL     *string       `json:"url,omitempty"` | ||||||
|  | 	Message *string       `json:"message,omitempty"` | ||||||
|  | 	Tagger  *CommitAuthor `json:"tagger,omitempty"` | ||||||
|  | 	Object  *GitObject    `json:"object,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createTagRequest represents the body of a CreateTag request.  This is mostly | ||||||
|  | // identical to Tag with the exception that the object SHA and Type are | ||||||
|  | // top-level fields, rather than being nested inside a JSON object. | ||||||
|  | type createTagRequest struct { | ||||||
|  | 	Tag     *string       `json:"tag,omitempty"` | ||||||
|  | 	Message *string       `json:"message,omitempty"` | ||||||
|  | 	Object  *string       `json:"object,omitempty"` | ||||||
|  | 	Type    *string       `json:"type,omitempty"` | ||||||
|  | 	Tagger  *CommitAuthor `json:"tagger,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetTag fetchs a tag from a repo given a SHA. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/tags/#get-a-tag | ||||||
|  | func (s *GitService) GetTag(owner string, repo string, sha string) (*Tag, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/tags/%v", owner, repo, sha) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tag := new(Tag) | ||||||
|  | 	resp, err := s.client.Do(req, tag) | ||||||
|  | 	return tag, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateTag creates a tag object. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/tags/#create-a-tag-object | ||||||
|  | func (s *GitService) CreateTag(owner string, repo string, tag *Tag) (*Tag, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/tags", owner, repo) | ||||||
|  |  | ||||||
|  | 	// convert Tag into a createTagRequest | ||||||
|  | 	tagRequest := &createTagRequest{ | ||||||
|  | 		Tag:     tag.Tag, | ||||||
|  | 		Message: tag.Message, | ||||||
|  | 		Tagger:  tag.Tagger, | ||||||
|  | 	} | ||||||
|  | 	if tag.Object != nil { | ||||||
|  | 		tagRequest.Object = tag.Object.SHA | ||||||
|  | 		tagRequest.Type = tag.Object.Type | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, tagRequest) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t := new(Tag) | ||||||
|  | 	resp, err := s.client.Do(req, t) | ||||||
|  | 	return t, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_tags_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_tags_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGitService_GetTag(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/tags/s", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"tag": "t"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	tag, _, err := client.Git.GetTag("o", "r", "s") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.GetTag returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Tag{Tag: String("t")} | ||||||
|  | 	if !reflect.DeepEqual(tag, want) { | ||||||
|  | 		t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateTag(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &createTagRequest{Tag: String("t"), Object: String("s")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/tags", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(createTagRequest) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"tag": "t"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	tag, _, err := client.Git.CreateTag("o", "r", &Tag{ | ||||||
|  | 		Tag:    input.Tag, | ||||||
|  | 		Object: &GitObject{SHA: input.Object}, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.CreateTag returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Tag{Tag: String("t")} | ||||||
|  | 	if !reflect.DeepEqual(tag, want) { | ||||||
|  | 		t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										89
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_trees.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_trees.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // Tree represents a GitHub tree. | ||||||
|  | type Tree struct { | ||||||
|  | 	SHA     *string     `json:"sha,omitempty"` | ||||||
|  | 	Entries []TreeEntry `json:"tree,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t Tree) String() string { | ||||||
|  | 	return Stringify(t) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TreeEntry represents the contents of a tree structure.  TreeEntry can | ||||||
|  | // represent either a blob, a commit (in the case of a submodule), or another | ||||||
|  | // tree. | ||||||
|  | type TreeEntry struct { | ||||||
|  | 	SHA     *string `json:"sha,omitempty"` | ||||||
|  | 	Path    *string `json:"path,omitempty"` | ||||||
|  | 	Mode    *string `json:"mode,omitempty"` | ||||||
|  | 	Type    *string `json:"type,omitempty"` | ||||||
|  | 	Size    *int    `json:"size,omitempty"` | ||||||
|  | 	Content *string `json:"content,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t TreeEntry) String() string { | ||||||
|  | 	return Stringify(t) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetTree fetches the Tree object for a given sha hash from a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/trees/#get-a-tree | ||||||
|  | func (s *GitService) GetTree(owner string, repo string, sha string, recursive bool) (*Tree, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/trees/%v", owner, repo, sha) | ||||||
|  | 	if recursive { | ||||||
|  | 		u += "?recursive=1" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t := new(Tree) | ||||||
|  | 	resp, err := s.client.Do(req, t) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return t, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createTree represents the body of a CreateTree request. | ||||||
|  | type createTree struct { | ||||||
|  | 	BaseTree string      `json:"base_tree,omitempty"` | ||||||
|  | 	Entries  []TreeEntry `json:"tree"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateTree creates a new tree in a repository.  If both a tree and a nested | ||||||
|  | // path modifying that tree are specified, it will overwrite the contents of | ||||||
|  | // that tree with the new path contents and write a new tree out. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/git/trees/#create-a-tree | ||||||
|  | func (s *GitService) CreateTree(owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo) | ||||||
|  |  | ||||||
|  | 	body := &createTree{ | ||||||
|  | 		BaseTree: baseTree, | ||||||
|  | 		Entries:  entries, | ||||||
|  | 	} | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t := new(Tree) | ||||||
|  | 	resp, err := s.client.Do(req, t) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return t, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										189
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_trees_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/git_trees_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGitService_GetTree(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/trees/s", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		if m := "GET"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, `{ | ||||||
|  | 			  "sha": "s", | ||||||
|  | 			  "tree": [ { "type": "blob" } ] | ||||||
|  | 			}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	tree, _, err := client.Git.GetTree("o", "r", "s", true) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.GetTree returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := Tree{ | ||||||
|  | 		SHA: String("s"), | ||||||
|  | 		Entries: []TreeEntry{ | ||||||
|  | 			{ | ||||||
|  | 				Type: String("blob"), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(*tree, want) { | ||||||
|  | 		t.Errorf("Tree.Get returned %+v, want %+v", *tree, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_GetTree_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Git.GetTree("%", "%", "%", false) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateTree(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := []TreeEntry{ | ||||||
|  | 		{ | ||||||
|  | 			Path: String("file.rb"), | ||||||
|  | 			Mode: String("100644"), | ||||||
|  | 			Type: String("blob"), | ||||||
|  | 			SHA:  String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(createTree) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		if m := "POST"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		want := &createTree{ | ||||||
|  | 			BaseTree: "b", | ||||||
|  | 			Entries:  input, | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(v, want) { | ||||||
|  | 			t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{ | ||||||
|  | 		  "sha": "cd8274d15fa3ae2ab983129fb037999f264ba9a7", | ||||||
|  | 		  "tree": [ | ||||||
|  | 		    { | ||||||
|  | 		      "path": "file.rb", | ||||||
|  | 		      "mode": "100644", | ||||||
|  | 		      "type": "blob", | ||||||
|  | 		      "size": 132, | ||||||
|  | 		      "sha": "7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b" | ||||||
|  | 		    } | ||||||
|  | 		  ] | ||||||
|  | 		}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	tree, _, err := client.Git.CreateTree("o", "r", "b", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.CreateTree returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := Tree{ | ||||||
|  | 		String("cd8274d15fa3ae2ab983129fb037999f264ba9a7"), | ||||||
|  | 		[]TreeEntry{ | ||||||
|  | 			{ | ||||||
|  | 				Path: String("file.rb"), | ||||||
|  | 				Mode: String("100644"), | ||||||
|  | 				Type: String("blob"), | ||||||
|  | 				Size: Int(132), | ||||||
|  | 				SHA:  String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(*tree, want) { | ||||||
|  | 		t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateTree_Content(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := []TreeEntry{ | ||||||
|  | 		{ | ||||||
|  | 			Path:    String("content.md"), | ||||||
|  | 			Mode:    String("100644"), | ||||||
|  | 			Content: String("file content"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(createTree) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		if m := "POST"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		want := &createTree{ | ||||||
|  | 			BaseTree: "b", | ||||||
|  | 			Entries:  input, | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(v, want) { | ||||||
|  | 			t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{ | ||||||
|  | 		  "sha": "5c6780ad2c68743383b740fd1dab6f6a33202b11", | ||||||
|  | 		  "url": "https://api.github.com/repos/o/r/git/trees/5c6780ad2c68743383b740fd1dab6f6a33202b11", | ||||||
|  | 		  "tree": [ | ||||||
|  | 		    { | ||||||
|  | 			  "mode": "100644", | ||||||
|  | 			  "type": "blob", | ||||||
|  | 			  "sha":  "aad8feacf6f8063150476a7b2bd9770f2794c08b", | ||||||
|  | 			  "path": "content.md", | ||||||
|  | 			  "size": 12, | ||||||
|  | 			  "url": "https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b" | ||||||
|  | 		    } | ||||||
|  | 		  ] | ||||||
|  | 		}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	tree, _, err := client.Git.CreateTree("o", "r", "b", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Git.CreateTree returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := Tree{ | ||||||
|  | 		String("5c6780ad2c68743383b740fd1dab6f6a33202b11"), | ||||||
|  | 		[]TreeEntry{ | ||||||
|  | 			{ | ||||||
|  | 				Path: String("content.md"), | ||||||
|  | 				Mode: String("100644"), | ||||||
|  | 				Type: String("blob"), | ||||||
|  | 				Size: Int(12), | ||||||
|  | 				SHA:  String("aad8feacf6f8063150476a7b2bd9770f2794c08b"), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(*tree, want) { | ||||||
|  | 		t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitService_CreateTree_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Git.CreateTree("%", "%", "", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										568
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/github.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										568
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/github.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,568 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/google/go-querystring/query" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	libraryVersion = "0.1" | ||||||
|  | 	defaultBaseURL = "https://api.github.com/" | ||||||
|  | 	uploadBaseURL  = "https://uploads.github.com/" | ||||||
|  | 	userAgent      = "go-github/" + libraryVersion | ||||||
|  |  | ||||||
|  | 	headerRateLimit     = "X-RateLimit-Limit" | ||||||
|  | 	headerRateRemaining = "X-RateLimit-Remaining" | ||||||
|  | 	headerRateReset     = "X-RateLimit-Reset" | ||||||
|  |  | ||||||
|  | 	mediaTypeV3      = "application/vnd.github.v3+json" | ||||||
|  | 	defaultMediaType = "application/octet-stream" | ||||||
|  |  | ||||||
|  | 	// Media Type values to access preview APIs | ||||||
|  |  | ||||||
|  | 	// https://developer.github.com/changes/2014-08-05-team-memberships-api/ | ||||||
|  | 	mediaTypeMembershipPreview = "application/vnd.github.the-wasp-preview+json" | ||||||
|  |  | ||||||
|  | 	// https://developer.github.com/changes/2014-01-09-preview-the-new-deployments-api/ | ||||||
|  | 	mediaTypeDeploymentPreview = "application/vnd.github.cannonball-preview+json" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // A Client manages communication with the GitHub API. | ||||||
|  | type Client struct { | ||||||
|  | 	// HTTP client used to communicate with the API. | ||||||
|  | 	client *http.Client | ||||||
|  |  | ||||||
|  | 	// Base URL for API requests.  Defaults to the public GitHub API, but can be | ||||||
|  | 	// set to a domain endpoint to use with GitHub Enterprise.  BaseURL should | ||||||
|  | 	// always be specified with a trailing slash. | ||||||
|  | 	BaseURL *url.URL | ||||||
|  |  | ||||||
|  | 	// Base URL for uploading files. | ||||||
|  | 	UploadURL *url.URL | ||||||
|  |  | ||||||
|  | 	// User agent used when communicating with the GitHub API. | ||||||
|  | 	UserAgent string | ||||||
|  |  | ||||||
|  | 	// Rate specifies the current rate limit for the client as determined by the | ||||||
|  | 	// most recent API call.  If the client is used in a multi-user application, | ||||||
|  | 	// this rate may not always be up-to-date.  Call RateLimit() to check the | ||||||
|  | 	// current rate. | ||||||
|  | 	Rate Rate | ||||||
|  |  | ||||||
|  | 	// Services used for talking to different parts of the GitHub API. | ||||||
|  | 	Activity      *ActivityService | ||||||
|  | 	Gists         *GistsService | ||||||
|  | 	Git           *GitService | ||||||
|  | 	Gitignores    *GitignoresService | ||||||
|  | 	Issues        *IssuesService | ||||||
|  | 	Organizations *OrganizationsService | ||||||
|  | 	PullRequests  *PullRequestsService | ||||||
|  | 	Repositories  *RepositoriesService | ||||||
|  | 	Search        *SearchService | ||||||
|  | 	Users         *UsersService | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListOptions specifies the optional parameters to various List methods that | ||||||
|  | // support pagination. | ||||||
|  | type ListOptions struct { | ||||||
|  | 	// For paginated result sets, page of results to retrieve. | ||||||
|  | 	Page int `url:"page,omitempty"` | ||||||
|  |  | ||||||
|  | 	// For paginated result sets, the number of results to include per page. | ||||||
|  | 	PerPage int `url:"per_page,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UploadOptions specifies the parameters to methods that support uploads. | ||||||
|  | type UploadOptions struct { | ||||||
|  | 	Name string `url:"name,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // addOptions adds the parameters in opt as URL query parameters to s.  opt | ||||||
|  | // must be a struct whose fields may contain "url" tags. | ||||||
|  | func addOptions(s string, opt interface{}) (string, error) { | ||||||
|  | 	v := reflect.ValueOf(opt) | ||||||
|  | 	if v.Kind() == reflect.Ptr && v.IsNil() { | ||||||
|  | 		return s, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u, err := url.Parse(s) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return s, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	qs, err := query.Values(opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return s, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u.RawQuery = qs.Encode() | ||||||
|  | 	return u.String(), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewClient returns a new GitHub API client.  If a nil httpClient is | ||||||
|  | // provided, http.DefaultClient will be used.  To use API methods which require | ||||||
|  | // authentication, provide an http.Client that will perform the authentication | ||||||
|  | // for you (such as that provided by the golang.org/x/oauth2 library). | ||||||
|  | func NewClient(httpClient *http.Client) *Client { | ||||||
|  | 	if httpClient == nil { | ||||||
|  | 		httpClient = http.DefaultClient | ||||||
|  | 	} | ||||||
|  | 	baseURL, _ := url.Parse(defaultBaseURL) | ||||||
|  | 	uploadURL, _ := url.Parse(uploadBaseURL) | ||||||
|  |  | ||||||
|  | 	c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL} | ||||||
|  | 	c.Activity = &ActivityService{client: c} | ||||||
|  | 	c.Gists = &GistsService{client: c} | ||||||
|  | 	c.Git = &GitService{client: c} | ||||||
|  | 	c.Gitignores = &GitignoresService{client: c} | ||||||
|  | 	c.Issues = &IssuesService{client: c} | ||||||
|  | 	c.Organizations = &OrganizationsService{client: c} | ||||||
|  | 	c.PullRequests = &PullRequestsService{client: c} | ||||||
|  | 	c.Repositories = &RepositoriesService{client: c} | ||||||
|  | 	c.Search = &SearchService{client: c} | ||||||
|  | 	c.Users = &UsersService{client: c} | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewRequest creates an API request. A relative URL can be provided in urlStr, | ||||||
|  | // in which case it is resolved relative to the BaseURL of the Client. | ||||||
|  | // Relative URLs should always be specified without a preceding slash.  If | ||||||
|  | // specified, the value pointed to by body is JSON encoded and included as the | ||||||
|  | // request body. | ||||||
|  | func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { | ||||||
|  | 	rel, err := url.Parse(urlStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u := c.BaseURL.ResolveReference(rel) | ||||||
|  |  | ||||||
|  | 	var buf io.ReadWriter | ||||||
|  | 	if body != nil { | ||||||
|  | 		buf = new(bytes.Buffer) | ||||||
|  | 		err := json.NewEncoder(buf).Encode(body) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := http.NewRequest(method, u.String(), buf) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req.Header.Add("Accept", mediaTypeV3) | ||||||
|  | 	if c.UserAgent != "" { | ||||||
|  | 		req.Header.Add("User-Agent", c.UserAgent) | ||||||
|  | 	} | ||||||
|  | 	return req, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewUploadRequest creates an upload request. A relative URL can be provided in | ||||||
|  | // urlStr, in which case it is resolved relative to the UploadURL of the Client. | ||||||
|  | // Relative URLs should always be specified without a preceding slash. | ||||||
|  | func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { | ||||||
|  | 	rel, err := url.Parse(urlStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u := c.UploadURL.ResolveReference(rel) | ||||||
|  | 	req, err := http.NewRequest("POST", u.String(), reader) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	req.ContentLength = size | ||||||
|  |  | ||||||
|  | 	if len(mediaType) == 0 { | ||||||
|  | 		mediaType = defaultMediaType | ||||||
|  | 	} | ||||||
|  | 	req.Header.Add("Content-Type", mediaType) | ||||||
|  | 	req.Header.Add("Accept", mediaTypeV3) | ||||||
|  | 	req.Header.Add("User-Agent", c.UserAgent) | ||||||
|  | 	return req, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Response is a GitHub API response.  This wraps the standard http.Response | ||||||
|  | // returned from GitHub and provides convenient access to things like | ||||||
|  | // pagination links. | ||||||
|  | type Response struct { | ||||||
|  | 	*http.Response | ||||||
|  |  | ||||||
|  | 	// These fields provide the page values for paginating through a set of | ||||||
|  | 	// results.  Any or all of these may be set to the zero value for | ||||||
|  | 	// responses that are not part of a paginated set, or for which there | ||||||
|  | 	// are no additional pages. | ||||||
|  |  | ||||||
|  | 	NextPage  int | ||||||
|  | 	PrevPage  int | ||||||
|  | 	FirstPage int | ||||||
|  | 	LastPage  int | ||||||
|  |  | ||||||
|  | 	Rate | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // newResponse creats a new Response for the provided http.Response. | ||||||
|  | func newResponse(r *http.Response) *Response { | ||||||
|  | 	response := &Response{Response: r} | ||||||
|  | 	response.populatePageValues() | ||||||
|  | 	response.populateRate() | ||||||
|  | 	return response | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // populatePageValues parses the HTTP Link response headers and populates the | ||||||
|  | // various pagination link values in the Reponse. | ||||||
|  | func (r *Response) populatePageValues() { | ||||||
|  | 	if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 { | ||||||
|  | 		for _, link := range strings.Split(links[0], ",") { | ||||||
|  | 			segments := strings.Split(strings.TrimSpace(link), ";") | ||||||
|  |  | ||||||
|  | 			// link must at least have href and rel | ||||||
|  | 			if len(segments) < 2 { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// ensure href is properly formatted | ||||||
|  | 			if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// try to pull out page parameter | ||||||
|  | 			url, err := url.Parse(segments[0][1 : len(segments[0])-1]) | ||||||
|  | 			if err != nil { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			page := url.Query().Get("page") | ||||||
|  | 			if page == "" { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			for _, segment := range segments[1:] { | ||||||
|  | 				switch strings.TrimSpace(segment) { | ||||||
|  | 				case `rel="next"`: | ||||||
|  | 					r.NextPage, _ = strconv.Atoi(page) | ||||||
|  | 				case `rel="prev"`: | ||||||
|  | 					r.PrevPage, _ = strconv.Atoi(page) | ||||||
|  | 				case `rel="first"`: | ||||||
|  | 					r.FirstPage, _ = strconv.Atoi(page) | ||||||
|  | 				case `rel="last"`: | ||||||
|  | 					r.LastPage, _ = strconv.Atoi(page) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // populateRate parses the rate related headers and populates the response Rate. | ||||||
|  | func (r *Response) populateRate() { | ||||||
|  | 	if limit := r.Header.Get(headerRateLimit); limit != "" { | ||||||
|  | 		r.Rate.Limit, _ = strconv.Atoi(limit) | ||||||
|  | 	} | ||||||
|  | 	if remaining := r.Header.Get(headerRateRemaining); remaining != "" { | ||||||
|  | 		r.Rate.Remaining, _ = strconv.Atoi(remaining) | ||||||
|  | 	} | ||||||
|  | 	if reset := r.Header.Get(headerRateReset); reset != "" { | ||||||
|  | 		if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 { | ||||||
|  | 			r.Rate.Reset = Timestamp{time.Unix(v, 0)} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Do sends an API request and returns the API response.  The API response is | ||||||
|  | // JSON decoded and stored in the value pointed to by v, or returned as an | ||||||
|  | // error if an API error has occurred.  If v implements the io.Writer | ||||||
|  | // interface, the raw response body will be written to v, without attempting to | ||||||
|  | // first decode it. | ||||||
|  | func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { | ||||||
|  | 	resp, err := c.client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
|  | 	response := newResponse(resp) | ||||||
|  |  | ||||||
|  | 	c.Rate = response.Rate | ||||||
|  |  | ||||||
|  | 	err = CheckResponse(resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		// even though there was an error, we still return the response | ||||||
|  | 		// in case the caller wants to inspect it further | ||||||
|  | 		return response, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if v != nil { | ||||||
|  | 		if w, ok := v.(io.Writer); ok { | ||||||
|  | 			io.Copy(w, resp.Body) | ||||||
|  | 		} else { | ||||||
|  | 			err = json.NewDecoder(resp.Body).Decode(v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return response, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | An ErrorResponse reports one or more errors caused by an API request. | ||||||
|  |  | ||||||
|  | GitHub API docs: http://developer.github.com/v3/#client-errors | ||||||
|  | */ | ||||||
|  | type ErrorResponse struct { | ||||||
|  | 	Response *http.Response // HTTP response that caused this error | ||||||
|  | 	Message  string         `json:"message"` // error message | ||||||
|  | 	Errors   []Error        `json:"errors"`  // more detail on individual errors | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *ErrorResponse) Error() string { | ||||||
|  | 	return fmt.Sprintf("%v %v: %d %v %+v", | ||||||
|  | 		r.Response.Request.Method, r.Response.Request.URL, | ||||||
|  | 		r.Response.StatusCode, r.Message, r.Errors) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | An Error reports more details on an individual error in an ErrorResponse. | ||||||
|  | These are the possible validation error codes: | ||||||
|  |  | ||||||
|  |     missing: | ||||||
|  |         resource does not exist | ||||||
|  |     missing_field: | ||||||
|  |         a required field on a resource has not been set | ||||||
|  |     invalid: | ||||||
|  |         the formatting of a field is invalid | ||||||
|  |     already_exists: | ||||||
|  |         another resource has the same valid as this field | ||||||
|  |  | ||||||
|  | GitHub API docs: http://developer.github.com/v3/#client-errors | ||||||
|  | */ | ||||||
|  | type Error struct { | ||||||
|  | 	Resource string `json:"resource"` // resource on which the error occurred | ||||||
|  | 	Field    string `json:"field"`    // field on which the error occurred | ||||||
|  | 	Code     string `json:"code"`     // validation error code | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Error) Error() string { | ||||||
|  | 	return fmt.Sprintf("%v error caused by %v field on %v resource", | ||||||
|  | 		e.Code, e.Field, e.Resource) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CheckResponse checks the API response for errors, and returns them if | ||||||
|  | // present.  A response is considered an error if it has a status code outside | ||||||
|  | // the 200 range.  API error responses are expected to have either no response | ||||||
|  | // body, or a JSON response body that maps to ErrorResponse.  Any other | ||||||
|  | // response body will be silently ignored. | ||||||
|  | func CheckResponse(r *http.Response) error { | ||||||
|  | 	if c := r.StatusCode; 200 <= c && c <= 299 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	errorResponse := &ErrorResponse{Response: r} | ||||||
|  | 	data, err := ioutil.ReadAll(r.Body) | ||||||
|  | 	if err == nil && data != nil { | ||||||
|  | 		json.Unmarshal(data, errorResponse) | ||||||
|  | 	} | ||||||
|  | 	return errorResponse | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // parseBoolResponse determines the boolean result from a GitHub API response. | ||||||
|  | // Several GitHub API methods return boolean responses indicated by the HTTP | ||||||
|  | // status code in the response (true indicated by a 204, false indicated by a | ||||||
|  | // 404).  This helper function will determine that result and hide the 404 | ||||||
|  | // error if present.  Any other error will be returned through as-is. | ||||||
|  | func parseBoolResponse(err error) (bool, error) { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return true, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err, ok := err.(*ErrorResponse); ok && err.Response.StatusCode == http.StatusNotFound { | ||||||
|  | 		// Simply false.  In this one case, we do not pass the error through. | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// some other real error occurred | ||||||
|  | 	return false, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Rate represents the rate limit for the current client. | ||||||
|  | type Rate struct { | ||||||
|  | 	// The number of requests per hour the client is currently limited to. | ||||||
|  | 	Limit int `json:"limit"` | ||||||
|  |  | ||||||
|  | 	// The number of remaining requests the client can make this hour. | ||||||
|  | 	Remaining int `json:"remaining"` | ||||||
|  |  | ||||||
|  | 	// The time at which the current rate limit will reset. | ||||||
|  | 	Reset Timestamp `json:"reset"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r Rate) String() string { | ||||||
|  | 	return Stringify(r) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RateLimits represents the rate limits for the current client. | ||||||
|  | type RateLimits struct { | ||||||
|  | 	// The rate limit for non-search API requests.  Unauthenticated | ||||||
|  | 	// requests are limited to 60 per hour.  Authenticated requests are | ||||||
|  | 	// limited to 5,000 per hour. | ||||||
|  | 	Core *Rate `json:"core"` | ||||||
|  |  | ||||||
|  | 	// The rate limit for search API requests.  Unauthenticated requests | ||||||
|  | 	// are limited to 5 requests per minutes.  Authenticated requests are | ||||||
|  | 	// limited to 20 per minute. | ||||||
|  | 	// | ||||||
|  | 	// GitHub API docs: https://developer.github.com/v3/search/#rate-limit | ||||||
|  | 	Search *Rate `json:"search"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r RateLimits) String() string { | ||||||
|  | 	return Stringify(r) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RateLimit is deprecated.  Use RateLimits instead. | ||||||
|  | func (c *Client) RateLimit() (*Rate, *Response, error) { | ||||||
|  | 	limits, resp, err := c.RateLimits() | ||||||
|  | 	if limits == nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return limits.Core, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RateLimits returns the rate limits for the current client. | ||||||
|  | func (c *Client) RateLimits() (*RateLimits, *Response, error) { | ||||||
|  | 	req, err := c.NewRequest("GET", "rate_limit", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	response := new(struct { | ||||||
|  | 		Resources *RateLimits `json:"resources"` | ||||||
|  | 	}) | ||||||
|  | 	resp, err := c.Do(req, response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return response.Resources, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | UnauthenticatedRateLimitedTransport allows you to make unauthenticated calls | ||||||
|  | that need to use a higher rate limit associated with your OAuth application. | ||||||
|  |  | ||||||
|  | 	t := &github.UnauthenticatedRateLimitedTransport{ | ||||||
|  | 		ClientID:     "your app's client ID", | ||||||
|  | 		ClientSecret: "your app's client secret", | ||||||
|  | 	} | ||||||
|  | 	client := github.NewClient(t.Client()) | ||||||
|  |  | ||||||
|  | This will append the querystring params client_id=xxx&client_secret=yyy to all | ||||||
|  | requests. | ||||||
|  |  | ||||||
|  | See http://developer.github.com/v3/#unauthenticated-rate-limited-requests for | ||||||
|  | more information. | ||||||
|  | */ | ||||||
|  | type UnauthenticatedRateLimitedTransport struct { | ||||||
|  | 	// ClientID is the GitHub OAuth client ID of the current application, which | ||||||
|  | 	// can be found by selecting its entry in the list at | ||||||
|  | 	// https://github.com/settings/applications. | ||||||
|  | 	ClientID string | ||||||
|  |  | ||||||
|  | 	// ClientSecret is the GitHub OAuth client secret of the current | ||||||
|  | 	// application. | ||||||
|  | 	ClientSecret string | ||||||
|  |  | ||||||
|  | 	// Transport is the underlying HTTP transport to use when making requests. | ||||||
|  | 	// It will default to http.DefaultTransport if nil. | ||||||
|  | 	Transport http.RoundTripper | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RoundTrip implements the RoundTripper interface. | ||||||
|  | func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||||||
|  | 	if t.ClientID == "" { | ||||||
|  | 		return nil, errors.New("t.ClientID is empty") | ||||||
|  | 	} | ||||||
|  | 	if t.ClientSecret == "" { | ||||||
|  | 		return nil, errors.New("t.ClientSecret is empty") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// To set extra querystring params, we must make a copy of the Request so | ||||||
|  | 	// that we don't modify the Request we were given. This is required by the | ||||||
|  | 	// specification of http.RoundTripper. | ||||||
|  | 	req = cloneRequest(req) | ||||||
|  | 	q := req.URL.Query() | ||||||
|  | 	q.Set("client_id", t.ClientID) | ||||||
|  | 	q.Set("client_secret", t.ClientSecret) | ||||||
|  | 	req.URL.RawQuery = q.Encode() | ||||||
|  |  | ||||||
|  | 	// Make the HTTP request. | ||||||
|  | 	return t.transport().RoundTrip(req) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Client returns an *http.Client that makes requests which are subject to the | ||||||
|  | // rate limit of your OAuth application. | ||||||
|  | func (t *UnauthenticatedRateLimitedTransport) Client() *http.Client { | ||||||
|  | 	return &http.Client{Transport: t} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper { | ||||||
|  | 	if t.Transport != nil { | ||||||
|  | 		return t.Transport | ||||||
|  | 	} | ||||||
|  | 	return http.DefaultTransport | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // cloneRequest returns a clone of the provided *http.Request. The clone is a | ||||||
|  | // shallow copy of the struct and its Header map. | ||||||
|  | func cloneRequest(r *http.Request) *http.Request { | ||||||
|  | 	// shallow copy of the struct | ||||||
|  | 	r2 := new(http.Request) | ||||||
|  | 	*r2 = *r | ||||||
|  | 	// deep copy of the Header | ||||||
|  | 	r2.Header = make(http.Header) | ||||||
|  | 	for k, s := range r.Header { | ||||||
|  | 		r2.Header[k] = s | ||||||
|  | 	} | ||||||
|  | 	return r2 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Bool is a helper routine that allocates a new bool value | ||||||
|  | // to store v and returns a pointer to it. | ||||||
|  | func Bool(v bool) *bool { | ||||||
|  | 	p := new(bool) | ||||||
|  | 	*p = v | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Int is a helper routine that allocates a new int32 value | ||||||
|  | // to store v and returns a pointer to it, but unlike Int32 | ||||||
|  | // its argument value is an int. | ||||||
|  | func Int(v int) *int { | ||||||
|  | 	p := new(int) | ||||||
|  | 	*p = v | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // String is a helper routine that allocates a new string value | ||||||
|  | // to store v and returns a pointer to it. | ||||||
|  | func String(v string) *string { | ||||||
|  | 	p := new(string) | ||||||
|  | 	*p = v | ||||||
|  | 	return p | ||||||
|  | } | ||||||
							
								
								
									
										660
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/github_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										660
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/github_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,660 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"net/url" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// mux is the HTTP request multiplexer used with the test server. | ||||||
|  | 	mux *http.ServeMux | ||||||
|  |  | ||||||
|  | 	// client is the GitHub client being tested. | ||||||
|  | 	client *Client | ||||||
|  |  | ||||||
|  | 	// server is a test HTTP server used to provide mock API responses. | ||||||
|  | 	server *httptest.Server | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // setup sets up a test HTTP server along with a github.Client that is | ||||||
|  | // configured to talk to that test server.  Tests should register handlers on | ||||||
|  | // mux which provide mock responses for the API method being tested. | ||||||
|  | func setup() { | ||||||
|  | 	// test server | ||||||
|  | 	mux = http.NewServeMux() | ||||||
|  | 	server = httptest.NewServer(mux) | ||||||
|  |  | ||||||
|  | 	// github client configured to use test server | ||||||
|  | 	client = NewClient(nil) | ||||||
|  | 	url, _ := url.Parse(server.URL) | ||||||
|  | 	client.BaseURL = url | ||||||
|  | 	client.UploadURL = url | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // teardown closes the test HTTP server. | ||||||
|  | func teardown() { | ||||||
|  | 	server.Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // openTestFile creates a new file with the given name and content for testing. | ||||||
|  | // In order to ensure the exact file name, this function will create a new temp | ||||||
|  | // directory, and create the file in that directory.  It is the caller's | ||||||
|  | // responsibility to remove the directy and its contents when no longer needed. | ||||||
|  | func openTestFile(name, content string) (file *os.File, dir string, err error) { | ||||||
|  | 	dir, err = ioutil.TempDir("", "go-github") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, dir, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	file, err = os.OpenFile(path.Join(dir, name), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, dir, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fmt.Fprint(file, content) | ||||||
|  |  | ||||||
|  | 	// close and re-open the file to keep file.Stat() happy | ||||||
|  | 	file.Close() | ||||||
|  | 	file, err = os.Open(file.Name()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, dir, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return file, dir, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func testMethod(t *testing.T, r *http.Request, want string) { | ||||||
|  | 	if got := r.Method; got != want { | ||||||
|  | 		t.Errorf("Request method: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type values map[string]string | ||||||
|  |  | ||||||
|  | func testFormValues(t *testing.T, r *http.Request, values values) { | ||||||
|  | 	want := url.Values{} | ||||||
|  | 	for k, v := range values { | ||||||
|  | 		want.Add(k, v) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r.ParseForm() | ||||||
|  | 	if got := r.Form; !reflect.DeepEqual(got, want) { | ||||||
|  | 		t.Errorf("Request parameters: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func testHeader(t *testing.T, r *http.Request, header string, want string) { | ||||||
|  | 	if got := r.Header.Get(header); got != want { | ||||||
|  | 		t.Errorf("Header.Get(%q) returned %s, want %s", header, got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func testURLParseError(t *testing.T, err error) { | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected error to be returned") | ||||||
|  | 	} | ||||||
|  | 	if err, ok := err.(*url.Error); !ok || err.Op != "parse" { | ||||||
|  | 		t.Errorf("Expected URL parse error, got %+v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func testBody(t *testing.T, r *http.Request, want string) { | ||||||
|  | 	b, err := ioutil.ReadAll(r.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Error reading request body: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if got := string(b); got != want { | ||||||
|  | 		t.Errorf("request Body is %s, want %s", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Helper function to test that a value is marshalled to JSON as expected. | ||||||
|  | func testJSONMarshal(t *testing.T, v interface{}, want string) { | ||||||
|  | 	j, err := json.Marshal(v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Unable to marshal JSON for %v", v) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	w := new(bytes.Buffer) | ||||||
|  | 	err = json.Compact(w, []byte(want)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("String is not valid json: %s", want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if w.String() != string(j) { | ||||||
|  | 		t.Errorf("json.Marshal(%q) returned %s, want %s", v, j, w) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// now go the other direction and make sure things unmarshal as expected | ||||||
|  | 	u := reflect.ValueOf(v).Interface() | ||||||
|  | 	if err := json.Unmarshal([]byte(want), u); err != nil { | ||||||
|  | 		t.Errorf("Unable to unmarshal JSON for %v", want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(v, u) { | ||||||
|  | 		t.Errorf("json.Unmarshal(%q) returned %s, want %s", want, u, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewClient(t *testing.T) { | ||||||
|  | 	c := NewClient(nil) | ||||||
|  |  | ||||||
|  | 	if got, want := c.BaseURL.String(), defaultBaseURL; got != want { | ||||||
|  | 		t.Errorf("NewClient BaseURL is %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := c.UserAgent, userAgent; got != want { | ||||||
|  | 		t.Errorf("NewClient UserAgent is %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewRequest(t *testing.T) { | ||||||
|  | 	c := NewClient(nil) | ||||||
|  |  | ||||||
|  | 	inURL, outURL := "/foo", defaultBaseURL+"foo" | ||||||
|  | 	inBody, outBody := &User{Login: String("l")}, `{"login":"l"}`+"\n" | ||||||
|  | 	req, _ := c.NewRequest("GET", inURL, inBody) | ||||||
|  |  | ||||||
|  | 	// test that relative URL was expanded | ||||||
|  | 	if got, want := req.URL.String(), outURL; got != want { | ||||||
|  | 		t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// test that body was JSON encoded | ||||||
|  | 	body, _ := ioutil.ReadAll(req.Body) | ||||||
|  | 	if got, want := string(body), outBody; got != want { | ||||||
|  | 		t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// test that default user-agent is attached to the request | ||||||
|  | 	if got, want := req.Header.Get("User-Agent"), c.UserAgent; got != want { | ||||||
|  | 		t.Errorf("NewRequest() User-Agent is %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewRequest_invalidJSON(t *testing.T) { | ||||||
|  | 	c := NewClient(nil) | ||||||
|  |  | ||||||
|  | 	type T struct { | ||||||
|  | 		A map[int]interface{} | ||||||
|  | 	} | ||||||
|  | 	_, err := c.NewRequest("GET", "/", &T{}) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Error("Expected error to be returned.") | ||||||
|  | 	} | ||||||
|  | 	if err, ok := err.(*json.UnsupportedTypeError); !ok { | ||||||
|  | 		t.Errorf("Expected a JSON error; got %#v.", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewRequest_badURL(t *testing.T) { | ||||||
|  | 	c := NewClient(nil) | ||||||
|  | 	_, err := c.NewRequest("GET", ":", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensure that no User-Agent header is set if the client's UserAgent is empty. | ||||||
|  | // This caused a problem with Google's internal http client. | ||||||
|  | func TestNewRequest_emptyUserAgent(t *testing.T) { | ||||||
|  | 	c := NewClient(nil) | ||||||
|  | 	c.UserAgent = "" | ||||||
|  | 	req, err := c.NewRequest("GET", "/", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("NewRequest returned unexpected error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if _, ok := req.Header["User-Agent"]; ok { | ||||||
|  | 		t.Fatal("constructed request contains unexpected User-Agent header") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // If a nil body is passed to github.NewRequest, make sure that nil is also | ||||||
|  | // passed to http.NewRequest.  In most cases, passing an io.Reader that returns | ||||||
|  | // no content is fine, since there is no difference between an HTTP request | ||||||
|  | // body that is an empty string versus one that is not set at all.  However in | ||||||
|  | // certain cases, intermediate systems may treat these differently resulting in | ||||||
|  | // subtle errors. | ||||||
|  | func TestNewRequest_emptyBody(t *testing.T) { | ||||||
|  | 	c := NewClient(nil) | ||||||
|  | 	req, err := c.NewRequest("GET", "/", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("NewRequest returned unexpected error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if req.Body != nil { | ||||||
|  | 		t.Fatalf("constructed request contains a non-nil Body") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestResponse_populatePageValues(t *testing.T) { | ||||||
|  | 	r := http.Response{ | ||||||
|  | 		Header: http.Header{ | ||||||
|  | 			"Link": {`<https://api.github.com/?page=1>; rel="first",` + | ||||||
|  | 				` <https://api.github.com/?page=2>; rel="prev",` + | ||||||
|  | 				` <https://api.github.com/?page=4>; rel="next",` + | ||||||
|  | 				` <https://api.github.com/?page=5>; rel="last"`, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	response := newResponse(&r) | ||||||
|  | 	if got, want := response.FirstPage, 1; got != want { | ||||||
|  | 		t.Errorf("response.FirstPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := response.PrevPage, 2; want != got { | ||||||
|  | 		t.Errorf("response.PrevPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := response.NextPage, 4; want != got { | ||||||
|  | 		t.Errorf("response.NextPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := response.LastPage, 5; want != got { | ||||||
|  | 		t.Errorf("response.LastPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestResponse_populatePageValues_invalid(t *testing.T) { | ||||||
|  | 	r := http.Response{ | ||||||
|  | 		Header: http.Header{ | ||||||
|  | 			"Link": {`<https://api.github.com/?page=1>,` + | ||||||
|  | 				`<https://api.github.com/?page=abc>; rel="first",` + | ||||||
|  | 				`https://api.github.com/?page=2; rel="prev",` + | ||||||
|  | 				`<https://api.github.com/>; rel="next",` + | ||||||
|  | 				`<https://api.github.com/?page=>; rel="last"`, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	response := newResponse(&r) | ||||||
|  | 	if got, want := response.FirstPage, 0; got != want { | ||||||
|  | 		t.Errorf("response.FirstPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := response.PrevPage, 0; got != want { | ||||||
|  | 		t.Errorf("response.PrevPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := response.NextPage, 0; got != want { | ||||||
|  | 		t.Errorf("response.NextPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := response.LastPage, 0; got != want { | ||||||
|  | 		t.Errorf("response.LastPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// more invalid URLs | ||||||
|  | 	r = http.Response{ | ||||||
|  | 		Header: http.Header{ | ||||||
|  | 			"Link": {`<https://api.github.com/%?page=2>; rel="first"`}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	response = newResponse(&r) | ||||||
|  | 	if got, want := response.FirstPage, 0; got != want { | ||||||
|  | 		t.Errorf("response.FirstPage: %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDo(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	type foo struct { | ||||||
|  | 		A string | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		if m := "GET"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, `{"A":"a"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	req, _ := client.NewRequest("GET", "/", nil) | ||||||
|  | 	body := new(foo) | ||||||
|  | 	client.Do(req, body) | ||||||
|  |  | ||||||
|  | 	want := &foo{"a"} | ||||||
|  | 	if !reflect.DeepEqual(body, want) { | ||||||
|  | 		t.Errorf("Response body = %v, want %v", body, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDo_httpError(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		http.Error(w, "Bad Request", 400) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	req, _ := client.NewRequest("GET", "/", nil) | ||||||
|  | 	_, err := client.Do(req, nil) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Error("Expected HTTP 400 error.") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Test handling of an error caused by the internal http client's Do() | ||||||
|  | // function.  A redirect loop is pretty unlikely to occur within the GitHub | ||||||
|  | // API, but does allow us to exercise the right code path. | ||||||
|  | func TestDo_redirectLoop(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		http.Redirect(w, r, "/", http.StatusFound) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	req, _ := client.NewRequest("GET", "/", nil) | ||||||
|  | 	_, err := client.Do(req, nil) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Error("Expected error to be returned.") | ||||||
|  | 	} | ||||||
|  | 	if err, ok := err.(*url.Error); !ok { | ||||||
|  | 		t.Errorf("Expected a URL error; got %#v.", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDo_rateLimit(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Add(headerRateLimit, "60") | ||||||
|  | 		w.Header().Add(headerRateRemaining, "59") | ||||||
|  | 		w.Header().Add(headerRateReset, "1372700873") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	if got, want := client.Rate.Limit, 0; got != want { | ||||||
|  | 		t.Errorf("Client rate limit = %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := client.Rate.Limit, 0; got != want { | ||||||
|  | 		t.Errorf("Client rate remaining = %v, got %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if !client.Rate.Reset.IsZero() { | ||||||
|  | 		t.Errorf("Client rate reset not initialized to zero value") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, _ := client.NewRequest("GET", "/", nil) | ||||||
|  | 	client.Do(req, nil) | ||||||
|  |  | ||||||
|  | 	if got, want := client.Rate.Limit, 60; got != want { | ||||||
|  | 		t.Errorf("Client rate limit = %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := client.Rate.Remaining, 59; got != want { | ||||||
|  | 		t.Errorf("Client rate remaining = %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) | ||||||
|  | 	if client.Rate.Reset.UTC() != reset { | ||||||
|  | 		t.Errorf("Client rate reset = %v, want %v", client.Rate.Reset, reset) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensure rate limit is still parsed, even for error responses | ||||||
|  | func TestDo_rateLimit_errorResponse(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Add(headerRateLimit, "60") | ||||||
|  | 		w.Header().Add(headerRateRemaining, "59") | ||||||
|  | 		w.Header().Add(headerRateReset, "1372700873") | ||||||
|  | 		http.Error(w, "Bad Request", 400) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	req, _ := client.NewRequest("GET", "/", nil) | ||||||
|  | 	client.Do(req, nil) | ||||||
|  |  | ||||||
|  | 	if got, want := client.Rate.Limit, 60; got != want { | ||||||
|  | 		t.Errorf("Client rate limit = %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	if got, want := client.Rate.Remaining, 59; got != want { | ||||||
|  | 		t.Errorf("Client rate remaining = %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | 	reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) | ||||||
|  | 	if client.Rate.Reset.UTC() != reset { | ||||||
|  | 		t.Errorf("Client rate reset = %v, want %v", client.Rate.Reset, reset) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestCheckResponse(t *testing.T) { | ||||||
|  | 	res := &http.Response{ | ||||||
|  | 		Request:    &http.Request{}, | ||||||
|  | 		StatusCode: http.StatusBadRequest, | ||||||
|  | 		Body: ioutil.NopCloser(strings.NewReader(`{"message":"m",  | ||||||
|  | 			"errors": [{"resource": "r", "field": "f", "code": "c"}]}`)), | ||||||
|  | 	} | ||||||
|  | 	err := CheckResponse(res).(*ErrorResponse) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected error response.") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &ErrorResponse{ | ||||||
|  | 		Response: res, | ||||||
|  | 		Message:  "m", | ||||||
|  | 		Errors:   []Error{{Resource: "r", Field: "f", Code: "c"}}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(err, want) { | ||||||
|  | 		t.Errorf("Error = %#v, want %#v", err, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensure that we properly handle API errors that do not contain a response body | ||||||
|  | func TestCheckResponse_noBody(t *testing.T) { | ||||||
|  | 	res := &http.Response{ | ||||||
|  | 		Request:    &http.Request{}, | ||||||
|  | 		StatusCode: http.StatusBadRequest, | ||||||
|  | 		Body:       ioutil.NopCloser(strings.NewReader("")), | ||||||
|  | 	} | ||||||
|  | 	err := CheckResponse(res).(*ErrorResponse) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected error response.") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &ErrorResponse{ | ||||||
|  | 		Response: res, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(err, want) { | ||||||
|  | 		t.Errorf("Error = %#v, want %#v", err, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestParseBooleanResponse_true(t *testing.T) { | ||||||
|  | 	result, err := parseBoolResponse(nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("parseBoolResponse returned error: %+v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if want := true; result != want { | ||||||
|  | 		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestParseBooleanResponse_false(t *testing.T) { | ||||||
|  | 	v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusNotFound}} | ||||||
|  | 	result, err := parseBoolResponse(v) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("parseBoolResponse returned error: %+v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if want := false; result != want { | ||||||
|  | 		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestParseBooleanResponse_error(t *testing.T) { | ||||||
|  | 	v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusBadRequest}} | ||||||
|  | 	result, err := parseBoolResponse(v) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected error to be returned.") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if want := false; result != want { | ||||||
|  | 		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestErrorResponse_Error(t *testing.T) { | ||||||
|  | 	res := &http.Response{Request: &http.Request{}} | ||||||
|  | 	err := ErrorResponse{Message: "m", Response: res} | ||||||
|  | 	if err.Error() == "" { | ||||||
|  | 		t.Errorf("Expected non-empty ErrorResponse.Error()") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestError_Error(t *testing.T) { | ||||||
|  | 	err := Error{} | ||||||
|  | 	if err.Error() == "" { | ||||||
|  | 		t.Errorf("Expected non-empty Error.Error()") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestRateLimit(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		if m := "GET"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  | 		//fmt.Fprint(w, `{"resources":{"core": {"limit":2,"remaining":1,"reset":1372700873}}}`) | ||||||
|  | 		fmt.Fprint(w, `{"resources":{ | ||||||
|  | 			"core": {"limit":2,"remaining":1,"reset":1372700873}, | ||||||
|  | 			"search": {"limit":3,"remaining":2,"reset":1372700874} | ||||||
|  | 		}}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	rate, _, err := client.RateLimit() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Rate limit returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Rate{ | ||||||
|  | 		Limit:     2, | ||||||
|  | 		Remaining: 1, | ||||||
|  | 		Reset:     Timestamp{time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local()}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(rate, want) { | ||||||
|  | 		t.Errorf("RateLimit returned %+v, want %+v", rate, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestRateLimits(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		if m := "GET"; m != r.Method { | ||||||
|  | 			t.Errorf("Request method = %v, want %v", r.Method, m) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, `{"resources":{ | ||||||
|  | 			"core": {"limit":2,"remaining":1,"reset":1372700873}, | ||||||
|  | 			"search": {"limit":3,"remaining":2,"reset":1372700874} | ||||||
|  | 		}}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	rate, _, err := client.RateLimits() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("RateLimits returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &RateLimits{ | ||||||
|  | 		Core: &Rate{ | ||||||
|  | 			Limit:     2, | ||||||
|  | 			Remaining: 1, | ||||||
|  | 			Reset:     Timestamp{time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local()}, | ||||||
|  | 		}, | ||||||
|  | 		Search: &Rate{ | ||||||
|  | 			Limit:     3, | ||||||
|  | 			Remaining: 2, | ||||||
|  | 			Reset:     Timestamp{time.Date(2013, 7, 1, 17, 47, 54, 0, time.UTC).Local()}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(rate, want) { | ||||||
|  | 		t.Errorf("RateLimits returned %+v, want %+v", rate, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUnauthenticatedRateLimitedTransport(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		var v, want string | ||||||
|  | 		q := r.URL.Query() | ||||||
|  | 		if v, want = q.Get("client_id"), "id"; v != want { | ||||||
|  | 			t.Errorf("OAuth Client ID = %v, want %v", v, want) | ||||||
|  | 		} | ||||||
|  | 		if v, want = q.Get("client_secret"), "secret"; v != want { | ||||||
|  | 			t.Errorf("OAuth Client Secret = %v, want %v", v, want) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	tp := &UnauthenticatedRateLimitedTransport{ | ||||||
|  | 		ClientID:     "id", | ||||||
|  | 		ClientSecret: "secret", | ||||||
|  | 	} | ||||||
|  | 	unauthedClient := NewClient(tp.Client()) | ||||||
|  | 	unauthedClient.BaseURL = client.BaseURL | ||||||
|  | 	req, _ := unauthedClient.NewRequest("GET", "/", nil) | ||||||
|  | 	unauthedClient.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUnauthenticatedRateLimitedTransport_missingFields(t *testing.T) { | ||||||
|  | 	// missing ClientID | ||||||
|  | 	tp := &UnauthenticatedRateLimitedTransport{ | ||||||
|  | 		ClientSecret: "secret", | ||||||
|  | 	} | ||||||
|  | 	_, err := tp.RoundTrip(nil) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected error to be returned") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// missing ClientSecret | ||||||
|  | 	tp = &UnauthenticatedRateLimitedTransport{ | ||||||
|  | 		ClientID: "id", | ||||||
|  | 	} | ||||||
|  | 	_, err = tp.RoundTrip(nil) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected error to be returned") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUnauthenticatedRateLimitedTransport_transport(t *testing.T) { | ||||||
|  | 	// default transport | ||||||
|  | 	tp := &UnauthenticatedRateLimitedTransport{ | ||||||
|  | 		ClientID:     "id", | ||||||
|  | 		ClientSecret: "secret", | ||||||
|  | 	} | ||||||
|  | 	if tp.transport() != http.DefaultTransport { | ||||||
|  | 		t.Errorf("Expected http.DefaultTransport to be used.") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// custom transport | ||||||
|  | 	tp = &UnauthenticatedRateLimitedTransport{ | ||||||
|  | 		ClientID:     "id", | ||||||
|  | 		ClientSecret: "secret", | ||||||
|  | 		Transport:    &http.Transport{}, | ||||||
|  | 	} | ||||||
|  | 	if tp.transport() == http.DefaultTransport { | ||||||
|  | 		t.Errorf("Expected custom transport to be used.") | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gitignore.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gitignore.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // GitignoresService provides access to the gitignore related functions in the | ||||||
|  | // GitHub API. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/gitignore/ | ||||||
|  | type GitignoresService struct { | ||||||
|  | 	client *Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Gitignore represents a .gitignore file as returned by the GitHub API. | ||||||
|  | type Gitignore struct { | ||||||
|  | 	Name   *string `json:"name,omitempty"` | ||||||
|  | 	Source *string `json:"source,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g Gitignore) String() string { | ||||||
|  | 	return Stringify(g) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List all available Gitignore templates. | ||||||
|  | // | ||||||
|  | // http://developer.github.com/v3/gitignore/#listing-available-templates | ||||||
|  | func (s GitignoresService) List() ([]string, *Response, error) { | ||||||
|  | 	req, err := s.client.NewRequest("GET", "gitignore/templates", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	availableTemplates := new([]string) | ||||||
|  | 	resp, err := s.client.Do(req, availableTemplates) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *availableTemplates, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get a Gitignore by name. | ||||||
|  | // | ||||||
|  | // http://developer.github.com/v3/gitignore/#get-a-single-template | ||||||
|  | func (s GitignoresService) Get(name string) (*Gitignore, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("gitignore/templates/%v", name) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gitignore := new(Gitignore) | ||||||
|  | 	resp, err := s.client.Do(req, gitignore) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return gitignore, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gitignore_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/gitignore_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGitignoresService_List(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gitignore/templates", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `["C", "Go"]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	available, _, err := client.Gitignores.List() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gitignores.List returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []string{"C", "Go"} | ||||||
|  | 	if !reflect.DeepEqual(available, want) { | ||||||
|  | 		t.Errorf("Gitignores.List returned %+v, want %+v", available, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitignoresService_Get(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/gitignore/templates/name", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"name":"Name","source":"template source"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	gitignore, _, err := client.Gitignores.Get("name") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Gitignores.List returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Gitignore{Name: String("Name"), Source: String("template source")} | ||||||
|  | 	if !reflect.DeepEqual(gitignore, want) { | ||||||
|  | 		t.Errorf("Gitignores.Get returned %+v, want %+v", gitignore, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGitignoresService_Get_invalidTemplate(t *testing.T) { | ||||||
|  | 	_, _, err := client.Gitignores.Get("%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										261
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,261 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IssuesService handles communication with the issue related | ||||||
|  | // methods of the GitHub API. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/ | ||||||
|  | type IssuesService struct { | ||||||
|  | 	client *Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Issue represents a GitHub issue on a repository. | ||||||
|  | type Issue struct { | ||||||
|  | 	Number           *int              `json:"number,omitempty"` | ||||||
|  | 	State            *string           `json:"state,omitempty"` | ||||||
|  | 	Title            *string           `json:"title,omitempty"` | ||||||
|  | 	Body             *string           `json:"body,omitempty"` | ||||||
|  | 	User             *User             `json:"user,omitempty"` | ||||||
|  | 	Labels           []Label           `json:"labels,omitempty"` | ||||||
|  | 	Assignee         *User             `json:"assignee,omitempty"` | ||||||
|  | 	Comments         *int              `json:"comments,omitempty"` | ||||||
|  | 	ClosedAt         *time.Time        `json:"closed_at,omitempty"` | ||||||
|  | 	CreatedAt        *time.Time        `json:"created_at,omitempty"` | ||||||
|  | 	UpdatedAt        *time.Time        `json:"updated_at,omitempty"` | ||||||
|  | 	URL              *string           `json:"url,omitempty"` | ||||||
|  | 	HTMLURL          *string           `json:"html_url,omitempty"` | ||||||
|  | 	Milestone        *Milestone        `json:"milestone,omitempty"` | ||||||
|  | 	PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"` | ||||||
|  |  | ||||||
|  | 	// TextMatches is only populated from search results that request text matches | ||||||
|  | 	// See: search.go and https://developer.github.com/v3/search/#text-match-metadata | ||||||
|  | 	TextMatches []TextMatch `json:"text_matches,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i Issue) String() string { | ||||||
|  | 	return Stringify(i) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IssueRequest represents a request to create/edit an issue. | ||||||
|  | // It is separate from Issue above because otherwise Labels | ||||||
|  | // and Assignee fail to serialize to the correct JSON. | ||||||
|  | type IssueRequest struct { | ||||||
|  | 	Title     *string  `json:"title,omitempty"` | ||||||
|  | 	Body      *string  `json:"body,omitempty"` | ||||||
|  | 	Labels    []string `json:"labels,omitempty"` | ||||||
|  | 	Assignee  *string  `json:"assignee,omitempty"` | ||||||
|  | 	State     *string  `json:"state,omitempty"` | ||||||
|  | 	Milestone *int     `json:"milestone,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IssueListOptions specifies the optional parameters to the IssuesService.List | ||||||
|  | // and IssuesService.ListByOrg methods. | ||||||
|  | type IssueListOptions struct { | ||||||
|  | 	// Filter specifies which issues to list.  Possible values are: assigned, | ||||||
|  | 	// created, mentioned, subscribed, all.  Default is "assigned". | ||||||
|  | 	Filter string `url:"filter,omitempty"` | ||||||
|  |  | ||||||
|  | 	// State filters issues based on their state.  Possible values are: open, | ||||||
|  | 	// closed.  Default is "open". | ||||||
|  | 	State string `url:"state,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Labels filters issues based on their label. | ||||||
|  | 	Labels []string `url:"labels,comma,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Sort specifies how to sort issues.  Possible values are: created, updated, | ||||||
|  | 	// and comments.  Default value is "assigned". | ||||||
|  | 	Sort string `url:"sort,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Direction in which to sort issues.  Possible values are: asc, desc. | ||||||
|  | 	// Default is "asc". | ||||||
|  | 	Direction string `url:"direction,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Since filters issues by time. | ||||||
|  | 	Since time.Time `url:"since,omitempty"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PullRequestLinks object is added to the Issue object when it's an issue included | ||||||
|  | // in the IssueCommentEvent webhook payload, if the webhooks is fired by a comment on a PR | ||||||
|  | type PullRequestLinks struct { | ||||||
|  | 	URL      *string `json:"url,omitempty"` | ||||||
|  | 	HTMLURL  *string `json:"html_url,omitempty"` | ||||||
|  | 	DiffURL  *string `json:"diff_url,omitempty"` | ||||||
|  | 	PatchURL *string `json:"patch_url,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List the issues for the authenticated user.  If all is true, list issues | ||||||
|  | // across all the user's visible repositories including owned, member, and | ||||||
|  | // organization repositories; if false, list only owned and member | ||||||
|  | // repositories. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/#list-issues | ||||||
|  | func (s *IssuesService) List(all bool, opt *IssueListOptions) ([]Issue, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if all { | ||||||
|  | 		u = "issues" | ||||||
|  | 	} else { | ||||||
|  | 		u = "user/issues" | ||||||
|  | 	} | ||||||
|  | 	return s.listIssues(u, opt) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListByOrg fetches the issues in the specified organization for the | ||||||
|  | // authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/#list-issues | ||||||
|  | func (s *IssuesService) ListByOrg(org string, opt *IssueListOptions) ([]Issue, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/issues", org) | ||||||
|  | 	return s.listIssues(u, opt) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *IssuesService) listIssues(u string, opt *IssueListOptions) ([]Issue, *Response, error) { | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	issues := new([]Issue) | ||||||
|  | 	resp, err := s.client.Do(req, issues) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *issues, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IssueListByRepoOptions specifies the optional parameters to the | ||||||
|  | // IssuesService.ListByRepo method. | ||||||
|  | type IssueListByRepoOptions struct { | ||||||
|  | 	// Milestone limits issues for the specified milestone.  Possible values are | ||||||
|  | 	// a milestone number, "none" for issues with no milestone, "*" for issues | ||||||
|  | 	// with any milestone. | ||||||
|  | 	Milestone string `url:"milestone,omitempty"` | ||||||
|  |  | ||||||
|  | 	// State filters issues based on their state.  Possible values are: open, | ||||||
|  | 	// closed.  Default is "open". | ||||||
|  | 	State string `url:"state,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Assignee filters issues based on their assignee.  Possible values are a | ||||||
|  | 	// user name, "none" for issues that are not assigned, "*" for issues with | ||||||
|  | 	// any assigned user. | ||||||
|  | 	Assignee string `url:"assignee,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Assignee filters issues based on their creator. | ||||||
|  | 	Creator string `url:"creator,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Assignee filters issues to those mentioned a specific user. | ||||||
|  | 	Mentioned string `url:"mentioned,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Labels filters issues based on their label. | ||||||
|  | 	Labels []string `url:"labels,omitempty,comma"` | ||||||
|  |  | ||||||
|  | 	// Sort specifies how to sort issues.  Possible values are: created, updated, | ||||||
|  | 	// and comments.  Default value is "assigned". | ||||||
|  | 	Sort string `url:"sort,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Direction in which to sort issues.  Possible values are: asc, desc. | ||||||
|  | 	// Default is "asc". | ||||||
|  | 	Direction string `url:"direction,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Since filters issues by time. | ||||||
|  | 	Since time.Time `url:"since,omitempty"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListByRepo lists the issues for the specified repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/#list-issues-for-a-repository | ||||||
|  | func (s *IssuesService) ListByRepo(owner string, repo string, opt *IssueListByRepoOptions) ([]Issue, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	issues := new([]Issue) | ||||||
|  | 	resp, err := s.client.Do(req, issues) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *issues, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get a single issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/#get-a-single-issue | ||||||
|  | func (s *IssuesService) Get(owner string, repo string, number int) (*Issue, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	issue := new(Issue) | ||||||
|  | 	resp, err := s.client.Do(req, issue) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return issue, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create a new issue on the specified repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/#create-an-issue | ||||||
|  | func (s *IssuesService) Create(owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, issue) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	i := new(Issue) | ||||||
|  | 	resp, err := s.client.Do(req, i) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return i, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Edit an issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/#edit-an-issue | ||||||
|  | func (s *IssuesService) Edit(owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, issue) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	i := new(Issue) | ||||||
|  | 	resp, err := s.client.Do(req, i) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return i, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_assignees.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_assignees.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // ListAssignees fetches all available assignees (owners and collaborators) to | ||||||
|  | // which issues may be assigned. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/assignees/#list-assignees | ||||||
|  | func (s *IssuesService) ListAssignees(owner string, repo string, opt *ListOptions) ([]User, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	assignees := new([]User) | ||||||
|  | 	resp, err := s.client.Do(req, assignees) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *assignees, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsAssignee checks if a user is an assignee for the specified repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/assignees/#check-assignee | ||||||
|  | func (s *IssuesService) IsAssignee(owner string, repo string, user string) (bool, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/assignees/%v", owner, repo, user) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, nil, err | ||||||
|  | 	} | ||||||
|  | 	resp, err := s.client.Do(req, nil) | ||||||
|  | 	assignee, err := parseBoolResponse(err) | ||||||
|  | 	return assignee, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										98
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_assignees_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_assignees_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListAssignees(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/assignees", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{"page": "2"}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	assignees, _, err := client.Issues.ListAssignees("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.List returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []User{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(assignees, want) { | ||||||
|  | 		t.Errorf("Issues.ListAssignees returned %+v, want %+v", assignees, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListAssignees_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListAssignees("%", "r", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_IsAssignee_true(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	assignee, _, err := client.Issues.IsAssignee("o", "r", "u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.IsAssignee returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := true; assignee != want { | ||||||
|  | 		t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_IsAssignee_false(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	assignee, _, err := client.Issues.IsAssignee("o", "r", "u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.IsAssignee returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := false; assignee != want { | ||||||
|  | 		t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_IsAssignee_error(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		http.Error(w, "BadRequest", http.StatusBadRequest) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	assignee, _, err := client.Issues.IsAssignee("o", "r", "u") | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected HTTP 400 response") | ||||||
|  | 	} | ||||||
|  | 	if want := false; assignee != want { | ||||||
|  | 		t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_IsAssignee_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.IsAssignee("%", "r", "u") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										138
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_comments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_comments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IssueComment represents a comment left on an issue. | ||||||
|  | type IssueComment struct { | ||||||
|  | 	ID        *int       `json:"id,omitempty"` | ||||||
|  | 	Body      *string    `json:"body,omitempty"` | ||||||
|  | 	User      *User      `json:"user,omitempty"` | ||||||
|  | 	CreatedAt *time.Time `json:"created_at,omitempty"` | ||||||
|  | 	UpdatedAt *time.Time `json:"updated_at,omitempty"` | ||||||
|  | 	URL       *string    `json:"url,omitempty"` | ||||||
|  | 	HTMLURL   *string    `json:"html_url,omitempty"` | ||||||
|  | 	IssueURL  *string    `json:"issue_url,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i IssueComment) String() string { | ||||||
|  | 	return Stringify(i) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IssueListCommentsOptions specifies the optional parameters to the | ||||||
|  | // IssuesService.ListComments method. | ||||||
|  | type IssueListCommentsOptions struct { | ||||||
|  | 	// Sort specifies how to sort comments.  Possible values are: created, updated. | ||||||
|  | 	Sort string `url:"sort,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Direction in which to sort comments.  Possible values are: asc, desc. | ||||||
|  | 	Direction string `url:"direction,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Since filters comments by time. | ||||||
|  | 	Since time.Time `url:"since,omitempty"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListComments lists all comments on the specified issue.  Specifying an issue | ||||||
|  | // number of 0 will return all comments on all issues for the repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/comments/#list-comments-on-an-issue | ||||||
|  | func (s *IssuesService) ListComments(owner string, repo string, number int, opt *IssueListCommentsOptions) ([]IssueComment, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if number == 0 { | ||||||
|  | 		u = fmt.Sprintf("repos/%v/%v/issues/comments", owner, repo) | ||||||
|  | 	} else { | ||||||
|  | 		u = fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	comments := new([]IssueComment) | ||||||
|  | 	resp, err := s.client.Do(req, comments) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *comments, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetComment fetches the specified issue comment. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/comments/#get-a-single-comment | ||||||
|  | func (s *IssuesService) GetComment(owner string, repo string, id int) (*IssueComment, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	comment := new(IssueComment) | ||||||
|  | 	resp, err := s.client.Do(req, comment) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return comment, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateComment creates a new comment on the specified issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/comments/#create-a-comment | ||||||
|  | func (s *IssuesService) CreateComment(owner string, repo string, number int, comment *IssueComment) (*IssueComment, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, comment) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	c := new(IssueComment) | ||||||
|  | 	resp, err := s.client.Do(req, c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EditComment updates an issue comment. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/comments/#edit-a-comment | ||||||
|  | func (s *IssuesService) EditComment(owner string, repo string, id int, comment *IssueComment) (*IssueComment, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, comment) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	c := new(IssueComment) | ||||||
|  | 	resp, err := s.client.Do(req, c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteComment deletes an issue comment. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/comments/#delete-a-comment | ||||||
|  | func (s *IssuesService) DeleteComment(owner string, repo string, id int) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
							
								
								
									
										184
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_comments_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_comments_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListComments_allIssues(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/comments", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"sort":      "updated", | ||||||
|  | 			"direction": "desc", | ||||||
|  | 			"since":     "2002-02-10T15:30:00Z", | ||||||
|  | 			"page":      "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &IssueListCommentsOptions{ | ||||||
|  | 		Sort:        "updated", | ||||||
|  | 		Direction:   "desc", | ||||||
|  | 		Since:       time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), | ||||||
|  | 		ListOptions: ListOptions{Page: 2}, | ||||||
|  | 	} | ||||||
|  | 	comments, _, err := client.Issues.ListComments("o", "r", 0, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListComments returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []IssueComment{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(comments, want) { | ||||||
|  | 		t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListComments_specificIssue(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	comments, _, err := client.Issues.ListComments("o", "r", 1, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListComments returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []IssueComment{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(comments, want) { | ||||||
|  | 		t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListComments_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListComments("%", "r", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_GetComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"id":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	comment, _, err := client.Issues.GetComment("o", "r", 1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.GetComment returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &IssueComment{ID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(comment, want) { | ||||||
|  | 		t.Errorf("Issues.GetComment returned %+v, want %+v", comment, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_GetComment_invalidOrg(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.GetComment("%", "r", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_CreateComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &IssueComment{Body: String("b")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(IssueComment) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"id":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	comment, _, err := client.Issues.CreateComment("o", "r", 1, input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.CreateComment returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &IssueComment{ID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(comment, want) { | ||||||
|  | 		t.Errorf("Issues.CreateComment returned %+v, want %+v", comment, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_CreateComment_invalidOrg(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.CreateComment("%", "r", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_EditComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &IssueComment{Body: String("b")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(IssueComment) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"id":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	comment, _, err := client.Issues.EditComment("o", "r", 1, input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.EditComment returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &IssueComment{ID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(comment, want) { | ||||||
|  | 		t.Errorf("Issues.EditComment returned %+v, want %+v", comment, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_EditComment_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.EditComment("%", "r", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_DeleteComment(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Issues.DeleteComment("o", "r", 1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.DeleteComments returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_DeleteComment_invalidOwner(t *testing.T) { | ||||||
|  | 	_, err := client.Issues.DeleteComment("%", "r", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IssueEvent represents an event that occurred around an Issue or Pull Request. | ||||||
|  | type IssueEvent struct { | ||||||
|  | 	ID  *int    `json:"id,omitempty"` | ||||||
|  | 	URL *string `json:"url,omitempty"` | ||||||
|  |  | ||||||
|  | 	// The User that generated this event. | ||||||
|  | 	Actor *User `json:"actor,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Event identifies the actual type of Event that occurred.  Possible | ||||||
|  | 	// values are: | ||||||
|  | 	// | ||||||
|  | 	//     closed | ||||||
|  | 	//       The issue was closed by the actor. When the commit_id is | ||||||
|  | 	//       present, it identifies the commit that closed the issue using | ||||||
|  | 	//       “closes / fixes #NN” syntax. | ||||||
|  | 	// | ||||||
|  | 	//     reopened | ||||||
|  | 	//       The issue was reopened by the actor. | ||||||
|  | 	// | ||||||
|  | 	//     subscribed | ||||||
|  | 	//       The actor subscribed to receive notifications for an issue. | ||||||
|  | 	// | ||||||
|  | 	//     merged | ||||||
|  | 	//       The issue was merged by the actor. The commit_id attribute is the SHA1 of the HEAD commit that was merged. | ||||||
|  | 	// | ||||||
|  | 	//     referenced | ||||||
|  | 	//       The issue was referenced from a commit message. The commit_id attribute is the commit SHA1 of where that happened. | ||||||
|  | 	// | ||||||
|  | 	//     mentioned | ||||||
|  | 	//       The actor was @mentioned in an issue body. | ||||||
|  | 	// | ||||||
|  | 	//     assigned | ||||||
|  | 	//       The issue was assigned to the actor. | ||||||
|  | 	// | ||||||
|  | 	//     head_ref_deleted | ||||||
|  | 	//       The pull request’s branch was deleted. | ||||||
|  | 	// | ||||||
|  | 	//     head_ref_restored | ||||||
|  | 	//       The pull request’s branch was restored. | ||||||
|  | 	Event *string `json:"event,omitempty"` | ||||||
|  |  | ||||||
|  | 	// The SHA of the commit that referenced this commit, if applicable. | ||||||
|  | 	CommitID *string `json:"commit_id,omitempty"` | ||||||
|  |  | ||||||
|  | 	CreatedAt *time.Time `json:"created_at,omitempty"` | ||||||
|  | 	Issue     *Issue     `json:"issue,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListIssueEvents lists events for the specified issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue | ||||||
|  | func (s *IssuesService) ListIssueEvents(owner, repo string, number int, opt *ListOptions) ([]IssueEvent, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var events []IssueEvent | ||||||
|  | 	resp, err := s.client.Do(req, &events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListRepositoryEvents lists events for the specified repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository | ||||||
|  | func (s *IssuesService) ListRepositoryEvents(owner, repo string, opt *ListOptions) ([]IssueEvent, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var events []IssueEvent | ||||||
|  | 	resp, err := s.client.Do(req, &events) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return events, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetEvent returns the specified issue event. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event | ||||||
|  | func (s *IssuesService) GetEvent(owner, repo string, id int) (*IssueEvent, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id) | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	event := new(IssueEvent) | ||||||
|  | 	resp, err := s.client.Do(req, event) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return event, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_events_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_events_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListIssueEvents(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page":     "1", | ||||||
|  | 			"per_page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 1, PerPage: 2} | ||||||
|  | 	events, _, err := client.Issues.ListIssueEvents("o", "r", 1, opt) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListIssueEvents returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []IssueEvent{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Issues.ListIssueEvents returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListRepositoryEvents(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"page":     "1", | ||||||
|  | 			"per_page": "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 1, PerPage: 2} | ||||||
|  | 	events, _, err := client.Issues.ListRepositoryEvents("o", "r", opt) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListRepositoryEvents returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []IssueEvent{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(events, want) { | ||||||
|  | 		t.Errorf("Issues.ListRepositoryEvents returned %+v, want %+v", events, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_GetEvent(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/events/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"id":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	event, _, err := client.Issues.GetEvent("o", "r", 1) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.GetEvent returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &IssueEvent{ID: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(event, want) { | ||||||
|  | 		t.Errorf("Issues.GetEvent returned %+v, want %+v", event, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										222
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // Label represents a GitHib label on an Issue | ||||||
|  | type Label struct { | ||||||
|  | 	URL   *string `json:"url,omitempty"` | ||||||
|  | 	Name  *string `json:"name,omitempty"` | ||||||
|  | 	Color *string `json:"color,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l Label) String() string { | ||||||
|  | 	return fmt.Sprint(*l.Name) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListLabels lists all labels for a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository | ||||||
|  | func (s *IssuesService) ListLabels(owner string, repo string, opt *ListOptions) ([]Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	labels := new([]Label) | ||||||
|  | 	resp, err := s.client.Do(req, labels) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *labels, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetLabel gets a single label. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#get-a-single-label | ||||||
|  | func (s *IssuesService) GetLabel(owner string, repo string, name string) (*Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	label := new(Label) | ||||||
|  | 	resp, err := s.client.Do(req, label) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return label, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateLabel creates a new label on the specified repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#create-a-label | ||||||
|  | func (s *IssuesService) CreateLabel(owner string, repo string, label *Label) (*Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, label) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l := new(Label) | ||||||
|  | 	resp, err := s.client.Do(req, l) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return l, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EditLabel edits a label. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#update-a-label | ||||||
|  | func (s *IssuesService) EditLabel(owner string, repo string, name string, label *Label) (*Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, label) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l := new(Label) | ||||||
|  | 	resp, err := s.client.Do(req, l) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return l, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteLabel deletes a label. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#delete-a-label | ||||||
|  | func (s *IssuesService) DeleteLabel(owner string, repo string, name string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListLabelsByIssue lists all labels for an issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository | ||||||
|  | func (s *IssuesService) ListLabelsByIssue(owner string, repo string, number int, opt *ListOptions) ([]Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	labels := new([]Label) | ||||||
|  | 	resp, err := s.client.Do(req, labels) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *labels, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddLabelsToIssue adds labels to an issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository | ||||||
|  | func (s *IssuesService) AddLabelsToIssue(owner string, repo string, number int, labels []string) ([]Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, labels) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l := new([]Label) | ||||||
|  | 	resp, err := s.client.Do(req, l) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *l, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RemoveLabelForIssue removes a label for an issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue | ||||||
|  | func (s *IssuesService) RemoveLabelForIssue(owner string, repo string, number int, label string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d/labels/%v", owner, repo, number, label) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ReplaceLabelsForIssue replaces all labels for an issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue | ||||||
|  | func (s *IssuesService) ReplaceLabelsForIssue(owner string, repo string, number int, labels []string) ([]Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, labels) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l := new([]Label) | ||||||
|  | 	resp, err := s.client.Do(req, l) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *l, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RemoveLabelsForIssue removes all labels for an issue. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue | ||||||
|  | func (s *IssuesService) RemoveLabelsForIssue(owner string, repo string, number int) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListLabelsForMilestone lists labels for every issue in a milestone. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone | ||||||
|  | func (s *IssuesService) ListLabelsForMilestone(owner string, repo string, number int, opt *ListOptions) ([]Label, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/milestones/%d/labels", owner, repo, number) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	labels := new([]Label) | ||||||
|  | 	resp, err := s.client.Do(req, labels) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *labels, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										313
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_labels_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_labels_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,313 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListLabels(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{"page": "2"}) | ||||||
|  | 		fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	labels, _, err := client.Issues.ListLabels("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListLabels returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Label{{Name: String("a")}, {Name: String("b")}} | ||||||
|  | 	if !reflect.DeepEqual(labels, want) { | ||||||
|  | 		t.Errorf("Issues.ListLabels returned %+v, want %+v", labels, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListLabels_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListLabels("%", "%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_GetLabel(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"url":"u", "name": "n", "color": "c"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	label, _, err := client.Issues.GetLabel("o", "r", "n") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.GetLabel returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Label{URL: String("u"), Name: String("n"), Color: String("c")} | ||||||
|  | 	if !reflect.DeepEqual(label, want) { | ||||||
|  | 		t.Errorf("Issues.GetLabel returned %+v, want %+v", label, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_GetLabel_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.GetLabel("%", "%", "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_CreateLabel(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Label{Name: String("n")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Label) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"url":"u"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	label, _, err := client.Issues.CreateLabel("o", "r", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.CreateLabel returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Label{URL: String("u")} | ||||||
|  | 	if !reflect.DeepEqual(label, want) { | ||||||
|  | 		t.Errorf("Issues.CreateLabel returned %+v, want %+v", label, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_CreateLabel_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.CreateLabel("%", "%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_EditLabel(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Label{Name: String("z")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Label) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"url":"u"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	label, _, err := client.Issues.EditLabel("o", "r", "n", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.EditLabel returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Label{URL: String("u")} | ||||||
|  | 	if !reflect.DeepEqual(label, want) { | ||||||
|  | 		t.Errorf("Issues.EditLabel returned %+v, want %+v", label, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_EditLabel_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.EditLabel("%", "%", "%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_DeleteLabel(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Issues.DeleteLabel("o", "r", "n") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.DeleteLabel returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_DeleteLabel_invalidOwner(t *testing.T) { | ||||||
|  | 	_, err := client.Issues.DeleteLabel("%", "%", "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListLabelsByIssue(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{"page": "2"}) | ||||||
|  | 		fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	labels, _, err := client.Issues.ListLabelsByIssue("o", "r", 1, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListLabelsByIssue returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Label{{Name: String("a")}, {Name: String("b")}} | ||||||
|  | 	if !reflect.DeepEqual(labels, want) { | ||||||
|  | 		t.Errorf("Issues.ListLabelsByIssue returned %+v, want %+v", labels, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListLabelsByIssue_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListLabelsByIssue("%", "%", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_AddLabelsToIssue(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := []string{"a", "b"} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new([]string) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(*v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `[{"url":"u"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	labels, _, err := client.Issues.AddLabelsToIssue("o", "r", 1, input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.AddLabelsToIssue returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Label{{URL: String("u")}} | ||||||
|  | 	if !reflect.DeepEqual(labels, want) { | ||||||
|  | 		t.Errorf("Issues.AddLabelsToIssue returned %+v, want %+v", labels, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_AddLabelsToIssue_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.AddLabelsToIssue("%", "%", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_RemoveLabelForIssue(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/labels/l", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Issues.RemoveLabelForIssue("o", "r", 1, "l") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.RemoveLabelForIssue returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_RemoveLabelForIssue_invalidOwner(t *testing.T) { | ||||||
|  | 	_, err := client.Issues.RemoveLabelForIssue("%", "%", 1, "%") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ReplaceLabelsForIssue(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := []string{"a", "b"} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new([]string) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PUT") | ||||||
|  | 		if !reflect.DeepEqual(*v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `[{"url":"u"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	labels, _, err := client.Issues.ReplaceLabelsForIssue("o", "r", 1, input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ReplaceLabelsForIssue returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Label{{URL: String("u")}} | ||||||
|  | 	if !reflect.DeepEqual(labels, want) { | ||||||
|  | 		t.Errorf("Issues.ReplaceLabelsForIssue returned %+v, want %+v", labels, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ReplaceLabelsForIssue_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ReplaceLabelsForIssue("%", "%", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_RemoveLabelsForIssue(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Issues.RemoveLabelsForIssue("o", "r", 1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.RemoveLabelsForIssue returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_RemoveLabelsForIssue_invalidOwner(t *testing.T) { | ||||||
|  | 	_, err := client.Issues.RemoveLabelsForIssue("%", "%", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListLabelsForMilestone(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/milestones/1/labels", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{"page": "2"}) | ||||||
|  | 		fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOptions{Page: 2} | ||||||
|  | 	labels, _, err := client.Issues.ListLabelsForMilestone("o", "r", 1, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListLabelsForMilestone returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Label{{Name: String("a")}, {Name: String("b")}} | ||||||
|  | 	if !reflect.DeepEqual(labels, want) { | ||||||
|  | 		t.Errorf("Issues.ListLabelsForMilestone returned %+v, want %+v", labels, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListLabelsForMilestone_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListLabelsForMilestone("%", "%", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										140
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_milestones.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_milestones.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Milestone represents a Github repository milestone. | ||||||
|  | type Milestone struct { | ||||||
|  | 	URL          *string    `json:"url,omitempty"` | ||||||
|  | 	Number       *int       `json:"number,omitempty"` | ||||||
|  | 	State        *string    `json:"state,omitempty"` | ||||||
|  | 	Title        *string    `json:"title,omitempty"` | ||||||
|  | 	Description  *string    `json:"description,omitempty"` | ||||||
|  | 	Creator      *User      `json:"creator,omitempty"` | ||||||
|  | 	OpenIssues   *int       `json:"open_issues,omitempty"` | ||||||
|  | 	ClosedIssues *int       `json:"closed_issues,omitempty"` | ||||||
|  | 	CreatedAt    *time.Time `json:"created_at,omitempty"` | ||||||
|  | 	UpdatedAt    *time.Time `json:"updated_at,omitempty"` | ||||||
|  | 	DueOn        *time.Time `json:"due_on,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m Milestone) String() string { | ||||||
|  | 	return Stringify(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MilestoneListOptions specifies the optional parameters to the | ||||||
|  | // IssuesService.ListMilestones method. | ||||||
|  | type MilestoneListOptions struct { | ||||||
|  | 	// State filters milestones based on their state. Possible values are: | ||||||
|  | 	// open, closed. Default is "open". | ||||||
|  | 	State string `url:"state,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Sort specifies how to sort milestones. Possible values are: due_date, completeness. | ||||||
|  | 	// Default value is "due_date". | ||||||
|  | 	Sort string `url:"sort,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Direction in which to sort milestones. Possible values are: asc, desc. | ||||||
|  | 	// Default is "asc". | ||||||
|  | 	Direction string `url:"direction,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListMilestones lists all milestones for a repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository | ||||||
|  | func (s *IssuesService) ListMilestones(owner string, repo string, opt *MilestoneListOptions) ([]Milestone, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("/repos/%v/%v/milestones", owner, repo) | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	milestones := new([]Milestone) | ||||||
|  | 	resp, err := s.client.Do(req, milestones) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *milestones, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetMilestone gets a single milestone. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone | ||||||
|  | func (s *IssuesService) GetMilestone(owner string, repo string, number int) (*Milestone, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("/repos/%v/%v/milestones/%d", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	milestone := new(Milestone) | ||||||
|  | 	resp, err := s.client.Do(req, milestone) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return milestone, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateMilestone creates a new milestone on the specified repository. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone | ||||||
|  | func (s *IssuesService) CreateMilestone(owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("/repos/%v/%v/milestones", owner, repo) | ||||||
|  | 	req, err := s.client.NewRequest("POST", u, milestone) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m := new(Milestone) | ||||||
|  | 	resp, err := s.client.Do(req, m) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return m, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EditMilestone edits a milestone. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/milestones/#update-a-milestone | ||||||
|  | func (s *IssuesService) EditMilestone(owner string, repo string, number int, milestone *Milestone) (*Milestone, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, milestone) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m := new(Milestone) | ||||||
|  | 	resp, err := s.client.Do(req, m) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return m, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteMilestone deletes a milestone. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/issues/milestones/#delete-a-milestone | ||||||
|  | func (s *IssuesService) DeleteMilestone(owner string, repo string, number int) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
							
								
								
									
										157
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_milestones_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_milestones_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListMilestones(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"state":     "closed", | ||||||
|  | 			"sort":      "due_date", | ||||||
|  | 			"direction": "asc", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"number":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &MilestoneListOptions{"closed", "due_date", "asc"} | ||||||
|  | 	milestones, _, err := client.Issues.ListMilestones("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("IssuesService.ListMilestones returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Milestone{{Number: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(milestones, want) { | ||||||
|  | 		t.Errorf("IssuesService.ListMilestones returned %+v, want %+v", milestones, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListMilestones_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListMilestones("%", "r", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_GetMilestone(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"number":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	milestone, _, err := client.Issues.GetMilestone("o", "r", 1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("IssuesService.GetMilestone returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Milestone{Number: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(milestone, want) { | ||||||
|  | 		t.Errorf("IssuesService.GetMilestone returned %+v, want %+v", milestone, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_GetMilestone_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.GetMilestone("%", "r", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_CreateMilestone(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Milestone{Title: String("t")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Milestone) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"number":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	milestone, _, err := client.Issues.CreateMilestone("o", "r", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("IssuesService.CreateMilestone returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Milestone{Number: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(milestone, want) { | ||||||
|  | 		t.Errorf("IssuesService.CreateMilestone returned %+v, want %+v", milestone, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_CreateMilestone_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.CreateMilestone("%", "r", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_EditMilestone(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Milestone{Title: String("t")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Milestone) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"number":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	milestone, _, err := client.Issues.EditMilestone("o", "r", 1, input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("IssuesService.EditMilestone returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Milestone{Number: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(milestone, want) { | ||||||
|  | 		t.Errorf("IssuesService.EditMilestone returned %+v, want %+v", milestone, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_EditMilestone_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.EditMilestone("%", "r", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_DeleteMilestone(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Issues.DeleteMilestone("o", "r", 1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("IssuesService.DeleteMilestone returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_DeleteMilestone_invalidOwner(t *testing.T) { | ||||||
|  | 	_, err := client.Issues.DeleteMilestone("%", "r", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										242
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/issues_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,242 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestIssuesService_List_all(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/issues", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"filter":    "all", | ||||||
|  | 			"state":     "closed", | ||||||
|  | 			"labels":    "a,b", | ||||||
|  | 			"sort":      "updated", | ||||||
|  | 			"direction": "asc", | ||||||
|  | 			"since":     "2002-02-10T15:30:00Z", | ||||||
|  | 			"page":      "1", | ||||||
|  | 			"per_page":  "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"number":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &IssueListOptions{ | ||||||
|  | 		"all", "closed", []string{"a", "b"}, "updated", "asc", | ||||||
|  | 		time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), | ||||||
|  | 		ListOptions{Page: 1, PerPage: 2}, | ||||||
|  | 	} | ||||||
|  | 	issues, _, err := client.Issues.List(true, opt) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.List returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Issue{{Number: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(issues, want) { | ||||||
|  | 		t.Errorf("Issues.List returned %+v, want %+v", issues, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_List_owned(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/issues", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"number":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	issues, _, err := client.Issues.List(false, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.List returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Issue{{Number: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(issues, want) { | ||||||
|  | 		t.Errorf("Issues.List returned %+v, want %+v", issues, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListByOrg(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/issues", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"number":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	issues, _, err := client.Issues.ListByOrg("o", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListByOrg returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Issue{{Number: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(issues, want) { | ||||||
|  | 		t.Errorf("Issues.List returned %+v, want %+v", issues, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListByOrg_invalidOrg(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListByOrg("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListByRepo(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"milestone": "*", | ||||||
|  | 			"state":     "closed", | ||||||
|  | 			"assignee":  "a", | ||||||
|  | 			"creator":   "c", | ||||||
|  | 			"mentioned": "m", | ||||||
|  | 			"labels":    "a,b", | ||||||
|  | 			"sort":      "updated", | ||||||
|  | 			"direction": "asc", | ||||||
|  | 			"since":     "2002-02-10T15:30:00Z", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"number":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &IssueListByRepoOptions{ | ||||||
|  | 		"*", "closed", "a", "c", "m", []string{"a", "b"}, "updated", "asc", | ||||||
|  | 		time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), | ||||||
|  | 		ListOptions{0, 0}, | ||||||
|  | 	} | ||||||
|  | 	issues, _, err := client.Issues.ListByRepo("o", "r", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.ListByOrg returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Issue{{Number: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(issues, want) { | ||||||
|  | 		t.Errorf("Issues.List returned %+v, want %+v", issues, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_ListByRepo_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.ListByRepo("%", "r", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_Get(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"number":1, "labels": [{"url": "u", "name": "n", "color": "c"}]}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	issue, _, err := client.Issues.Get("o", "r", 1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.Get returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Issue{ | ||||||
|  | 		Number: Int(1), | ||||||
|  | 		Labels: []Label{{ | ||||||
|  | 			URL:   String("u"), | ||||||
|  | 			Name:  String("n"), | ||||||
|  | 			Color: String("c"), | ||||||
|  | 		}}, | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(issue, want) { | ||||||
|  | 		t.Errorf("Issues.Get returned %+v, want %+v", issue, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_Get_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.Get("%", "r", 1) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_Create(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &IssueRequest{ | ||||||
|  | 		Title:    String("t"), | ||||||
|  | 		Body:     String("b"), | ||||||
|  | 		Assignee: String("a"), | ||||||
|  | 		Labels:   []string{"l1", "l2"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(IssueRequest) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"number":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	issue, _, err := client.Issues.Create("o", "r", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.Create returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Issue{Number: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(issue, want) { | ||||||
|  | 		t.Errorf("Issues.Create returned %+v, want %+v", issue, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_Create_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.Create("%", "r", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_Edit(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &IssueRequest{Title: String("t")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(IssueRequest) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"number":1}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	issue, _, err := client.Issues.Edit("o", "r", 1, input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Issues.Edit returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Issue{Number: Int(1)} | ||||||
|  | 	if !reflect.DeepEqual(issue, want) { | ||||||
|  | 		t.Errorf("Issues.Edit returned %+v, want %+v", issue, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIssuesService_Edit_invalidOwner(t *testing.T) { | ||||||
|  | 	_, _, err := client.Issues.Edit("%", "r", 1, nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
							
								
								
									
										161
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/misc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/misc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/url" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // MarkdownOptions specifies optional parameters to the Markdown method. | ||||||
|  | type MarkdownOptions struct { | ||||||
|  | 	// Mode identifies the rendering mode.  Possible values are: | ||||||
|  | 	//   markdown - render a document as plain Markdown, just like | ||||||
|  | 	//   README files are rendered. | ||||||
|  | 	// | ||||||
|  | 	//   gfm - to render a document as user-content, e.g. like user | ||||||
|  | 	//   comments or issues are rendered. In GFM mode, hard line breaks are | ||||||
|  | 	//   always taken into account, and issue and user mentions are linked | ||||||
|  | 	//   accordingly. | ||||||
|  | 	// | ||||||
|  | 	// Default is "markdown". | ||||||
|  | 	Mode string | ||||||
|  |  | ||||||
|  | 	// Context identifies the repository context.  Only taken into account | ||||||
|  | 	// when rendering as "gfm". | ||||||
|  | 	Context string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type markdownRequest struct { | ||||||
|  | 	Text    *string `json:"text,omitempty"` | ||||||
|  | 	Mode    *string `json:"mode,omitempty"` | ||||||
|  | 	Context *string `json:"context,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Markdown renders an arbitrary Markdown document. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/markdown/ | ||||||
|  | func (c *Client) Markdown(text string, opt *MarkdownOptions) (string, *Response, error) { | ||||||
|  | 	request := &markdownRequest{Text: String(text)} | ||||||
|  | 	if opt != nil { | ||||||
|  | 		if opt.Mode != "" { | ||||||
|  | 			request.Mode = String(opt.Mode) | ||||||
|  | 		} | ||||||
|  | 		if opt.Context != "" { | ||||||
|  | 			request.Context = String(opt.Context) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := c.NewRequest("POST", "markdown", request) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf := new(bytes.Buffer) | ||||||
|  | 	resp, err := c.Do(req, buf) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf.String(), resp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListEmojis returns the emojis available to use on GitHub. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/emojis/ | ||||||
|  | func (c *Client) ListEmojis() (map[string]string, *Response, error) { | ||||||
|  | 	req, err := c.NewRequest("GET", "emojis", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var emoji map[string]string | ||||||
|  | 	resp, err := c.Do(req, &emoji) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return emoji, resp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // APIMeta represents metadata about the GitHub API. | ||||||
|  | type APIMeta struct { | ||||||
|  | 	// An Array of IP addresses in CIDR format specifying the addresses | ||||||
|  | 	// that incoming service hooks will originate from on GitHub.com. | ||||||
|  | 	Hooks []string `json:"hooks,omitempty"` | ||||||
|  |  | ||||||
|  | 	// An Array of IP addresses in CIDR format specifying the Git servers | ||||||
|  | 	// for GitHub.com. | ||||||
|  | 	Git []string `json:"git,omitempty"` | ||||||
|  |  | ||||||
|  | 	// Whether authentication with username and password is supported. | ||||||
|  | 	// (GitHub Enterprise instances using CAS or OAuth for authentication | ||||||
|  | 	// will return false. Features like Basic Authentication with a | ||||||
|  | 	// username and password, sudo mode, and two-factor authentication are | ||||||
|  | 	// not supported on these servers.) | ||||||
|  | 	VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // APIMeta returns information about GitHub.com, the service. Or, if you access | ||||||
|  | // this endpoint on your organization’s GitHub Enterprise installation, this | ||||||
|  | // endpoint provides information about that installation. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/meta/ | ||||||
|  | func (c *Client) APIMeta() (*APIMeta, *Response, error) { | ||||||
|  | 	req, err := c.NewRequest("GET", "meta", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	meta := new(APIMeta) | ||||||
|  | 	resp, err := c.Do(req, meta) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return meta, resp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Octocat returns an ASCII art octocat with the specified message in a speech | ||||||
|  | // bubble.  If message is empty, a random zen phrase is used. | ||||||
|  | func (c *Client) Octocat(message string) (string, *Response, error) { | ||||||
|  | 	u := "octocat" | ||||||
|  | 	if message != "" { | ||||||
|  | 		u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := c.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf := new(bytes.Buffer) | ||||||
|  | 	resp, err := c.Do(req, buf) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf.String(), resp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Zen returns a random line from The Zen of GitHub. | ||||||
|  | // | ||||||
|  | // see also: http://warpspire.com/posts/taste/ | ||||||
|  | func (c *Client) Zen() (string, *Response, error) { | ||||||
|  | 	req, err := c.NewRequest("GET", "zen", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf := new(bytes.Buffer) | ||||||
|  | 	resp, err := c.Do(req, buf) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf.String(), resp, nil | ||||||
|  | } | ||||||
							
								
								
									
										137
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/misc_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/misc_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | // Copyright 2014 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestMarkdown(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &markdownRequest{ | ||||||
|  | 		Text:    String("# text #"), | ||||||
|  | 		Mode:    String("gfm"), | ||||||
|  | 		Context: String("google/go-github"), | ||||||
|  | 	} | ||||||
|  | 	mux.HandleFunc("/markdown", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(markdownRequest) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprint(w, `<h1>text</h1>`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	md, _, err := client.Markdown("# text #", &MarkdownOptions{ | ||||||
|  | 		Mode:    "gfm", | ||||||
|  | 		Context: "google/go-github", | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Markdown returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if want := "<h1>text</h1>"; want != md { | ||||||
|  | 		t.Errorf("Markdown returned %+v, want %+v", md, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestListEmojis(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"+1": "+1.png"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	emoji, _, err := client.ListEmojis() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("ListEmojis returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := map[string]string{"+1": "+1.png"} | ||||||
|  | 	if !reflect.DeepEqual(want, emoji) { | ||||||
|  | 		t.Errorf("ListEmojis returned %+v, want %+v", emoji, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAPIMeta(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `{"hooks":["h"], "git":["g"], "verifiable_password_authentication": true}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	meta, _, err := client.APIMeta() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("APIMeta returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &APIMeta{ | ||||||
|  | 		Hooks: []string{"h"}, | ||||||
|  | 		Git:   []string{"g"}, | ||||||
|  | 		VerifiablePasswordAuthentication: Bool(true), | ||||||
|  | 	} | ||||||
|  | 	if !reflect.DeepEqual(want, meta) { | ||||||
|  | 		t.Errorf("APIMeta returned %+v, want %+v", meta, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOctocat(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := "input" | ||||||
|  | 	output := "sample text" | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/octocat", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{"s": input}) | ||||||
|  | 		w.Header().Set("Content-Type", "application/octocat-stream") | ||||||
|  | 		fmt.Fprint(w, output) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	got, _, err := client.Octocat(input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Octocat returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if want := output; got != want { | ||||||
|  | 		t.Errorf("Octocat returned %+v, want %+v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestZen(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	output := "sample text" | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/zen", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.Header().Set("Content-Type", "text/plain;charset=utf-8") | ||||||
|  | 		fmt.Fprint(w, output) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	got, _, err := client.Zen() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Zen returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if want := output; got != want { | ||||||
|  | 		t.Errorf("Zen returned %+v, want %+v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										137
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/orgs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/orgs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // OrganizationsService provides access to the organization related functions | ||||||
|  | // in the GitHub API. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/ | ||||||
|  | type OrganizationsService struct { | ||||||
|  | 	client *Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Organization represents a GitHub organization account. | ||||||
|  | type Organization struct { | ||||||
|  | 	Login             *string    `json:"login,omitempty"` | ||||||
|  | 	ID                *int       `json:"id,omitempty"` | ||||||
|  | 	AvatarURL         *string    `json:"avatar_url,omitempty"` | ||||||
|  | 	HTMLURL           *string    `json:"html_url,omitempty"` | ||||||
|  | 	Name              *string    `json:"name,omitempty"` | ||||||
|  | 	Company           *string    `json:"company,omitempty"` | ||||||
|  | 	Blog              *string    `json:"blog,omitempty"` | ||||||
|  | 	Location          *string    `json:"location,omitempty"` | ||||||
|  | 	Email             *string    `json:"email,omitempty"` | ||||||
|  | 	PublicRepos       *int       `json:"public_repos,omitempty"` | ||||||
|  | 	PublicGists       *int       `json:"public_gists,omitempty"` | ||||||
|  | 	Followers         *int       `json:"followers,omitempty"` | ||||||
|  | 	Following         *int       `json:"following,omitempty"` | ||||||
|  | 	CreatedAt         *time.Time `json:"created_at,omitempty"` | ||||||
|  | 	UpdatedAt         *time.Time `json:"updated_at,omitempty"` | ||||||
|  | 	TotalPrivateRepos *int       `json:"total_private_repos,omitempty"` | ||||||
|  | 	OwnedPrivateRepos *int       `json:"owned_private_repos,omitempty"` | ||||||
|  | 	PrivateGists      *int       `json:"private_gists,omitempty"` | ||||||
|  | 	DiskUsage         *int       `json:"disk_usage,omitempty"` | ||||||
|  | 	Collaborators     *int       `json:"collaborators,omitempty"` | ||||||
|  | 	BillingEmail      *string    `json:"billing_email,omitempty"` | ||||||
|  | 	Type              *string    `json:"type,omitempty"` | ||||||
|  | 	Plan              *Plan      `json:"plan,omitempty"` | ||||||
|  |  | ||||||
|  | 	// API URLs | ||||||
|  | 	URL              *string `json:"url,omitempty"` | ||||||
|  | 	EventsURL        *string `json:"events_url,omitempty"` | ||||||
|  | 	MembersURL       *string `json:"members_url,omitempty"` | ||||||
|  | 	PublicMembersURL *string `json:"public_members_url,omitempty"` | ||||||
|  | 	ReposURL         *string `json:"repos_url,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o Organization) String() string { | ||||||
|  | 	return Stringify(o) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Plan represents the payment plan for an account.  See plans at https://github.com/plans. | ||||||
|  | type Plan struct { | ||||||
|  | 	Name          *string `json:"name,omitempty"` | ||||||
|  | 	Space         *int    `json:"space,omitempty"` | ||||||
|  | 	Collaborators *int    `json:"collaborators,omitempty"` | ||||||
|  | 	PrivateRepos  *int    `json:"private_repos,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p Plan) String() string { | ||||||
|  | 	return Stringify(p) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List the organizations for a user.  Passing the empty string will list | ||||||
|  | // organizations for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/#list-user-organizations | ||||||
|  | func (s *OrganizationsService) List(user string, opt *ListOptions) ([]Organization, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if user != "" { | ||||||
|  | 		u = fmt.Sprintf("users/%v/orgs", user) | ||||||
|  | 	} else { | ||||||
|  | 		u = "user/orgs" | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	orgs := new([]Organization) | ||||||
|  | 	resp, err := s.client.Do(req, orgs) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *orgs, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get fetches an organization by name. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/#get-an-organization | ||||||
|  | func (s *OrganizationsService) Get(org string) (*Organization, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v", org) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	organization := new(Organization) | ||||||
|  | 	resp, err := s.client.Do(req, organization) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return organization, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Edit an organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/#edit-an-organization | ||||||
|  | func (s *OrganizationsService) Edit(name string, org *Organization) (*Organization, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v", name) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, org) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	o := new(Organization) | ||||||
|  | 	resp, err := s.client.Do(req, o) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return o, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										230
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/orgs_members.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/orgs_members.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,230 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | // Membership represents the status of a user's membership in an organization or team. | ||||||
|  | type Membership struct { | ||||||
|  | 	URL *string `json:"url,omitempty"` | ||||||
|  |  | ||||||
|  | 	// State is the user's status within the organization or team. | ||||||
|  | 	// Possible values are: "active", "pending" | ||||||
|  | 	State *string `json:"state,omitempty"` | ||||||
|  |  | ||||||
|  | 	// TODO(willnorris): add docs | ||||||
|  | 	Role *string `json:"role,omitempty"` | ||||||
|  |  | ||||||
|  | 	// For organization membership, the API URL of the organization. | ||||||
|  | 	OrganizationURL *string `json:"organization_url,omitempty"` | ||||||
|  |  | ||||||
|  | 	// For organization membership, the organization the membership is for. | ||||||
|  | 	Organization *Organization `json:"organization,omitempty"` | ||||||
|  |  | ||||||
|  | 	// For organization membership, the user the membership is for. | ||||||
|  | 	User *User `json:"user,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m Membership) String() string { | ||||||
|  | 	return Stringify(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListMembersOptions specifies optional parameters to the | ||||||
|  | // OrganizationsService.ListMembers method. | ||||||
|  | type ListMembersOptions struct { | ||||||
|  | 	// If true (or if the authenticated user is not an owner of the | ||||||
|  | 	// organization), list only publicly visible members. | ||||||
|  | 	PublicOnly bool `url:"-"` | ||||||
|  |  | ||||||
|  | 	// Filter members returned in the list.  Possible values are: | ||||||
|  | 	// 2fa_disabled, all.  Default is "all". | ||||||
|  | 	Filter string `url:"filter,omitempty"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListMembers lists the members for an organization.  If the authenticated | ||||||
|  | // user is an owner of the organization, this will return both concealed and | ||||||
|  | // public members, otherwise it will only return public members. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/members/#members-list | ||||||
|  | func (s *OrganizationsService) ListMembers(org string, opt *ListMembersOptions) ([]User, *Response, error) { | ||||||
|  | 	var u string | ||||||
|  | 	if opt != nil && opt.PublicOnly { | ||||||
|  | 		u = fmt.Sprintf("orgs/%v/public_members", org) | ||||||
|  | 	} else { | ||||||
|  | 		u = fmt.Sprintf("orgs/%v/members", org) | ||||||
|  | 	} | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	members := new([]User) | ||||||
|  | 	resp, err := s.client.Do(req, members) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return *members, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsMember checks if a user is a member of an organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/members/#check-membership | ||||||
|  | func (s *OrganizationsService) IsMember(org, user string) (bool, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/members/%v", org, user) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp, err := s.client.Do(req, nil) | ||||||
|  | 	member, err := parseBoolResponse(err) | ||||||
|  | 	return member, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsPublicMember checks if a user is a public member of an organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/members/#check-public-membership | ||||||
|  | func (s *OrganizationsService) IsPublicMember(org, user string) (bool, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp, err := s.client.Do(req, nil) | ||||||
|  | 	member, err := parseBoolResponse(err) | ||||||
|  | 	return member, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RemoveMember removes a user from all teams of an organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/members/#remove-a-member | ||||||
|  | func (s *OrganizationsService) RemoveMember(org, user string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/members/%v", org, user) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PublicizeMembership publicizes a user's membership in an organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/members/#publicize-a-users-membership | ||||||
|  | func (s *OrganizationsService) PublicizeMembership(org, user string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) | ||||||
|  | 	req, err := s.client.NewRequest("PUT", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ConcealMembership conceals a user's membership in an organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: http://developer.github.com/v3/orgs/members/#conceal-a-users-membership | ||||||
|  | func (s *OrganizationsService) ConcealMembership(org, user string) (*Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) | ||||||
|  | 	req, err := s.client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s.client.Do(req, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListOrgMembershipsOptions specifies optional parameters to the | ||||||
|  | // OrganizationsService.ListOrgMemberships method. | ||||||
|  | type ListOrgMembershipsOptions struct { | ||||||
|  | 	// Filter memberships to include only those withe the specified state. | ||||||
|  | 	// Possible values are: "active", "pending". | ||||||
|  | 	State string `url:"state,omitempty"` | ||||||
|  |  | ||||||
|  | 	ListOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListOrgMemberships lists the organization memberships for the authenticated user. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/orgs/members/#list-your-organization-memberships | ||||||
|  | func (s *OrganizationsService) ListOrgMemberships(opt *ListOrgMembershipsOptions) ([]Membership, *Response, error) { | ||||||
|  | 	u := "user/memberships/orgs" | ||||||
|  | 	u, err := addOptions(u, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: remove custom Accept header when this API fully launches | ||||||
|  | 	req.Header.Set("Accept", mediaTypeMembershipPreview) | ||||||
|  |  | ||||||
|  | 	var memberships []Membership | ||||||
|  | 	resp, err := s.client.Do(req, &memberships) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return memberships, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetOrgMembership gets the membership for the authenticated user for the | ||||||
|  | // specified organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/orgs/members/#get-your-organization-membership | ||||||
|  | func (s *OrganizationsService) GetOrgMembership(org string) (*Membership, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("user/memberships/orgs/%v", org) | ||||||
|  | 	req, err := s.client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: remove custom Accept header when this API fully launches | ||||||
|  | 	req.Header.Set("Accept", mediaTypeMembershipPreview) | ||||||
|  |  | ||||||
|  | 	membership := new(Membership) | ||||||
|  | 	resp, err := s.client.Do(req, membership) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return membership, resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EditOrgMembership edits the membership for the authenticated user for the | ||||||
|  | // specified organization. | ||||||
|  | // | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership | ||||||
|  | func (s *OrganizationsService) EditOrgMembership(org string, membership *Membership) (*Membership, *Response, error) { | ||||||
|  | 	u := fmt.Sprintf("user/memberships/orgs/%v", org) | ||||||
|  | 	req, err := s.client.NewRequest("PATCH", u, membership) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: remove custom Accept header when this API fully launches | ||||||
|  | 	req.Header.Set("Accept", mediaTypeMembershipPreview) | ||||||
|  |  | ||||||
|  | 	m := new(Membership) | ||||||
|  | 	resp, err := s.client.Do(req, m) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return m, resp, err | ||||||
|  | } | ||||||
							
								
								
									
										292
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/orgs_members_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								Godeps/_workspace/src/github.com/google/go-github/github/orgs_members_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,292 @@ | |||||||
|  | // Copyright 2013 The go-github AUTHORS. All rights reserved. | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package github | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_ListMembers(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/members", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"filter": "2fa_disabled", | ||||||
|  | 			"page":   "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListMembersOptions{ | ||||||
|  | 		PublicOnly:  false, | ||||||
|  | 		Filter:      "2fa_disabled", | ||||||
|  | 		ListOptions: ListOptions{Page: 2}, | ||||||
|  | 	} | ||||||
|  | 	members, _, err := client.Organizations.ListMembers("o", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.ListMembers returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []User{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(members, want) { | ||||||
|  | 		t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_ListMembers_invalidOrg(t *testing.T) { | ||||||
|  | 	_, _, err := client.Organizations.ListMembers("%", nil) | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_ListMembers_public(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/public_members", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		fmt.Fprint(w, `[{"id":1}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListMembersOptions{PublicOnly: true} | ||||||
|  | 	members, _, err := client.Organizations.ListMembers("o", opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.ListMembers returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []User{{ID: Int(1)}} | ||||||
|  | 	if !reflect.DeepEqual(members, want) { | ||||||
|  | 		t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_IsMember(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNoContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	member, _, err := client.Organizations.IsMember("o", "u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.IsMember returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := true; member != want { | ||||||
|  | 		t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensure that a 404 response is interpreted as "false" and not an error | ||||||
|  | func TestOrganizationsService_IsMember_notMember(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	member, _, err := client.Organizations.IsMember("o", "u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.IsMember returned error: %+v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := false; member != want { | ||||||
|  | 		t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensure that a 400 response is interpreted as an actual error, and not simply | ||||||
|  | // as "false" like the above case of a 404 | ||||||
|  | func TestOrganizationsService_IsMember_error(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		http.Error(w, "BadRequest", http.StatusBadRequest) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	member, _, err := client.Organizations.IsMember("o", "u") | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected HTTP 400 response") | ||||||
|  | 	} | ||||||
|  | 	if want := false; member != want { | ||||||
|  | 		t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_IsMember_invalidOrg(t *testing.T) { | ||||||
|  | 	_, _, err := client.Organizations.IsMember("%", "u") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_IsPublicMember(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNoContent) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	member, _, err := client.Organizations.IsPublicMember("o", "u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.IsPublicMember returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := true; member != want { | ||||||
|  | 		t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensure that a 404 response is interpreted as "false" and not an error | ||||||
|  | func TestOrganizationsService_IsPublicMember_notMember(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	member, _, err := client.Organizations.IsPublicMember("o", "u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.IsPublicMember returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if want := false; member != want { | ||||||
|  | 		t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensure that a 400 response is interpreted as an actual error, and not simply | ||||||
|  | // as "false" like the above case of a 404 | ||||||
|  | func TestOrganizationsService_IsPublicMember_error(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		http.Error(w, "BadRequest", http.StatusBadRequest) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	member, _, err := client.Organizations.IsPublicMember("o", "u") | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Errorf("Expected HTTP 400 response") | ||||||
|  | 	} | ||||||
|  | 	if want := false; member != want { | ||||||
|  | 		t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_IsPublicMember_invalidOrg(t *testing.T) { | ||||||
|  | 	_, _, err := client.Organizations.IsPublicMember("%", "u") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_RemoveMember(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "DELETE") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	_, err := client.Organizations.RemoveMember("o", "u") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.RemoveMember returned error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_RemoveMember_invalidOrg(t *testing.T) { | ||||||
|  | 	_, err := client.Organizations.RemoveMember("%", "u") | ||||||
|  | 	testURLParseError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_ListOrgMemberships(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/memberships/orgs", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testHeader(t, r, "Accept", mediaTypeMembershipPreview) | ||||||
|  | 		testFormValues(t, r, values{ | ||||||
|  | 			"state": "active", | ||||||
|  | 			"page":  "2", | ||||||
|  | 		}) | ||||||
|  | 		fmt.Fprint(w, `[{"url":"u"}]`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	opt := &ListOrgMembershipsOptions{ | ||||||
|  | 		State:       "active", | ||||||
|  | 		ListOptions: ListOptions{Page: 2}, | ||||||
|  | 	} | ||||||
|  | 	memberships, _, err := client.Organizations.ListOrgMemberships(opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.ListOrgMemberships returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := []Membership{{URL: String("u")}} | ||||||
|  | 	if !reflect.DeepEqual(memberships, want) { | ||||||
|  | 		t.Errorf("Organizations.ListOrgMemberships returned %+v, want %+v", memberships, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_GetOrgMembership(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		testMethod(t, r, "GET") | ||||||
|  | 		testHeader(t, r, "Accept", mediaTypeMembershipPreview) | ||||||
|  | 		fmt.Fprint(w, `{"url":"u"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	membership, _, err := client.Organizations.GetOrgMembership("o") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.GetOrgMembership returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Membership{URL: String("u")} | ||||||
|  | 	if !reflect.DeepEqual(membership, want) { | ||||||
|  | 		t.Errorf("Organizations.GetOrgMembership returned %+v, want %+v", membership, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOrganizationsService_EditOrgMembership(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  |  | ||||||
|  | 	input := &Membership{State: String("active")} | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		v := new(Membership) | ||||||
|  | 		json.NewDecoder(r.Body).Decode(v) | ||||||
|  |  | ||||||
|  | 		testMethod(t, r, "PATCH") | ||||||
|  | 		testHeader(t, r, "Accept", mediaTypeMembershipPreview) | ||||||
|  | 		if !reflect.DeepEqual(v, input) { | ||||||
|  | 			t.Errorf("Request body = %+v, want %+v", v, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Fprint(w, `{"url":"u"}`) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	membership, _, err := client.Organizations.EditOrgMembership("o", input) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Organizations.EditOrgMembership returned error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	want := &Membership{URL: String("u")} | ||||||
|  | 	if !reflect.DeepEqual(membership, want) { | ||||||
|  | 		t.Errorf("Organizations.EditOrgMembership returned %+v, want %+v", membership, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Mitchell Hashimoto
					Mitchell Hashimoto