<commit_before>// Copyright ©2012 The bíogo.bam 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 bgzf

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"io"
)

// Reader implements BGZF blocked gzip decompression.
type Reader struct {
	gzip.Header
	r io.Reader

	// head serialises access to the underlying
	// io.Reader.
	head chan *countReader

	// lastChunk is the virtual file offset
	// interval of the last successful read
	// or seek operation.
	lastChunk Chunk

	// nextBase is the file offset of the
	// block following the current block.
	nextBase int64

	active *decompressor

	// Cache is the Reader block cache. If Cache is not nil,
	// the cache is queried for blocks before an attempt to
	// read from the underlying io.Reader.
	Cache Cache

	err error
}

type decompressor struct {
	owner *Reader

	gz gzip.Reader

	cr   *countReader
	mark int64 // Offset at start of useUnderlying.

	// Current block size.
	blockSize int

	// Buffered compressed data from read ahead.
	i   int // Current position in buffered data.
	n   int // Total size of buffered data.
	buf [MaxBlockSize]byte

	// Decompressed data.
	decompressed Block

	err error
}

// countReader wraps flate.Reader, adding support for querying current offset.
type countReader struct {
	// Underlying Reader.
	fr flate.Reader

	// Offset within the underlying reader.
	off int64
}

// newCountReader returns a new countReader.
func newCountReader(r io.Reader) *countReader {
	switch r := r.(type) {
	case *countReader:
		panic("bgzf: illegal use of internal type")
	case flate.Reader:
		return &countReader{fr: r}
	default:
		return &countReader{fr: bufio.NewReader(r)}
	}
}

// Read is required to satisfy flate.Reader.
func (r *countReader) Read(p []byte) (int, error) {
	n, err := r.fr.Read(p)
	r.off += int64(n)
	return n, err
}

// ReadByte is required to satisfy flate.Reader.
func (r *countReader) ReadByte() (byte, error) {
	b, err := r.fr.ReadByte()
	if err == nil {
		r.off++
	}
	return b, err
}

// offset returns the current offset in the underlying reader.
func (r *countReader) offset() int64 { return r.off }

// seek moves the countReader to the specified offset using rs as the
// underlying reader.
func (r *countReader) seek(rs io.ReadSeeker, off int64) error {
	_, err := rs.Seek(off, 0)
	if err != nil {
		return err
	}

	type reseter interface {
		Reset(io.Reader)
	}
	switch cr := r.fr.(type) {
	case reseter:
		cr.Reset(rs)
	default:
		r.fr = newCountReader(rs)
	}
	r.off = off

	return nil
}

func newDecompressor() *decompressor { return &decompressor{} }

// init initialises a decompressor to use the provided flate.Reader.
func (d *decompressor) init(cr *countReader) (*decompressor, error) {
	defer d.releaseHead()

	d.cr = cr
	d.useUnderlying()
	err := d.gz.Reset(d)
	if err != nil {
		d.blockSize = -1
		return d, err
	}
	d.blockSize = expectedBlockSize(d.gz.Header)
	if d.blockSize < 0 {
		return d, ErrNoBlockSize
	}
	d.owner.Header = d.gz.Header

	return d, d.readAhead()
}

// acquireHead gains the read head from the decompressor's owner.
func (d *decompressor) acquireHead() {
	d.cr = <-d.owner.head
}

// releaseHead releases the read head back to the decompressor's owner.
func (d *decompressor) releaseHead() {
	d.owner.head <- d.cr
	d.cr = nil // Defensively zero the reader.
}

// lazyBlock conditionally creates a ready to use Block and returns whether
// the Block subsequently held by the decompressor needs to be reset before
// being filled.
func (d *decompressor) lazyBlock() bool {
	if d.decompressed == nil {
		if w, ok := d.owner.Cache.(Wrapper); ok {
			d.decompressed = w.Wrap(&block{owner: d.owner})
		} else {
			d.decompressed = &block{owner: d.owner}
		}
		return false
	}
	if !d.decompressed.ownedBy(d.owner) {
		d.decompressed.setOwner(d.owner)
	}
	return true
}

// isBuffered returns whether the decompressor has buffered compressed data.
func (d *decompressor) isBuffered() bool { return d.n != 0 }

// Read provides the Read method for the decompressor's gzip.Reader.
func (d *decompressor) Read(p []byte) (int, error) {
	var (
		n   int
		err error
	)
	if d.isBuffered() {
		if d.i >= d.n {
			return 0, io.EOF
		}
		if n := d.n - d.i; len(p) > n {
			p = p[:n]
		}
		n = copy(p, d.buf[d.i:])
		d.i += n
	} else {
		n, err = d.cr.Read(p)
	}
	return n, err
}

// ReadByte provides the ReadByte method for the decompressor's gzip.Reader.
func (d *decompressor) ReadByte() (byte, error) {
	var (
		b   byte
		err error
	)
	if d.isBuffered() {
		if d.i == d.n {
			return 0, io.EOF
		}
		b = d.buf[d.i]
		d.i++
	} else {
		b, err = d.cr.ReadByte()
	}
	return b, err
}

// reset makes the decompressor ready for reading decompressed data
// from its Block. It checks if there is a cached Block for the nextBase,
// otherwise it seeks to the correct location if decompressor is not
// correctly positioned, and then reads the compressed data and fills
// the decompressed Block.
func (d *decompressor) reset() {
	needReset := d.lazyBlock()

	if d.gotBlockFor(d.owner.nextBase) {
		return
	}

	d.acquireHead()
	if needReset && d.cr.offset() != d.owner.nextBase {
		// It should not be possible for the expected next block base
		// to be out of register with the count reader unless Seek
		// has been called, so we know the base reader must be an
		// io.ReadSeeker.
		d.err = d.cr.seek(d.owner.r.(io.ReadSeeker), d.owner.nextBase)
		if d.err != nil {
			d.releaseHead()
			return
		}
	}

	d.err = d.fill(needReset)
}

// seekRead is the seeking equivalent of reset. It checks if the seek
// is within the current Block and if not whether the seeked Block is
// cached, returning successfully if either is true. Otherwise it seeks
// to the offset and fills the decompressed Block.
func (d *decompressor) seekRead(r io.ReadSeeker, off int64) {
	d.lazyBlock()

	if off == d.decompressed.Base() && d.decompressed.hasData() {
		return
	}

	if d.gotBlockFor(off) {
		return
	}

	d.acquireHead()
	d.err = d.cr.seek(r, off)
	if d.err != nil {
		d.releaseHead()
		return
	}

	d.err = d.fill(true)
}

// gotBlockFor returns true if the decompressor has access to a cache
// and that cache holds the block with given base and the correct
// owner, otherwise it returns false.
// gotBlockFor has side effects of recovering the block and putting
// the currently active block into the cache. If the cache returns
// a block owned by another reader, it is discarded.
func (d *decompressor) gotBlockFor(base int64) bool {
	if d.owner.Cache != nil {
		dec := d.decompressed
		if blk := d.owner.Cache.Get(base); blk != nil && blk.ownedBy(d.owner) {
			if dec != nil && dec.hasData() {
				// TODO(kortschak): Under some conditions, e.g. FIFO
				// cache we will be discarding a non-nil evicted Block.
				// Consider retaining these in a sync.Pool.
				d.owner.Cache.Put(dec)
			}
			if d.err = blk.seek(0); d.err == nil {
				d.decompressed = blk
				d.owner.nextBase = blk.nextBase()
				return true
			}
		}
		if dec != nil && dec.hasData() {
			dec, retained := d.owner.Cache.Put(dec)
			if retained {
				d.decompressed = dec
				d.lazyBlock()
			}
		}
	}

	return false
}

// useUnderlying set the decompressor to Read from the underlying flate.Reader.
// It marks the offset at from where the underlying reader has been used.
func (d *decompressor) useUnderlying() { d.n = 0; d.mark = d.cr.offset() }

// readAhead reads compressed data into the decompressor buffer. It reads until
// the underlying flate.Reader is positioned at the end of the gzip member in
// which the readAhead call was made. readAhead should not be called unless the
// decompressor has had init called successfully.
func (d *decompressor) readAhead() error {
	d.i = 0
	var err error
	d.n, err = io.ReadFull(d.cr, d.buf[:d.blockSize-d.deltaOffset()])
	return err
}

// deltaOffset returns the number of bytes read since the last call to
// useUnderlying.
func (d *decompressor) deltaOffset() int { return int(d.cr.offset() - d.mark) }

// fill decompresses data into the decompressor's Block. If reset is true
// it first initialises the decompressor using its current flate.Reader
// and buffers the compressed data.
func (d *decompressor) fill(reset bool) error {
	dec := d.decompressed

	if !reset {
		d.releaseHead()
	} else {
		dec.setBase(d.cr.offset())
		_, err := d.init(d.cr)
		if err != nil {
			return err
		}
	}

	dec.setHeader(d.gz.Header)
	return dec.readFrom(&d.gz)
}

// expectedBlock size returns the size of the BGZF conformant gzip member.
// It returns -1 if no BGZF block size field is found.
func expectedBlockSize(h gzip.Header) int {
	i := bytes.Index(h.Extra, bgzfExtraPrefix)
	if i < 0 || i+5 >= len(h.Extra) {
		return -1
	}
	return (int(h.Extra[i+4]) | int(h.Extra[i+5])<<8) + 1
}

// NewReader returns a new BGZF reader.
//
// The number of concurrent read decompressors is specified by
// rd (currently ignored).
func NewReader(r io.Reader, rd int) (*Reader, error) {
	bg := &Reader{
		r:      r,
		active: newDecompressor(),
		head:   make(chan *countReader, 1),
	}
	bg.active.owner = bg
	_, err := bg.active.init(newCountReader(r))
	if err != nil {
		return nil, err
	}

	return bg, nil
}

// Offset is a BGZF virtual offset.
type Offset struct {
	File  int64
	Block uint16
}

// Chunk is a region of a BGZF file.
type Chunk struct {
	Begin Offset
	End   Offset
}

// Seek performs a seek operation to the given virtual offset.
func (bg *Reader) Seek(off Offset) error {
	rs, ok := bg.r.(io.ReadSeeker)
	if !ok {
		return ErrNotASeeker
	}

	bg.active.seekRead(rs, off.File)
	bg.err = bg.active.err
	if bg.err != nil {
		return bg.err
	}
	bg.nextBase = bg.active.decompressed.nextBase()

	bg.err = bg.active.decompressed.seek(int64(off.Block))
	if bg.err == nil {
		bg.lastChunk = Chunk{Begin: off, End: off}
	}

	return bg.err
}

// LastChunk returns the region of the BGZF file read by the last read
// operation or the resulting virtual offset of the last successful
// seek operation.
func (bg *Reader) LastChunk() Chunk { return bg.lastChunk }

// Close closes the reader and releases resources.
func (bg *Reader) Close() error {
	bg.Cache = nil
	return bg.active.gz.Close()
}

// Read implements the io.Reader interface.
func (bg *Reader) Read(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}

	dec := bg.active.decompressed

	if dec == nil {
		bg.active.lazyBlock()
		dec = bg.active.decompressed
		bg.err = dec.readFrom(&bg.active.gz)
		if bg.err != nil {
			return 0, bg.err
		}
		dec.setHeader(bg.active.gz.Header)
		bg.nextBase = dec.nextBase()
	}

	for dec.len() == 0 {
		dec, bg.err = bg.resetDecompressor()
		if bg.err != nil {
			return 0, bg.err
		}
	}

	bg.lastChunk.Begin = dec.txOffset()

	var n int
	for n < len(p) && bg.err == nil {
		var _n int
		_n, bg.err = dec.Read(p[n:])
		if _n > 0 {
			bg.lastChunk.End = dec.txOffset()
		}
		n += _n
		if bg.err == io.EOF {
			if n == len(p) {
				bg.err = nil
				break
			}

			dec, bg.err = bg.resetDecompressor()
			if bg.err != nil {
				break
			}
		}
	}

	return n, bg.err
}

func (bg *Reader) resetDecompressor() (Block, error) {
	bg.active.reset()
	if bg.active.err != nil {
		return nil, bg.active.err
	}
	bg.nextBase = bg.active.decompressed.nextBase()
	return bg.active.decompressed, nil
}
<commit_msg>Conditional initialisation is no longer required<commit_after>// Copyright ©2012 The bíogo.bam 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 bgzf

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"io"
)

// Reader implements BGZF blocked gzip decompression.
type Reader struct {
	gzip.Header
	r io.Reader

	// head serialises access to the underlying
	// io.Reader.
	head chan *countReader

	// lastChunk is the virtual file offset
	// interval of the last successful read
	// or seek operation.
	lastChunk Chunk

	// nextBase is the file offset of the
	// block following the current block.
	nextBase int64

	active *decompressor

	// Cache is the Reader block cache. If Cache is not nil,
	// the cache is queried for blocks before an attempt to
	// read from the underlying io.Reader.
	Cache Cache

	err error
}

type decompressor struct {
	owner *Reader

	gz gzip.Reader

	cr   *countReader
	mark int64 // Offset at start of useUnderlying.

	// Current block size.
	blockSize int

	// Buffered compressed data from read ahead.
	i   int // Current position in buffered data.
	n   int // Total size of buffered data.
	buf [MaxBlockSize]byte

	// Decompressed data.
	decompressed Block

	err error
}

// countReader wraps flate.Reader, adding support for querying current offset.
type countReader struct {
	// Underlying Reader.
	fr flate.Reader

	// Offset within the underlying reader.
	off int64
}

// newCountReader returns a new countReader.
func newCountReader(r io.Reader) *countReader {
	switch r := r.(type) {
	case *countReader:
		panic("bgzf: illegal use of internal type")
	case flate.Reader:
		return &countReader{fr: r}
	default:
		return &countReader{fr: bufio.NewReader(r)}
	}
}

// Read is required to satisfy flate.Reader.
func (r *countReader) Read(p []byte) (int, error) {
	n, err := r.fr.Read(p)
	r.off += int64(n)
	return n, err
}

// ReadByte is required to satisfy flate.Reader.
func (r *countReader) ReadByte() (byte, error) {
	b, err := r.fr.ReadByte()
	if err == nil {
		r.off++
	}
	return b, err
}

// offset returns the current offset in the underlying reader.
func (r *countReader) offset() int64 { return r.off }

// seek moves the countReader to the specified offset using rs as the
// underlying reader.
func (r *countReader) seek(rs io.ReadSeeker, off int64) error {
	_, err := rs.Seek(off, 0)
	if err != nil {
		return err
	}

	type reseter interface {
		Reset(io.Reader)
	}
	switch cr := r.fr.(type) {
	case reseter:
		cr.Reset(rs)
	default:
		r.fr = newCountReader(rs)
	}
	r.off = off

	return nil
}

func newDecompressor() *decompressor { return &decompressor{} }

// init initialises a decompressor to use the provided flate.Reader.
func (d *decompressor) init(cr *countReader) (*decompressor, error) {
	defer d.releaseHead()

	d.cr = cr
	d.useUnderlying()
	err := d.gz.Reset(d)
	if err != nil {
		d.blockSize = -1
		return d, err
	}
	d.blockSize = expectedBlockSize(d.gz.Header)
	if d.blockSize < 0 {
		return d, ErrNoBlockSize
	}
	d.owner.Header = d.gz.Header

	return d, d.readAhead()
}

// acquireHead gains the read head from the decompressor's owner.
func (d *decompressor) acquireHead() {
	d.cr = <-d.owner.head
}

// releaseHead releases the read head back to the decompressor's owner.
func (d *decompressor) releaseHead() {
	d.owner.head <- d.cr
	d.cr = nil // Defensively zero the reader.
}

// lazyBlock conditionally creates a ready to use Block.
func (d *decompressor) lazyBlock() {
	if d.decompressed == nil {
		if w, ok := d.owner.Cache.(Wrapper); ok {
			d.decompressed = w.Wrap(&block{owner: d.owner})
		} else {
			d.decompressed = &block{owner: d.owner}
		}
		return
	}
	if !d.decompressed.ownedBy(d.owner) {
		d.decompressed.setOwner(d.owner)
	}
}

// isBuffered returns whether the decompressor has buffered compressed data.
func (d *decompressor) isBuffered() bool { return d.n != 0 }

// Read provides the Read method for the decompressor's gzip.Reader.
func (d *decompressor) Read(p []byte) (int, error) {
	var (
		n   int
		err error
	)
	if d.isBuffered() {
		if d.i >= d.n {
			return 0, io.EOF
		}
		if n := d.n - d.i; len(p) > n {
			p = p[:n]
		}
		n = copy(p, d.buf[d.i:])
		d.i += n
	} else {
		n, err = d.cr.Read(p)
	}
	return n, err
}

// ReadByte provides the ReadByte method for the decompressor's gzip.Reader.
func (d *decompressor) ReadByte() (byte, error) {
	var (
		b   byte
		err error
	)
	if d.isBuffered() {
		if d.i == d.n {
			return 0, io.EOF
		}
		b = d.buf[d.i]
		d.i++
	} else {
		b, err = d.cr.ReadByte()
	}
	return b, err
}

// reset makes the decompressor ready for reading decompressed data
// from its Block. It checks if there is a cached Block for the nextBase,
// otherwise it seeks to the correct location if decompressor is not
// correctly positioned, and then reads the compressed data and fills
// the decompressed Block.
func (d *decompressor) reset() {
	d.lazyBlock()

	if d.gotBlockFor(d.owner.nextBase) {
		return
	}

	d.acquireHead()
	if d.cr.offset() != d.owner.nextBase {
		// It should not be possible for the expected next block base
		// to be out of register with the count reader unless Seek
		// has been called, so we know the base reader must be an
		// io.ReadSeeker.
		d.err = d.cr.seek(d.owner.r.(io.ReadSeeker), d.owner.nextBase)
		if d.err != nil {
			d.releaseHead()
			return
		}
	}

	d.err = d.fill()
}

// seekRead is the seeking equivalent of reset. It checks if the seek
// is within the current Block and if not whether the seeked Block is
// cached, returning successfully if either is true. Otherwise it seeks
// to the offset and fills the decompressed Block.
func (d *decompressor) seekRead(r io.ReadSeeker, off int64) {
	d.lazyBlock()

	if off == d.decompressed.Base() && d.decompressed.hasData() {
		return
	}

	if d.gotBlockFor(off) {
		return
	}

	d.acquireHead()
	d.err = d.cr.seek(r, off)
	if d.err != nil {
		d.releaseHead()
		return
	}

	d.err = d.fill()
}

// gotBlockFor returns true if the decompressor has access to a cache
// and that cache holds the block with given base and the correct
// owner, otherwise it returns false.
// gotBlockFor has side effects of recovering the block and putting
// the currently active block into the cache. If the cache returns
// a block owned by another reader, it is discarded.
func (d *decompressor) gotBlockFor(base int64) bool {
	if d.owner.Cache != nil {
		dec := d.decompressed
		if blk := d.owner.Cache.Get(base); blk != nil && blk.ownedBy(d.owner) {
			if dec != nil && dec.hasData() {
				// TODO(kortschak): Under some conditions, e.g. FIFO
				// cache we will be discarding a non-nil evicted Block.
				// Consider retaining these in a sync.Pool.
				d.owner.Cache.Put(dec)
			}
			if d.err = blk.seek(0); d.err == nil {
				d.decompressed = blk
				d.owner.nextBase = blk.nextBase()
				return true
			}
		}
		if dec != nil && dec.hasData() {
			dec, retained := d.owner.Cache.Put(dec)
			if retained {
				d.decompressed = dec
				d.lazyBlock()
			}
		}
	}

	return false
}

// useUnderlying set the decompressor to Read from the underlying flate.Reader.
// It marks the offset at from where the underlying reader has been used.
func (d *decompressor) useUnderlying() { d.n = 0; d.mark = d.cr.offset() }

// readAhead reads compressed data into the decompressor buffer. It reads until
// the underlying flate.Reader is positioned at the end of the gzip member in
// which the readAhead call was made. readAhead should not be called unless the
// decompressor has had init called successfully.
func (d *decompressor) readAhead() error {
	d.i = 0
	var err error
	d.n, err = io.ReadFull(d.cr, d.buf[:d.blockSize-d.deltaOffset()])
	return err
}

// deltaOffset returns the number of bytes read since the last call to
// useUnderlying.
func (d *decompressor) deltaOffset() int { return int(d.cr.offset() - d.mark) }

// fill decompresses data into the decompressor's Block.
func (d *decompressor) fill() error {
	dec := d.decompressed

	dec.setBase(d.cr.offset())
	_, err := d.init(d.cr)
	if err != nil {
		return err
	}

	dec.setHeader(d.gz.Header)
	return dec.readFrom(&d.gz)
}

// expectedBlock size returns the size of the BGZF conformant gzip member.
// It returns -1 if no BGZF block size field is found.
func expectedBlockSize(h gzip.Header) int {
	i := bytes.Index(h.Extra, bgzfExtraPrefix)
	if i < 0 || i+5 >= len(h.Extra) {
		return -1
	}
	return (int(h.Extra[i+4]) | int(h.Extra[i+5])<<8) + 1
}

// NewReader returns a new BGZF reader.
//
// The number of concurrent read decompressors is specified by
// rd (currently ignored).
func NewReader(r io.Reader, rd int) (*Reader, error) {
	bg := &Reader{
		r:      r,
		active: newDecompressor(),
		head:   make(chan *countReader, 1),
	}
	bg.active.owner = bg
	_, err := bg.active.init(newCountReader(r))
	if err != nil {
		return nil, err
	}

	return bg, nil
}

// Offset is a BGZF virtual offset.
type Offset struct {
	File  int64
	Block uint16
}

// Chunk is a region of a BGZF file.
type Chunk struct {
	Begin Offset
	End   Offset
}

// Seek performs a seek operation to the given virtual offset.
func (bg *Reader) Seek(off Offset) error {
	rs, ok := bg.r.(io.ReadSeeker)
	if !ok {
		return ErrNotASeeker
	}

	bg.active.seekRead(rs, off.File)
	bg.err = bg.active.err
	if bg.err != nil {
		return bg.err
	}
	bg.nextBase = bg.active.decompressed.nextBase()

	bg.err = bg.active.decompressed.seek(int64(off.Block))
	if bg.err == nil {
		bg.lastChunk = Chunk{Begin: off, End: off}
	}

	return bg.err
}

// LastChunk returns the region of the BGZF file read by the last read
// operation or the resulting virtual offset of the last successful
// seek operation.
func (bg *Reader) LastChunk() Chunk { return bg.lastChunk }

// Close closes the reader and releases resources.
func (bg *Reader) Close() error {
	bg.Cache = nil
	return bg.active.gz.Close()
}

// Read implements the io.Reader interface.
func (bg *Reader) Read(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}

	dec := bg.active.decompressed

	if dec == nil {
		bg.active.lazyBlock()
		dec = bg.active.decompressed
		bg.err = dec.readFrom(&bg.active.gz)
		if bg.err != nil {
			return 0, bg.err
		}
		dec.setHeader(bg.active.gz.Header)
		bg.nextBase = dec.nextBase()
	}

	for dec.len() == 0 {
		dec, bg.err = bg.resetDecompressor()
		if bg.err != nil {
			return 0, bg.err
		}
	}

	bg.lastChunk.Begin = dec.txOffset()

	var n int
	for n < len(p) && bg.err == nil {
		var _n int
		_n, bg.err = dec.Read(p[n:])
		if _n > 0 {
			bg.lastChunk.End = dec.txOffset()
		}
		n += _n
		if bg.err == io.EOF {
			if n == len(p) {
				bg.err = nil
				break
			}

			dec, bg.err = bg.resetDecompressor()
			if bg.err != nil {
				break
			}
		}
	}

	return n, bg.err
}

func (bg *Reader) resetDecompressor() (Block, error) {
	bg.active.reset()
	if bg.active.err != nil {
		return nil, bg.active.err
	}
	bg.nextBase = bg.active.decompressed.nextBase()
	return bg.active.decompressed, nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"time"

	"github.com/NyaaPantsu/nyaa/config"
)

// Notification model
type Notification struct {
	ID         uint
	Content    string
	Read       bool
	Identifier string
	URL        string
	Expire     time.Time
	UserID     uint
	//	User *User `gorm:"AssociationForeignKey:UserID;ForeignKey:user_id"` // Don't think that we need it here
}

// NewNotification : Create a new notification
func NewNotification(identifier string, c string, url string) Notification {
	return Notification{Identifier: identifier, Content: c, URL: url}
}

// TableName : Return the name of notification table
func (n *Notification) TableName() string {
	return config.Get().Models.NotificationsTableName
}

// Delete a notification
func (n *Notification) Delete() error {
	if n.ID == 0 {
		return errors.New("Can't delete a non existent notification")
	}
	ORM.Where("id = ?", n.ID).Delete(n)
	return nil
}
<commit_msg>Update notification.go<commit_after>package models

import (
	"errors"
	"time"

	"github.com/NyaaPantsu/nyaa/config"
)

// Notification model
type Notification struct {
	ID         uint
	Content    string
	Read       bool
	Identifier string
	URL        string
	Expire     time.Time
	Date       time.Time
	UserID     uint
	//	User *User `gorm:"AssociationForeignKey:UserID;ForeignKey:user_id"` // Don't think that we need it here
}

// NewNotification : Create a new notification
func NewNotification(identifier string, c string, url string) Notification {
	return Notification{Identifier: identifier, Content: c, URL: url, Date: time.Now()}
}

// TableName : Return the name of notification table
func (n *Notification) TableName() string {
	return config.Get().Models.NotificationsTableName
}

// Delete a notification
func (n *Notification) Delete() error {
	if n.ID == 0 {
		return errors.New("Can't delete a non existent notification")
	}
	ORM.Where("id = ?", n.ID).Delete(n)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package transform

import (
	"bytes"
	"testing"

	"github.com/spf13/hugo/helpers"
)

func TestHugoGeneratorInject(t *testing.T) {
	hugoGeneratorTag = "META"
	for i, this := range []struct {
		in     string
		expect string
	}{
		{`
	<foo />
`, `
	META
	<foo />
`},
		{`
	<foo />
`, `
	META
	<foo />
`},
		{`<head><meta name="generator" content="Jekyll" /></head>`, `<head><meta name="generator" content="Jekyll" /></head>`},
		{`<head><meta name='generator' content='Jekyll' /></head>`, `<head><meta name='generator' content='Jekyll' /></head>`},
		{`<head><meta name=generator content=Jekyll /></head>`, `<head><meta name=generator content=Jekyll /></head>`},
		{`<head><META     NAME="GENERATOR" content="Jekyll" /></head>`, `<head><META     NAME="GENERATOR" content="Jekyll" /></head>`},
		{"", ""},
		{"", ""},
		{"", "\n\tMETA"},
	} {
		in := helpers.StringToReader(this.in)
		out := new(bytes.Buffer)

		tr := NewChain(HugoGeneratorInject)
		tr.Apply(out, in, []byte(""))

		if out.String() != this.expect {
			t.Errorf("[%d] Expected \n%q got \n%q", i, this.expect, out.String())
		}
	}

}
<commit_msg>transform: Don't use helpers.StringToReader<commit_after>// Copyright 2016 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package transform

import (
	"bytes"
	"strings"
	"testing"
)

func TestHugoGeneratorInject(t *testing.T) {
	hugoGeneratorTag = "META"
	for i, this := range []struct {
		in     string
		expect string
	}{
		{`
	<foo />
`, `
	META
	<foo />
`},
		{`
	<foo />
`, `
	META
	<foo />
`},
		{`<head><meta name="generator" content="Jekyll" /></head>`, `<head><meta name="generator" content="Jekyll" /></head>`},
		{`<head><meta name='generator' content='Jekyll' /></head>`, `<head><meta name='generator' content='Jekyll' /></head>`},
		{`<head><meta name=generator content=Jekyll /></head>`, `<head><meta name=generator content=Jekyll /></head>`},
		{`<head><META     NAME="GENERATOR" content="Jekyll" /></head>`, `<head><META     NAME="GENERATOR" content="Jekyll" /></head>`},
		{"", ""},
		{"", ""},
		{"", "\n\tMETA"},
	} {
		in := strings.NewReader(this.in)
		out := new(bytes.Buffer)

		tr := NewChain(HugoGeneratorInject)
		tr.Apply(out, in, []byte(""))

		if out.String() != this.expect {
			t.Errorf("[%d] Expected \n%q got \n%q", i, this.expect, out.String())
		}
	}

}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// 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.

package unsplash

import (
	"io/ioutil"
	"log"
	"math/rand"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestAllCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.All(nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(2, resp.NextPage)
	lastPage := resp.LastPage
	//check collections
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))

	opt := *defaultListOpt
	opt.Page = 2
	collections, resp, err = unsplash.Collections.All(&opt)
	assert.Nil(err)
	log.Println(err)
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(3, resp.NextPage)
	assert.Equal(1, resp.PrevPage)
	assert.Equal(lastPage, resp.LastPage)
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))

	collections, resp, err = unsplash.Collections.All(&ListOpt{PerPage: -1})
	assert.Nil(collections)
	assert.Nil(resp)
	assert.NotNil(err)
	_, ok := err.(*InvalidListOpt)
	assert.Equal(true, ok)

}

func TestFeaturedCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.Featured(nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(2, resp.NextPage)
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))
}

func TestCuratedCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.Curated(nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(2, resp.NextPage)
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))
}

func TestRelatedCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.Related("296", nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	assert.NotNil(collections)
	log.Println(resp)

	collections, resp, err = unsplash.Collections.Related("", nil)
	assert.NotNil(err)
	assert.Nil(collections)
	assert.Nil(resp)
}

func TestSimpleCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collection, resp, err := unsplash.Collections.Collection("910")
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collection)
	log.Println(resp)

	collection, resp, err = unsplash.Collections.Collection("")
	assert.NotNil(err)
	assert.Nil(collection)
	assert.Nil(resp)
}

func TestCreateCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	var opt CollectionOpt
	title := "Test42"
	opt.Title = &title
	collection, resp, err := unsplash.Collections.Create(&opt)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collection)

	collection, resp, err = unsplash.Collections.Create(nil)
	assert.Nil(resp)
	assert.Nil(collection)
	assert.NotNil(err)
}

func TestUpdateCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()

	//get a user's collection
	collections, resp, err := unsplash.Users.Collections("gopher", nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collections)
	collection := (*collections)[0]
	assert.NotNil(collection)
	log.Println(*collection.ID)
	//random title
	var opt CollectionOpt
	title := "Test43" + strconv.Itoa(rand.Int())
	opt.Title = &title
	col, resp, err := unsplash.Collections.Update(*collection.ID, &opt)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(col)

	col, resp, err = unsplash.Collections.Update(0, &opt)
	assert.Nil(resp)
	assert.Nil(col)
	assert.NotNil(err)

	col, resp, err = unsplash.Collections.Update(246, nil)
	assert.Nil(resp)
	assert.Nil(col)
	assert.NotNil(err)

	col, resp, err = unsplash.Collections.Update(0, nil)
	assert.Nil(resp)
	assert.Nil(col)
	assert.NotNil(err)
}

func TestDeleteCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	var opt CollectionOpt
	title := "Test42"
	opt.Title = &title
	collection, resp, err := unsplash.Collections.Create(&opt)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collection)

	resp, err = unsplash.Collections.Delete(*collection.ID)
	assert.NotNil(resp)
	assert.Nil(err)

	resp, err = unsplash.Collections.Delete(0)
	assert.NotNil(err)
	assert.Nil(resp)
}

func TestAddPhoto(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()

	//get a random photo
	photos, resp, err := unsplash.Photos.Random(nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(photos)
	assert.Equal(1, len(*photos))
	photo := (*photos)[0]
	assert.NotNil(photo)

	//get a user's collection
	collections, resp, err := unsplash.Users.Collections("gopher", nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collections)

	collection := (*collections)[0]
	assert.NotNil(collection)

	//add the photo
	resp, err = unsplash.Collections.AddPhoto(*collection.ID, *photo.ID)
	assert.Nil(err)
	assert.NotNil(resp)

	//empty things
	resp, err = unsplash.Collections.AddPhoto(0, "photoID")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.AddPhoto(296, "")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.AddPhoto(0, "")
	assert.NotNil(err)
	assert.Nil(resp)

}

func TestRemovePhoto(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()

	//get a random photo
	photos, resp, err := unsplash.Photos.Random(nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(photos)
	assert.Equal(1, len(*photos))
	photo := (*photos)[0]
	assert.NotNil(photo)

	//get a user's collection
	collections, resp, err := unsplash.Users.Collections("gopher", nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collections)

	collection := (*collections)[0]
	assert.NotNil(collection)

	//add the photo
	resp, err = unsplash.Collections.AddPhoto(*collection.ID, *photo.ID)
	assert.Nil(err)
	assert.NotNil(resp)

	//remove the photo
	_, _ = unsplash.Collections.RemovePhoto(*collection.ID, *photo.ID)
	// API is being unreliable at the moment. Returns 403 sometimes
	// could be because of back-to-back requests?
	// assert.Nil(err)
	// assert.NotNil(resp)

	//empty stuff
	//empty things
	resp, err = unsplash.Collections.RemovePhoto(0, "photoID")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.RemovePhoto(296, "")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.RemovePhoto(0, "")
	assert.NotNil(err)
	assert.Nil(resp)
}
<commit_msg>mock errors for CollectionService<commit_after>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// 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.

package unsplash

import (
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"strconv"
	"testing"

	"github.com/jarcoal/httpmock"
	"github.com/stretchr/testify/assert"
)

func TestAllCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.All(nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(2, resp.NextPage)
	lastPage := resp.LastPage
	//check collections
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))

	opt := *defaultListOpt
	opt.Page = 2
	collections, resp, err = unsplash.Collections.All(&opt)
	assert.Nil(err)
	log.Println(err)
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(3, resp.NextPage)
	assert.Equal(1, resp.PrevPage)
	assert.Equal(lastPage, resp.LastPage)
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))

	collections, resp, err = unsplash.Collections.All(&ListOpt{PerPage: -1})
	assert.Nil(collections)
	assert.Nil(resp)
	assert.NotNil(err)
	_, ok := err.(*InvalidListOpt)
	assert.Equal(true, ok)

}

func TestFeaturedCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.Featured(nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(2, resp.NextPage)
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))
}

func TestCuratedCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.Curated(nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	log.Println(resp)
	assert.Equal(true, resp.HasNextPage)
	assert.Equal(2, resp.NextPage)
	assert.NotNil(collections)
	assert.Equal(10, len(*collections))
}

func TestRelatedCollections(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collections, resp, err := unsplash.Collections.Related("296", nil)
	assert.Nil(err)
	//check pagination
	assert.NotNil(resp)
	assert.NotNil(collections)
	log.Println(resp)

	collections, resp, err = unsplash.Collections.Related("", nil)
	assert.NotNil(err)
	assert.Nil(collections)
	assert.Nil(resp)
}

func TestSimpleCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	collection, resp, err := unsplash.Collections.Collection("910")
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collection)
	log.Println(resp)

	collection, resp, err = unsplash.Collections.Collection("")
	assert.NotNil(err)
	assert.Nil(collection)
	assert.Nil(resp)
}

func TestCreateCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	var opt CollectionOpt
	title := "Test42"
	opt.Title = &title
	collection, resp, err := unsplash.Collections.Create(&opt)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collection)

	title = ""
	collection, resp, err = unsplash.Collections.Create(&opt)
	assert.Nil(resp)
	assert.Nil(collection)
	assert.NotNil(err)

	collection, resp, err = unsplash.Collections.Create(nil)
	assert.Nil(resp)
	assert.Nil(collection)
	assert.NotNil(err)
}

func TestUpdateCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()

	//get a user's collection
	collections, resp, err := unsplash.Users.Collections("gopher", nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collections)
	collection := (*collections)[0]
	assert.NotNil(collection)
	log.Println(*collection.ID)
	//random title
	var opt CollectionOpt
	title := "Test43" + strconv.Itoa(rand.Int())
	opt.Title = &title
	col, resp, err := unsplash.Collections.Update(*collection.ID, &opt)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(col)

	col, resp, err = unsplash.Collections.Update(0, &opt)
	assert.Nil(resp)
	assert.Nil(col)
	assert.NotNil(err)

	col, resp, err = unsplash.Collections.Update(246, nil)
	assert.Nil(resp)
	assert.Nil(col)
	assert.NotNil(err)

	col, resp, err = unsplash.Collections.Update(0, nil)
	assert.Nil(resp)
	assert.Nil(col)
	assert.NotNil(err)
}

func TestDeleteCollection(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()
	var opt CollectionOpt
	title := "Test42"
	opt.Title = &title
	collection, resp, err := unsplash.Collections.Create(&opt)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collection)

	resp, err = unsplash.Collections.Delete(*collection.ID)
	assert.NotNil(resp)
	assert.Nil(err)

	resp, err = unsplash.Collections.Delete(0)
	assert.NotNil(err)
	assert.Nil(resp)
}

func TestAddPhoto(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()

	//get a random photo
	photos, resp, err := unsplash.Photos.Random(nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(photos)
	assert.Equal(1, len(*photos))
	photo := (*photos)[0]
	assert.NotNil(photo)

	//get a user's collection
	collections, resp, err := unsplash.Users.Collections("gopher", nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collections)

	collection := (*collections)[0]
	assert.NotNil(collection)

	//add the photo
	resp, err = unsplash.Collections.AddPhoto(*collection.ID, *photo.ID)
	assert.Nil(err)
	assert.NotNil(resp)

	//empty things
	resp, err = unsplash.Collections.AddPhoto(0, "photoID")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.AddPhoto(296, "")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.AddPhoto(0, "")
	assert.NotNil(err)
	assert.Nil(resp)

}

func TestRemovePhoto(T *testing.T) {
	assert := assert.New(T)
	log.SetOutput(ioutil.Discard)
	unsplash := setup()

	//get a random photo
	photos, resp, err := unsplash.Photos.Random(nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(photos)
	assert.Equal(1, len(*photos))
	photo := (*photos)[0]
	assert.NotNil(photo)

	//get a user's collection
	collections, resp, err := unsplash.Users.Collections("gopher", nil)
	assert.Nil(err)
	assert.NotNil(resp)
	assert.NotNil(collections)

	collection := (*collections)[0]
	assert.NotNil(collection)

	//add the photo
	resp, err = unsplash.Collections.AddPhoto(*collection.ID, *photo.ID)
	assert.Nil(err)
	assert.NotNil(resp)

	//remove the photo
	_, _ = unsplash.Collections.RemovePhoto(*collection.ID, *photo.ID)
	// API is being unreliable at the moment. Returns 403 sometimes
	// could be because of back-to-back requests?
	// assert.Nil(err)
	// assert.NotNil(resp)

	//empty stuff
	//empty things
	resp, err = unsplash.Collections.RemovePhoto(0, "photoID")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.RemovePhoto(296, "")
	assert.NotNil(err)
	assert.Nil(resp)
	resp, err = unsplash.Collections.RemovePhoto(0, "")
	assert.NotNil(err)
	assert.Nil(resp)
}

func rogueCollectionServiceTest(T *testing.T, responder httpmock.Responder) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()
	log.SetOutput(os.Stdout)

	httpmock.RegisterResponder("GET", getEndpoint(base)+getEndpoint(collections)+"/gopherCollection",
		responder)
	httpmock.RegisterResponder("POST", getEndpoint(base)+getEndpoint(collections)+"?title=gopherCollection",
		responder)
	httpmock.RegisterResponder("PUT", getEndpoint(base)+getEndpoint(collections)+"/4242?title=gopherCollection",
		responder)
	httpmock.RegisterResponder("POST", getEndpoint(base)+getEndpoint(collections)+"/4242/add?photo_id=gopherPhoto",
		responder)
	httpmock.RegisterResponder("DELETE", getEndpoint(base)+getEndpoint(collections)+"/4242/remove?photo_id=gopherPhoto",
		responder)
	httpmock.RegisterResponder("DELETE", getEndpoint(base)+getEndpoint(collections)+"/4242",
		responder)

	unsplash := setup()
	assert := assert.New(T)
	collection, resp, err := unsplash.Collections.Collection("gopherCollection")
	assert.Nil(collection)
	assert.Nil(resp)
	assert.NotNil(err)
	log.Println(err)

	var opt CollectionOpt
	title := "gopherCollection"
	opt.Title = &title
	collection, resp, err = unsplash.Collections.Create(&opt)
	assert.Nil(collection)
	assert.Nil(resp)
	assert.NotNil(err)
	log.Println(err)

	collection, resp, err = unsplash.Collections.Update(4242, &opt)
	assert.Nil(collection)
	assert.Nil(resp)
	assert.NotNil(err)
	log.Println(err)

	resp, err = unsplash.Collections.Delete(4242)
	assert.Nil(resp)
	assert.NotNil(err)
	log.Println(err)

	resp, err = unsplash.Collections.AddPhoto(4242, "gopherPhoto")
	assert.NotNil(err)
	assert.Nil(resp)
	log.Println(err)
}

func TestCollectionServiceRogueStuff(T *testing.T) {
	rogueCollectionServiceTest(T, httpmock.NewStringResponder(200, `Bad ass Bug flow`))
	rogueCollectionServiceTest(T, nil)
}

func TestRemovePhotoRogue(T *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()
	log.SetOutput(os.Stdout)

	httpmock.RegisterResponder("DELETE", getEndpoint(base)+getEndpoint(collections)+"/4242/remove?photo_id=gopherPhoto",
		httpmock.NewStringResponder(202, `Bad ass Bug flow`))

	unsplash := setup()
	assert := assert.New(T)
	resp, err := unsplash.Collections.RemovePhoto(4242, "gopherPhoto")
	assert.NotNil(err)
	assert.Nil(resp)
	log.Println(err)

	httpmock.RegisterResponder("DELETE", getEndpoint(base)+getEndpoint(collections)+"/4242/remove?photo_id=gopherPhoto",
		nil)

	resp, err = unsplash.Collections.RemovePhoto(4242, "gopherPhoto")
	assert.Nil(resp)
	assert.NotNil(err)
	log.Println(err)
}
<|endoftext|>


<commit_before>package kateway

import (
	"bytes"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/funkygao/gafka/cmd/kateway/api/v1"
	"github.com/funkygao/gafka/cmd/kguard/monitor"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	log "github.com/funkygao/log4go"
)

func init() {
	monitor.RegisterWatcher("kateway.pubsub", func() monitor.Watcher {
		return &WatchPubsub{
			Tick: time.Minute,
		}
	})
}

// WatchPubsub monitors aliveness of kateway cluster.
type WatchPubsub struct {
	Zkzone *zk.ZkZone
	Stop   <-chan struct{}
	Tick   time.Duration
	Wg     *sync.WaitGroup
}

func (this *WatchPubsub) Init(ctx monitor.Context) {
	this.Zkzone = ctx.ZkZone()
	this.Stop = ctx.StopChan()
	this.Wg = ctx.Inflight()
}

func (this *WatchPubsub) Run() {
	defer this.Wg.Done()

	ticker := time.NewTicker(this.Tick)
	defer ticker.Stop()

	pubsubHealth := metrics.NewRegisteredGauge("kateway.pubsub", nil)

	for {
		select {
		case <-this.Stop:
			log.Info("kateway.pubsub stopped")
			return

		case <-ticker.C:
			pubsubHealth.Update(int64(1))
		}
	}
}

func (this *WatchPubsub) runCheckup() {
	kws, err := this.Zkzone.KatewayInfos()
	if err != nil {
		// TODO
	}

	var (
		myApp         = os.Getenv("MYAPP")
		hisApp        = os.Getenv("HISAPP")
		secret        = os.Getenv("APPKEY")
		ver    string = "v1"
		topic  string = "smoketestonly"
		group         = "__smoketestonly__"
	)

	for _, kw := range kws {
		// pub a message
		cf := api.DefaultConfig(myApp, secret)
		cf.Pub.Endpoint = kw.PubAddr
		cf.Sub.Endpoint = kw.SubAddr
		cli := api.NewClient(cf)
		msgId := rand.Int()
		pubMsg := fmt.Sprintf("smoke test[%d] from kguard", msgId)

		err = cli.Pub("", []byte(pubMsg), api.PubOption{
			Topic: topic,
			Ver:   ver,
		})
		if err != nil {
			// TODO
		}

		// confirm that sub can get the pub'ed message
		cli.Sub(api.SubOption{
			AppId: hisApp,
			Topic: topic,
			Ver:   ver,
			Group: group,
		}, func(statusCode int, subMsg []byte) error {
			if statusCode != http.StatusOK {
				// TODO
			}
			if !bytes.Equal(pubMsg, subMsg) {
				// TODO
			}

			return api.ErrSubStop
		})

	}
}
<commit_msg>kguard monitor pubsub<commit_after>package kateway

import (
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/funkygao/gafka/cmd/kateway/api/v1"
	"github.com/funkygao/gafka/cmd/kguard/monitor"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	log "github.com/funkygao/log4go"
)

func init() {
	monitor.RegisterWatcher("kateway.pubsub", func() monitor.Watcher {
		return &WatchPubsub{
			Tick: time.Minute,
		}
	})
}

// WatchPubsub monitors aliveness of kateway cluster.
type WatchPubsub struct {
	Zkzone *zk.ZkZone
	Stop   <-chan struct{}
	Tick   time.Duration
	Wg     *sync.WaitGroup
}

func (this *WatchPubsub) Init(ctx monitor.Context) {
	this.Zkzone = ctx.ZkZone()
	this.Stop = ctx.StopChan()
	this.Wg = ctx.Inflight()
}

func (this *WatchPubsub) Run() {
	defer this.Wg.Done()

	ticker := time.NewTicker(this.Tick)
	defer ticker.Stop()

	pubsubHealth := metrics.NewRegisteredGauge("kateway.pubsub", nil)

	for {
		select {
		case <-this.Stop:
			log.Info("kateway.pubsub stopped")
			return

		case <-ticker.C:
			pubsubHealth.Update(int64(1))
		}
	}
}

func (this *WatchPubsub) runCheckup() {
	kws, err := this.Zkzone.KatewayInfos()
	if err != nil {
		log.Error("pubsub: %v", err)
		return
	}

	var (
		myApp         = os.Getenv("MYAPP")
		hisApp        = os.Getenv("HISAPP")
		secret        = os.Getenv("APPKEY")
		ver    string = "v1"
		topic  string = "smoketestonly"
		group         = "__smoketestonly__"
	)

	for _, kw := range kws {
		// pub a message
		cf := api.DefaultConfig(myApp, secret)
		cf.Pub.Endpoint = kw.PubAddr
		cf.Sub.Endpoint = kw.SubAddr
		cli := api.NewClient(cf)
		msgId := rand.Int()
		pubMsg := fmt.Sprintf("smoke test[%d] from kguard", msgId)

		err = cli.Pub("", []byte(pubMsg), api.PubOption{
			Topic: topic,
			Ver:   ver,
		})
		if err != nil {
			log.Error("pub: %v", err)
			return
		}

		log.Info("pub[%s]: %s", kw.Host, pubMsg)

		// confirm that sub can get the pub'ed message
		cli.Sub(api.SubOption{
			AppId: hisApp,
			Topic: topic,
			Ver:   ver,
			Group: group,
		}, func(statusCode int, subMsg []byte) error {
			if statusCode != http.StatusOK {
				log.Error("sub status: %s", http.StatusText(statusCode))
			} else {
				log.Info("sub[%s]: %s", kw.Host, string(subMsg))
			}

			return api.ErrSubStop
		})

	}
}
<|endoftext|>


<commit_before>/* Copyright ©2015 The corridor 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 corridor

import (
	"fmt"
	"runtime"
	"testing"
)

// parallel small problem benchmark
func BenchmarkSmall(b *testing.B) {

	// set max processing units
	cpuCount := runtime.NumCPU()
	runtime.GOMAXPROCS(cpuCount)

	// initialize integer constants
	const (
		xDim           int = 20
		yDim           int = 20
		bandCount      int = 3
		objectiveCount int = 3
		populationSize int = 1000
	)

	// initialize domain
	sampleDomain := NewSampleDomain(xDim, yDim)
	sampleDomain.BndCnt = bandCount

	// initialize objectives
	sampleObjectives := NewSampleObjectives(sampleDomain.Rows, sampleDomain.Cols, objectiveCount)

	// initialize parameters
	sampleParameters := NewSampleParameters(sampleDomain)
	sampleParameters.PopSize = populationSize

	// evolve populations
	toyEvolution := NewEvolution(sampleParameters, sampleDomain, sampleObjectives)

	// extract output population
	finalPop := <-toyEvolution.Populations

	// view output population
	ViewPopulation(sampleDomain, sampleParameters, finalPop)

	// view sample chromosome
	ViewChromosome(sampleDomain, sampleParameters, <-finalPop.Chromosomes)

	// print top individual fitness
	fmt.Println("Population Mean Fitness =")
	fmt.Println(finalPop.MeanFitness)
}

// medium problem benchmark
func BenchmarkMedium(b *testing.B) {

	// set max processing units
	cpuCount := runtime.NumCPU()
	runtime.GOMAXPROCS(cpuCount)

	// initialize integer constants
	const (
		xDim           int = 20
		yDim           int = 20
		bandCount      int = 3
		objectiveCount int = 3
		populationSize int = 10000
	)

	// initialize domain
	sampleDomain := NewSampleDomain(xDim, yDim)
	sampleDomain.BndCnt = bandCount

	// initialize objectives
	sampleObjectives := NewSampleObjectives(sampleDomain.Rows, sampleDomain.Cols, objectiveCount)

	// initialize parameters
	sampleParameters := NewSampleParameters(sampleDomain)
	sampleParameters.PopSize = populationSize

	// evolve populations
	toyEvolution := NewEvolution(sampleParameters, sampleDomain, sampleObjectives)

	// extract output population
	finalPop := <-toyEvolution.Populations

	// view sample chromosome
	ViewChromosome(sampleDomain, sampleParameters, <-finalPop.Chromosomes)

	// view output population
	ViewPopulation(sampleDomain, sampleParameters, finalPop)

	// print top individual fitness
	fmt.Println("Population Mean Fitness =")
	fmt.Println(finalPop.MeanFitness)
}

// large problem benchmark
func BenchmarkLarge(b *testing.B) {

	// set max processing units
	cpuCount := runtime.NumCPU()
	runtime.GOMAXPROCS(cpuCount)

	// initialize integer constants
	const (
		xDim           int = 20
		yDim           int = 20
		bandCount      int = 3
		objectiveCount int = 3
		populationSize int = 10000
	)

	// initialize domain
	sampleDomain := NewSampleDomain(xDim, yDim)
	sampleDomain.BndCnt = bandCount

	// initialize objectives
	sampleObjectives := NewSampleObjectives(sampleDomain.Rows, sampleDomain.Cols, objectiveCount)

	// initialize parameters
	sampleParameters := NewSampleParameters(sampleDomain)
	sampleParameters.PopSize = populationSize

	// evolve populations
	toyEvolution := NewEvolution(sampleParameters, sampleDomain, sampleObjectives)

	// extract output population
	finalPop := <-toyEvolution.Populations

	// view sample chromosome
	ViewChromosome(sampleDomain, sampleParameters, <-finalPop.Chromosomes)

	// view output population
	ViewPopulation(sampleDomain, sampleParameters, finalPop)

	// print top individual fitness
	fmt.Println("Population Mean Fitness =")
	fmt.Println(finalPop.MeanFitness)
}
<commit_msg>Fixed test bug<commit_after>/* Copyright ©2015 The corridor 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 corridor

import (
	"fmt"
	"runtime"
	"testing"
)

// parallel small problem benchmark
func BenchmarkSmall(b *testing.B) {

	// set max processing units
	cpuCount := runtime.NumCPU()
	runtime.GOMAXPROCS(cpuCount)

	// initialize integer constants
	const (
		xDim           int = 20
		yDim           int = 20
		bandCount      int = 3
		objectiveCount int = 3
		populationSize int = 1000
	)

	// initialize domain
	sampleDomain := NewSampleDomain(xDim, yDim)
	sampleDomain.BndCnt = bandCount

	// initialize objectives
	sampleObjectives := NewSampleObjectives(sampleDomain.Rows, sampleDomain.Cols, objectiveCount)

	// initialize parameters
	sampleParameters := NewSampleParameters(sampleDomain)
	sampleParameters.PopSize = populationSize

	// evolve populations
	toyEvolution := NewEvolution(sampleParameters, sampleDomain, sampleObjectives)

	// extract output population
	finalPop := <-toyEvolution.Populations

	// view output population
	ViewPopulation(sampleDomain, sampleParameters, finalPop)

	// view sample chromosome
	ViewChromosome(sampleDomain, sampleParameters, <-finalPop.Chromosomes)

	// print top individual fitness
	fmt.Println("Population Mean Fitness =")
	fmt.Println(finalPop.MeanFitness)
}

// medium problem benchmark
func BenchmarkMedium(b *testing.B) {

	// set max processing units
	cpuCount := runtime.NumCPU()
	runtime.GOMAXPROCS(cpuCount)

	// initialize integer constants
	const (
		xDim           int = 20
		yDim           int = 20
		bandCount      int = 3
		objectiveCount int = 3
		populationSize int = 10000
	)

	// initialize domain
	sampleDomain := NewSampleDomain(xDim, yDim)
	sampleDomain.BndCnt = bandCount

	// initialize objectives
	sampleObjectives := NewSampleObjectives(sampleDomain.Rows, sampleDomain.Cols, objectiveCount)

	// initialize parameters
	sampleParameters := NewSampleParameters(sampleDomain)
	sampleParameters.PopSize = populationSize

	// evolve populations
	toyEvolution := NewEvolution(sampleParameters, sampleDomain, sampleObjectives)

	// extract output population
	finalPop := <-toyEvolution.Populations

	// view sample chromosome
	ViewChromosome(sampleDomain, sampleParameters, <-finalPop.Chromosomes)

	// view output population
	ViewPopulation(sampleDomain, sampleParameters, finalPop)

	// print top individual fitness
	fmt.Println("Population Mean Fitness =")
	fmt.Println(finalPop.MeanFitness)
}

// large problem benchmark
func BenchmarkLarge(b *testing.B) {

	// set max processing units
	cpuCount := runtime.NumCPU()
	runtime.GOMAXPROCS(cpuCount)

	// initialize integer constants
	const (
		xDim           int = 20
		yDim           int = 20
		bandCount      int = 3
		objectiveCount int = 3
		populationSize int = 100000
	)

	// initialize domain
	sampleDomain := NewSampleDomain(xDim, yDim)
	sampleDomain.BndCnt = bandCount

	// initialize objectives
	sampleObjectives := NewSampleObjectives(sampleDomain.Rows, sampleDomain.Cols, objectiveCount)

	// initialize parameters
	sampleParameters := NewSampleParameters(sampleDomain)
	sampleParameters.PopSize = populationSize

	// evolve populations
	toyEvolution := NewEvolution(sampleParameters, sampleDomain, sampleObjectives)

	// extract output population
	finalPop := <-toyEvolution.Populations

	// view sample chromosome
	ViewChromosome(sampleDomain, sampleParameters, <-finalPop.Chromosomes)

	// view output population
	ViewPopulation(sampleDomain, sampleParameters, finalPop)

	// print top individual fitness
	fmt.Println("Population Mean Fitness =")
	fmt.Println(finalPop.MeanFitness)
}
<|endoftext|>


<commit_before>package multiread

import (
	"context"
	"errors"
	"io"
	"sync"

	"github.com/itchio/wharf/ctxcopy"
)

type multiread struct {
	upstream io.Reader
	writers  []*io.PipeWriter
	doing    bool
}

// Multiread lets multiple readers read the same data
type Multiread interface {
	Reader() io.Reader
	Do(ctx context.Context) error
}

// New returns a new instance of Multiread
// reading from upstream
func New(upstream io.Reader) Multiread {
	return &multiread{upstream: upstream}
}

func (m *multiread) Reader() io.Reader {
	if m.doing {
		return &errReader{err: errors.New("multiread: cannot call Reader() after Do()")}
	}

	r, w := io.Pipe()
	m.writers = append(m.writers, w)
	return r
}

func (m *multiread) Do(ctx context.Context) error {
	m.doing = true

	var closeOnce sync.Once

	defer closeOnce.Do(func() {
		for _, w := range m.writers {
			w.Close()
		}
	})

	ww := make([]io.Writer, 0, len(m.writers))
	for _, w := range m.writers {
		ww = append(ww, w)
	}
	mw := io.MultiWriter(ww...)

	_, err := ctxcopy.Do(ctx, mw, m.upstream)
	if err != nil {
		closeOnce.Do(func() {
			for _, w := range m.writers {
				w.CloseWithError(err)
			}
		})
	}
	return err
}

// errReader

type errReader struct {
	err error
}

var _ io.Reader = (*errReader)(nil)

func (er *errReader) Read(buf []byte) (int, error) {
	return 0, er.err
}
<commit_msg>Add lock to avoid race condition in multiread<commit_after>package multiread

import (
	"context"
	"errors"
	"io"
	"sync"

	"github.com/itchio/wharf/ctxcopy"
)

type multiread struct {
	upstream  io.Reader
	writers   []*io.PipeWriter
	doing     bool
	doingLock sync.Mutex
}

// Multiread lets multiple readers read the same data
type Multiread interface {
	Reader() io.Reader
	Do(ctx context.Context) error
}

// New returns a new instance of Multiread
// reading from upstream
func New(upstream io.Reader) Multiread {
	return &multiread{upstream: upstream}
}

func (m *multiread) Reader() io.Reader {
	m.doingLock.Lock()
	defer m.doingLock.Unlock()
	if m.doing {
		return &errReader{err: errors.New("multiread: cannot call Reader() after Do()")}
	}

	r, w := io.Pipe()
	m.writers = append(m.writers, w)
	return r
}

func (m *multiread) Do(ctx context.Context) error {
	m.doingLock.Lock()
	m.doing = true
	m.doingLock.Unlock()

	var closeOnce sync.Once

	defer closeOnce.Do(func() {
		for _, w := range m.writers {
			w.Close()
		}
	})

	ww := make([]io.Writer, 0, len(m.writers))
	for _, w := range m.writers {
		ww = append(ww, w)
	}
	mw := io.MultiWriter(ww...)

	_, err := ctxcopy.Do(ctx, mw, m.upstream)
	if err != nil {
		closeOnce.Do(func() {
			for _, w := range m.writers {
				w.CloseWithError(err)
			}
		})
	}
	return err
}

// errReader

type errReader struct {
	err error
}

var _ io.Reader = (*errReader)(nil)

func (er *errReader) Read(buf []byte) (int, error) {
	return 0, er.err
}
<|endoftext|>


<commit_before>package handlers

import (
	"github.com/go-openapi/runtime/middleware"
	"github.com/sapcc/kubernikus/pkg/api"
	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/pkg/api/rest/operations"
)

func NewListAPIVersions(rt *api.Runtime) operations.ListAPIVersionsHandler {
	return &listAPIVersions{rt: rt}
}

type listAPIVersions struct {
	rt *api.Runtime
}

func (d *listAPIVersions) Handle(params operations.ListAPIVersionsParams) middleware.Responder {
	return operations.NewListAPIVersionsOK().WithPayload(
		models.APIVersions{Versions: []string{"v1"}},
	)
}
<commit_msg>Fix list_api_versions<commit_after>package handlers

import (
	"github.com/go-openapi/runtime/middleware"
	"github.com/sapcc/kubernikus/pkg/api"
	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/pkg/api/rest/operations"
)

func NewListAPIVersions(rt *api.Runtime) operations.ListAPIVersionsHandler {
	return &listAPIVersions{rt: rt}
}

type listAPIVersions struct {
	rt *api.Runtime
}

func (d *listAPIVersions) Handle(params operations.ListAPIVersionsParams) middleware.Responder {
	return operations.NewListAPIVersionsOK().WithPayload(
		&models.APIVersions{Versions: []string{"v1"}},
	)
}
<|endoftext|>


<commit_before>package myaws

import (
	"context"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/pkg/errors"
)

// FindSSMParameterMetadata returns an array of parameter metadata matching the name.
func (client *Client) FindSSMParameterMetadata(name string) ([]*ssm.ParameterMetadata, error) {
	var filter *ssm.ParametersFilter
	if len(name) > 0 {
		filter = &ssm.ParametersFilter{
			Key: aws.String("Name"),
			Values: []*string{
				aws.String(name),
			},
		}
	}
	filters := []*ssm.ParametersFilter{filter}

	input := &ssm.DescribeParametersInput{
		Filters: filters,
	}

	// We need to fetch all pages to get results.
	// The request timeout should be set in the caller context,
	// but for the moment we will create a context here.
	metadata := []*ssm.ParameterMetadata{}
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	err := client.SSM.DescribeParametersPagesWithContext(ctx,
		input,
		func(page *ssm.DescribeParametersOutput, lastPage bool) bool {
			metadata = append(metadata, page.Parameters...)
			return true
		})

	if err != nil {
		return nil, errors.Wrap(err, "DescribeParameters failed:")
	}

	return metadata, nil
}

// GetSSMParameters returns an array of parameters at once.
func (client *Client) GetSSMParameters(names []*string, withDecryption bool) ([]*ssm.Parameter, error) {
	results := []*ssm.Parameter{}

	// The AWS SSM GetPrameters API can only get 10 parameters at once.
	// To get 10 or more parameters, we need to call API multiple time.
	// https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetParameters.html
	chunkSize := 10

	for i := 0; i < len(names); i += chunkSize {
		end := i + chunkSize
		if end > len(names) {
			end = len(names)
		}
		chunk := names[i:end]
		resultsPerChunk, err := client.getSSMParametersPerChunk(chunk, withDecryption)
		if err != nil {
			return nil, err
		}

		results = append(results, resultsPerChunk...)
	}

	return results, nil
}

// getSSMParametersPerChunk returns an array of parameters per chunk.
func (client *Client) getSSMParametersPerChunk(names []*string, withDecryption bool) ([]*ssm.Parameter, error) {
	input := &ssm.GetParametersInput{
		Names:          names,
		WithDecryption: aws.Bool(withDecryption),
	}

	response, err := client.SSM.GetParameters(input)
	if err != nil {
		return nil, errors.Wrap(err, "GetParameters failed:")
	}

	if len(response.InvalidParameters) > 0 {
		return nil, errors.Errorf("InvalidParameters: %v", awsutil.Prettify(response.InvalidParameters))
	}

	return response.Parameters, nil
}

// GetParametersByPath returns an array of parameters parameters at once
func (client *Client) GetParametersByPath(path *string, withDecryption bool) ([]*ssm.Parameter, error) {
	input := &ssm.GetParametersByPathInput{
		Path:           path,
		Recursive:      aws.Bool(true),
		WithDecryption: aws.Bool(withDecryption),
	}

	var parameters []*ssm.Parameter
	err := client.SSM.GetParametersByPathPages(input,
		func(page *ssm.GetParametersByPathOutput, lastPage bool) bool {
			parameters = append(parameters, page.Parameters...)
			return true
		})
	if err != nil {
		return nil, errors.Wrap(err, "GetParameters failed:")
	}
	return parameters, nil
}
<commit_msg>fixed error message of GetParametersByPath<commit_after>package myaws

import (
	"context"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/pkg/errors"
)

// FindSSMParameterMetadata returns an array of parameter metadata matching the name.
func (client *Client) FindSSMParameterMetadata(name string) ([]*ssm.ParameterMetadata, error) {
	var filter *ssm.ParametersFilter
	if len(name) > 0 {
		filter = &ssm.ParametersFilter{
			Key: aws.String("Name"),
			Values: []*string{
				aws.String(name),
			},
		}
	}
	filters := []*ssm.ParametersFilter{filter}

	input := &ssm.DescribeParametersInput{
		Filters: filters,
	}

	// We need to fetch all pages to get results.
	// The request timeout should be set in the caller context,
	// but for the moment we will create a context here.
	metadata := []*ssm.ParameterMetadata{}
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	err := client.SSM.DescribeParametersPagesWithContext(ctx,
		input,
		func(page *ssm.DescribeParametersOutput, lastPage bool) bool {
			metadata = append(metadata, page.Parameters...)
			return true
		})

	if err != nil {
		return nil, errors.Wrap(err, "DescribeParameters failed:")
	}

	return metadata, nil
}

// GetSSMParameters returns an array of parameters at once.
func (client *Client) GetSSMParameters(names []*string, withDecryption bool) ([]*ssm.Parameter, error) {
	results := []*ssm.Parameter{}

	// The AWS SSM GetPrameters API can only get 10 parameters at once.
	// To get 10 or more parameters, we need to call API multiple time.
	// https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetParameters.html
	chunkSize := 10

	for i := 0; i < len(names); i += chunkSize {
		end := i + chunkSize
		if end > len(names) {
			end = len(names)
		}
		chunk := names[i:end]
		resultsPerChunk, err := client.getSSMParametersPerChunk(chunk, withDecryption)
		if err != nil {
			return nil, err
		}

		results = append(results, resultsPerChunk...)
	}

	return results, nil
}

// getSSMParametersPerChunk returns an array of parameters per chunk.
func (client *Client) getSSMParametersPerChunk(names []*string, withDecryption bool) ([]*ssm.Parameter, error) {
	input := &ssm.GetParametersInput{
		Names:          names,
		WithDecryption: aws.Bool(withDecryption),
	}

	response, err := client.SSM.GetParameters(input)
	if err != nil {
		return nil, errors.Wrap(err, "GetParameters failed:")
	}

	if len(response.InvalidParameters) > 0 {
		return nil, errors.Errorf("InvalidParameters: %v", awsutil.Prettify(response.InvalidParameters))
	}

	return response.Parameters, nil
}

// GetParametersByPath returns an array of parameters parameters at once
func (client *Client) GetParametersByPath(path *string, withDecryption bool) ([]*ssm.Parameter, error) {
	input := &ssm.GetParametersByPathInput{
		Path:           path,
		Recursive:      aws.Bool(true),
		WithDecryption: aws.Bool(withDecryption),
	}

	var parameters []*ssm.Parameter
	err := client.SSM.GetParametersByPathPages(input,
		func(page *ssm.GetParametersByPathOutput, lastPage bool) bool {
			parameters = append(parameters, page.Parameters...)
			return true
		})
	if err != nil {
		return nil, errors.Wrap(err, "GetParametersByPath failed:")
	}
	return parameters, nil
}
<|endoftext|>


<commit_before>package loadbalancer

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/ian-kent/service.go/log"
	"github.com/paasbox/paasbox/sysd/logger"
	"github.com/paasbox/paasbox/sysd/util/lockwarn"
)

// LB is a load balancer
type LB interface {
	AddListener(port int) (Listener, error)
	StopListener(port int) error
	Stats() LBStats
	Log() []string
}

// Listener ...
type Listener interface {
	Instances() []string
	AddInstances(addr ...string)
	RemoveInstance(addr ...string)
}

var _ LB = &lb{}
var _ Listener = &lbListener{}

type lb struct {
	listeners map[int]*lbListener
	logger    *lbLogger
	logDriver logger.Driver
}

type lbListener struct {
	net.Listener
	port      int
	instances map[string]struct{}
	mutex     *sync.RWMutex
	logger    *lbLogger

	txSend      int64
	txRecv      int64
	totalConns  int64
	activeConns int64

	connErrors  int64
	readErrors  int64
	writeErrors int64
	dialErrors  int64

	statChan chan listenerStat
}

type lbInstance struct {
	port int
}

// New creates a new load balancer
func New(logDriver logger.Driver) (LB, error) {
	var logOutput bool
	if s := os.Getenv("PAASBOX_LB_LOG"); s == "y" || s == "1" {
		logOutput = true
	}

	return &lb{
		listeners: make(map[int]*lbListener),
		logger: &lbLogger{
			Messages:  make([]*lbLoggerMessage, 50000, 50000),
			Pos:       0,
			Limit:     50000,
			Log:       logOutput,
			logDriver: logDriver,
		},
		logDriver: logDriver,
	}, nil
}

func (lb *lb) Stats() LBStats {
	var totalRx, totalTx, activeConns, totalConns, connErrors, dialErrors, readErrors, writeErrors int64
	listeners := make(map[int]Stats)

	for port, ln := range lb.listeners {
		totalRx += ln.txRecv
		totalTx += ln.txSend
		activeConns += ln.activeConns
		totalConns += ln.totalConns
		connErrors += ln.connErrors
		dialErrors += ln.dialErrors
		readErrors += ln.readErrors
		writeErrors += ln.writeErrors

		listeners[port] = Stats{
			Connections:      ConnectionStats{ln.totalConns, ln.activeConns},
			Bytes:            ByteStats{ln.txSend, ln.txRecv},
			ConnectionErrors: ln.connErrors,
			DialErrors:       ln.dialErrors,
			WriteErrors:      ln.writeErrors,
			ReadErrors:       ln.readErrors,
			HealthyInstances: int64(len(ln.instances)),
		}
	}
	return LBStats{
		Connections:      ConnectionStats{totalConns, activeConns},
		Bytes:            ByteStats{totalTx, totalRx},
		Listeners:        listeners,
		ConnectionErrors: connErrors,
		DialErrors:       dialErrors,
		WriteErrors:      writeErrors,
		ReadErrors:       readErrors,
	}
}

func (lb *lb) Log() (output []string) {
	c := lockwarn.Notify()
	lb.logger.RWMutex.RLock()
	close(c)
	defer lb.logger.RWMutex.RUnlock()

	for i := lb.logger.Pos; i < lb.logger.Limit; i++ {
		if s := lb.logger.Messages[i]; s != nil {
			output = append(output, s.String())
		}
	}
	for i := 0; i < lb.logger.Pos; i++ {
		if s := lb.logger.Messages[i]; s != nil {
			output = append(output, s.String())
		}
	}

	return
}

func (lb *lb) AddListener(port int) (Listener, error) {
	lb.logger.Message("<loadbalancer>", "adding listener", log.Data{"port": port})

	if _, ok := lb.listeners[port]; ok {
		return nil, errors.New("port already in use")
	}
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	listener := &lbListener{l, port, make(map[string]struct{}, 0), new(sync.RWMutex), lb.logger, 0, 0, 0, 0, 0, 0, 0, 0, make(chan listenerStat, 500)}
	lb.listeners[port] = listener
	go listener.start()
	return listener, nil
}

func (lb *lb) StopListener(port int) error {
	return errors.New("not implemented")
}

func (li *lbListener) Instances() (res []string) {
	for k := range li.instances {
		res = append(res, k)
	}
	return
}

func (li *lbListener) trackStats(doneCh chan struct{}) {
	for {
		select {
		case n := <-li.statChan:
			switch n.statType {
			case statRX:
				li.txRecv += n.n
			case statTX:
				li.txSend += n.n
			case statConn:
				li.activeConns += n.n
				if n.n > 0 {
					li.totalConns++
				}
			case statConnError:
				li.connErrors++
			case statReadError:
				li.readErrors++
			case statWriteError:
				li.writeErrors++
			case statDialError:
				li.dialErrors++
			}
		case <-doneCh:
			return
		}
	}
}

func (li *lbListener) start() {
	doneCh := make(chan struct{})
	go li.trackStats(doneCh)

	for {
		connID := uuid.New().String()

		lconn, err := li.Accept()
		if err != nil {
			li.logger.Error(err, nil, connID, "", nil)
			li.statChan <- listenerStat{statConnError, 1}
			continue
		}

		li.logger.Message(connID, "connection accepted", log.Data{"remote_addr": lconn.RemoteAddr(), "local_addr": lconn.LocalAddr()})
		li.statChan <- listenerStat{statConn, 1}

		c := lockwarn.Notify()
		li.mutex.RLock()
		close(c)
		instances := li.Instances()
		num := len(instances)
		if num < 1 {
			li.mutex.RUnlock()
			li.logger.Message(connID, "no healthy instances", nil)
			lconn.Close()
			li.statChan <- listenerStat{statConn, -1}
			continue
		}

		n := rand.Intn(num)
		li.logger.Message(connID, "instances", log.Data{"count": len(li.instances), "n": n, "instances": li.instances})
		dest := instances[n]
		li.mutex.RUnlock()

		rconn, err := net.DialTimeout("tcp", dest, time.Second*2)
		if err != nil {
			log.Error(err, nil)
			lconn.Close()
			li.statChan <- listenerStat{statConn, -1}
			li.statChan <- listenerStat{statDialError, 1}
			continue
		}

		li.logger.Message(connID, "connection opened", log.Data{"remote_addr": rconn.RemoteAddr(), "local_addr": rconn.LocalAddr()})

		go func() {
			defer lconn.Close()
			defer rconn.Close()
			defer func() {
				li.statChan <- listenerStat{statConn, -1}
				li.logger.Message(connID, "connection closed", log.Data{"remote_addr": lconn.RemoteAddr(), "local_addr": lconn.LocalAddr()})
			}()

			// TODO handle errors?
			var wg sync.WaitGroup
			var readErr, writeErr error
			wg.Add(2)

			go func() {
				defer wg.Done()
				writeErr = li.pipe(lconn, rconn)
				li.logger.Message(connID, "lconn->rconn pipe closed", log.Data{"error": writeErr})
			}()
			go func() {
				defer wg.Done()
				readErr = li.pipe(rconn, lconn)
				li.logger.Message(connID, "rconn->lconn pipe closed", log.Data{"error": readErr})
			}()

			c := lockwarn.Notify()
			wg.Wait()
			close(c)
			li.logger.Message(connID, "wait completed", nil)
		}()
	}
}

func (li *lbListener) AddInstances(addr ...string) {
	c := lockwarn.Notify()
	li.mutex.Lock()
	close(c)
	defer li.mutex.Unlock()
	for _, a := range addr {
		li.instances[a] = struct{}{}
	}
}

func (li *lbListener) RemoveInstance(addr ...string) {
	c := lockwarn.Notify()
	li.mutex.Lock()
	close(c)
	defer li.mutex.Unlock()
	for _, a := range addr {
		delete(li.instances, a)
	}
}

func (li *lbListener) pipe(src, dst io.ReadWriteCloser) error {
	//directional copy (64k buffer)
	buff := make([]byte, 0xffff)
	for {
		n, err := src.Read(buff)
		li.statChan <- listenerStat{statRX, int64(n)}
		if err != nil {
			if err != io.EOF && err != io.ErrClosedPipe {
				li.statChan <- listenerStat{statReadError, 1}
			}
			dst.Close()
			return err
		}
		b := buff[:n]

		//write out result
		n, err = dst.Write(b)
		li.statChan <- listenerStat{statTX, int64(n)}
		if err != nil {
			if err != io.EOF {
				li.statChan <- listenerStat{statWriteError, 1}
			}
			src.Close()
			return err
		}
	}
}

func (li *lbInstance) Port() int {
	return li.port
}
<commit_msg>remove lockwarn from loadbalancer<commit_after>package loadbalancer

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/ian-kent/service.go/log"
	"github.com/paasbox/paasbox/sysd/logger"
	"github.com/paasbox/paasbox/sysd/util/lockwarn"
)

// LB is a load balancer
type LB interface {
	AddListener(port int) (Listener, error)
	StopListener(port int) error
	Stats() LBStats
	Log() []string
}

// Listener ...
type Listener interface {
	Instances() []string
	AddInstances(addr ...string)
	RemoveInstance(addr ...string)
}

var _ LB = &lb{}
var _ Listener = &lbListener{}

type lb struct {
	listeners map[int]*lbListener
	logger    *lbLogger
	logDriver logger.Driver
}

type lbListener struct {
	net.Listener
	port      int
	instances map[string]struct{}
	mutex     *sync.RWMutex
	logger    *lbLogger

	txSend      int64
	txRecv      int64
	totalConns  int64
	activeConns int64

	connErrors  int64
	readErrors  int64
	writeErrors int64
	dialErrors  int64

	statChan chan listenerStat
}

type lbInstance struct {
	port int
}

// New creates a new load balancer
func New(logDriver logger.Driver) (LB, error) {
	var logOutput bool
	if s := os.Getenv("PAASBOX_LB_LOG"); s == "y" || s == "1" {
		logOutput = true
	}

	return &lb{
		listeners: make(map[int]*lbListener),
		logger: &lbLogger{
			Messages:  make([]*lbLoggerMessage, 50000, 50000),
			Pos:       0,
			Limit:     50000,
			Log:       logOutput,
			logDriver: logDriver,
		},
		logDriver: logDriver,
	}, nil
}

func (lb *lb) Stats() LBStats {
	var totalRx, totalTx, activeConns, totalConns, connErrors, dialErrors, readErrors, writeErrors int64
	listeners := make(map[int]Stats)

	for port, ln := range lb.listeners {
		totalRx += ln.txRecv
		totalTx += ln.txSend
		activeConns += ln.activeConns
		totalConns += ln.totalConns
		connErrors += ln.connErrors
		dialErrors += ln.dialErrors
		readErrors += ln.readErrors
		writeErrors += ln.writeErrors

		listeners[port] = Stats{
			Connections:      ConnectionStats{ln.totalConns, ln.activeConns},
			Bytes:            ByteStats{ln.txSend, ln.txRecv},
			ConnectionErrors: ln.connErrors,
			DialErrors:       ln.dialErrors,
			WriteErrors:      ln.writeErrors,
			ReadErrors:       ln.readErrors,
			HealthyInstances: int64(len(ln.instances)),
		}
	}
	return LBStats{
		Connections:      ConnectionStats{totalConns, activeConns},
		Bytes:            ByteStats{totalTx, totalRx},
		Listeners:        listeners,
		ConnectionErrors: connErrors,
		DialErrors:       dialErrors,
		WriteErrors:      writeErrors,
		ReadErrors:       readErrors,
	}
}

func (lb *lb) Log() (output []string) {
	c := lockwarn.Notify()
	lb.logger.RWMutex.RLock()
	close(c)
	defer lb.logger.RWMutex.RUnlock()

	for i := lb.logger.Pos; i < lb.logger.Limit; i++ {
		if s := lb.logger.Messages[i]; s != nil {
			output = append(output, s.String())
		}
	}
	for i := 0; i < lb.logger.Pos; i++ {
		if s := lb.logger.Messages[i]; s != nil {
			output = append(output, s.String())
		}
	}

	return
}

func (lb *lb) AddListener(port int) (Listener, error) {
	lb.logger.Message("<loadbalancer>", "adding listener", log.Data{"port": port})

	if _, ok := lb.listeners[port]; ok {
		return nil, errors.New("port already in use")
	}
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	listener := &lbListener{l, port, make(map[string]struct{}, 0), new(sync.RWMutex), lb.logger, 0, 0, 0, 0, 0, 0, 0, 0, make(chan listenerStat, 500)}
	lb.listeners[port] = listener
	go listener.start()
	return listener, nil
}

func (lb *lb) StopListener(port int) error {
	return errors.New("not implemented")
}

func (li *lbListener) Instances() (res []string) {
	for k := range li.instances {
		res = append(res, k)
	}
	return
}

func (li *lbListener) trackStats(doneCh chan struct{}) {
	for {
		select {
		case n := <-li.statChan:
			switch n.statType {
			case statRX:
				li.txRecv += n.n
			case statTX:
				li.txSend += n.n
			case statConn:
				li.activeConns += n.n
				if n.n > 0 {
					li.totalConns++
				}
			case statConnError:
				li.connErrors++
			case statReadError:
				li.readErrors++
			case statWriteError:
				li.writeErrors++
			case statDialError:
				li.dialErrors++
			}
		case <-doneCh:
			return
		}
	}
}

func (li *lbListener) start() {
	doneCh := make(chan struct{})
	go li.trackStats(doneCh)

	for {
		connID := uuid.New().String()

		lconn, err := li.Accept()
		if err != nil {
			li.logger.Error(err, nil, connID, "", nil)
			li.statChan <- listenerStat{statConnError, 1}
			continue
		}

		li.logger.Message(connID, "connection accepted", log.Data{"remote_addr": lconn.RemoteAddr(), "local_addr": lconn.LocalAddr()})
		li.statChan <- listenerStat{statConn, 1}

		c := lockwarn.Notify()
		li.mutex.RLock()
		close(c)
		instances := li.Instances()
		num := len(instances)
		if num < 1 {
			li.mutex.RUnlock()
			li.logger.Message(connID, "no healthy instances", nil)
			lconn.Close()
			li.statChan <- listenerStat{statConn, -1}
			continue
		}

		n := rand.Intn(num)
		li.logger.Message(connID, "instances", log.Data{"count": len(li.instances), "n": n, "instances": li.instances})
		dest := instances[n]
		li.mutex.RUnlock()

		rconn, err := net.DialTimeout("tcp", dest, time.Second*2)
		if err != nil {
			log.Error(err, nil)
			lconn.Close()
			li.statChan <- listenerStat{statConn, -1}
			li.statChan <- listenerStat{statDialError, 1}
			continue
		}

		li.logger.Message(connID, "connection opened", log.Data{"remote_addr": rconn.RemoteAddr(), "local_addr": rconn.LocalAddr()})

		go func() {
			defer lconn.Close()
			defer rconn.Close()
			defer func() {
				li.statChan <- listenerStat{statConn, -1}
				li.logger.Message(connID, "connection closed", log.Data{"remote_addr": lconn.RemoteAddr(), "local_addr": lconn.LocalAddr()})
			}()

			// TODO handle errors?
			var wg sync.WaitGroup
			var readErr, writeErr error
			wg.Add(2)

			go func() {
				defer wg.Done()
				writeErr = li.pipe(lconn, rconn)
				li.logger.Message(connID, "lconn->rconn pipe closed", log.Data{"error": writeErr})
			}()
			go func() {
				defer wg.Done()
				readErr = li.pipe(rconn, lconn)
				li.logger.Message(connID, "rconn->lconn pipe closed", log.Data{"error": readErr})
			}()

			wg.Wait()
			li.logger.Message(connID, "wait completed", nil)
		}()
	}
}

func (li *lbListener) AddInstances(addr ...string) {
	c := lockwarn.Notify()
	li.mutex.Lock()
	close(c)
	defer li.mutex.Unlock()
	for _, a := range addr {
		li.instances[a] = struct{}{}
	}
}

func (li *lbListener) RemoveInstance(addr ...string) {
	c := lockwarn.Notify()
	li.mutex.Lock()
	close(c)
	defer li.mutex.Unlock()
	for _, a := range addr {
		delete(li.instances, a)
	}
}

func (li *lbListener) pipe(src, dst io.ReadWriteCloser) error {
	//directional copy (64k buffer)
	buff := make([]byte, 0xffff)
	for {
		n, err := src.Read(buff)
		li.statChan <- listenerStat{statRX, int64(n)}
		if err != nil {
			if err != io.EOF && err != io.ErrClosedPipe {
				li.statChan <- listenerStat{statReadError, 1}
			}
			dst.Close()
			return err
		}
		b := buff[:n]

		//write out result
		n, err = dst.Write(b)
		li.statChan <- listenerStat{statTX, int64(n)}
		if err != nil {
			if err != io.EOF {
				li.statChan <- listenerStat{statWriteError, 1}
			}
			src.Close()
			return err
		}
	}
}

func (li *lbInstance) Port() int {
	return li.port
}
<|endoftext|>