<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package fsblkstorage

import (
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	"github.com/davecgh/go-spew/spew"
	"github.com/hyperledger/fabric-protos-go/common"
	"github.com/pkg/errors"
)

// constructCheckpointInfoFromBlockFiles scans the last blockfile (if any) and construct the checkpoint info
// if the last file contains no block or only a partially written block (potentially because of a crash while writing block to the file),
// this scans the second last file (if any)
func constructCheckpointInfoFromBlockFiles(rootDir string) (*checkpointInfo, error) {
	logger.Debugf("Retrieving checkpoint info from block files")
	var lastFileNum int
	var numBlocksInFile int
	var endOffsetLastBlock int64
	var lastBlockNumber uint64

	var lastBlockBytes []byte
	var lastBlock *common.Block
	var err error

	if lastFileNum, err = retrieveLastFileSuffix(rootDir); err != nil {
		return nil, err
	}
	logger.Debugf("Last file number found = %d", lastFileNum)

	if lastFileNum == -1 {
		cpInfo := &checkpointInfo{0, 0, true, 0}
		logger.Debugf("No block file found")
		return cpInfo, nil
	}

	fileInfo := getFileInfoOrPanic(rootDir, lastFileNum)
	logger.Debugf("Last Block file info: FileName=[%s], FileSize=[%d]", fileInfo.Name(), fileInfo.Size())
	if lastBlockBytes, endOffsetLastBlock, numBlocksInFile, err = scanForLastCompleteBlock(rootDir, lastFileNum, 0); err != nil {
		logger.Errorf("Error scanning last file [num=%d]: %s", lastFileNum, err)
		return nil, err
	}

	if numBlocksInFile == 0 && lastFileNum > 0 {
		secondLastFileNum := lastFileNum - 1
		fileInfo := getFileInfoOrPanic(rootDir, secondLastFileNum)
		logger.Debugf("Second last Block file info: FileName=[%s], FileSize=[%d]", fileInfo.Name(), fileInfo.Size())
		if lastBlockBytes, _, _, err = scanForLastCompleteBlock(rootDir, secondLastFileNum, 0); err != nil {
			logger.Errorf("Error scanning second last file [num=%d]: %s", secondLastFileNum, err)
			return nil, err
		}
	}

	if lastBlockBytes != nil {
		if lastBlock, err = deserializeBlock(lastBlockBytes); err != nil {
			logger.Errorf("Error deserializing last block: %s. Block bytes length: %d", err, len(lastBlockBytes))
			return nil, err
		}
		lastBlockNumber = lastBlock.Header.Number
	}

	cpInfo := &checkpointInfo{
		lastBlockNumber:          lastBlockNumber,
		latestFileChunksize:      int(endOffsetLastBlock),
		latestFileChunkSuffixNum: lastFileNum,
		isChainEmpty:             lastFileNum == 0 && numBlocksInFile == 0,
	}
	logger.Debugf("Checkpoint info constructed from file system = %s", spew.Sdump(cpInfo))
	return cpInfo, nil
}

// binarySearchFileNumForBlock locates the file number that contains the given block number.
// This function assumes that the caller invokes this function with a block number that has been commited
// For any uncommitted block, this function returns the last file present
func binarySearchFileNumForBlock(rootDir string, blockNum uint64) (int, error) {
	cpInfo, err := constructCheckpointInfoFromBlockFiles(rootDir)
	if err != nil {
		return -1, err
	}

	beginFile := 0
	endFile := cpInfo.latestFileChunkSuffixNum

	for endFile != beginFile {
		searchFile := beginFile + (endFile-beginFile)/2 + 1
		n, err := retriveFirstBlockNumFromFile(rootDir, searchFile)
		if err != nil {
			return -1, err
		}
		switch {
		case n == blockNum:
			return searchFile, nil
		case n > blockNum:
			endFile = searchFile - 1
		case n < blockNum:
			beginFile = searchFile
		}
	}
	return beginFile, nil
}

func retriveFirstBlockNumFromFile(rootDir string, fileNum int) (uint64, error) {
	s, err := newBlockfileStream(rootDir, fileNum, 0)
	if err != nil {
		return 0, err
	}
	defer s.close()
	bb, err := s.nextBlockBytes()
	if err != nil {
		return 0, err
	}
	blockInfo, err := extractSerializedBlockInfo(bb)
	if err != nil {
		return 0, err
	}
	return blockInfo.blockHeader.Number, nil
}

func retrieveLastFileSuffix(rootDir string) (int, error) {
	logger.Debugf("retrieveLastFileSuffix()")
	biggestFileNum := -1
	filesInfo, err := ioutil.ReadDir(rootDir)
	if err != nil {
		return -1, errors.Wrapf(err, "error reading dir %s", rootDir)
	}
	for _, fileInfo := range filesInfo {
		name := fileInfo.Name()
		if fileInfo.IsDir() || !isBlockFileName(name) {
			logger.Debugf("Skipping File name = %s", name)
			continue
		}
		fileSuffix := strings.TrimPrefix(name, blockfilePrefix)
		fileNum, err := strconv.Atoi(fileSuffix)
		if err != nil {
			return -1, err
		}
		if fileNum > biggestFileNum {
			biggestFileNum = fileNum
		}
	}
	logger.Debugf("retrieveLastFileSuffix() - biggestFileNum = %d", biggestFileNum)
	return biggestFileNum, err
}

func isBlockFileName(name string) bool {
	return strings.HasPrefix(name, blockfilePrefix)
}

func getFileInfoOrPanic(rootDir string, fileNum int) os.FileInfo {
	filePath := deriveBlockfilePath(rootDir, fileNum)
	fileInfo, err := os.Lstat(filePath)
	if err != nil {
		panic(errors.Wrapf(err, "error retrieving file info for file number %d", fileNum))
	}
	return fileInfo
}
<commit_msg>fix typo in the method name<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package fsblkstorage

import (
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	"github.com/davecgh/go-spew/spew"
	"github.com/hyperledger/fabric-protos-go/common"
	"github.com/pkg/errors"
)

// constructCheckpointInfoFromBlockFiles scans the last blockfile (if any) and construct the checkpoint info
// if the last file contains no block or only a partially written block (potentially because of a crash while writing block to the file),
// this scans the second last file (if any)
func constructCheckpointInfoFromBlockFiles(rootDir string) (*checkpointInfo, error) {
	logger.Debugf("Retrieving checkpoint info from block files")
	var lastFileNum int
	var numBlocksInFile int
	var endOffsetLastBlock int64
	var lastBlockNumber uint64

	var lastBlockBytes []byte
	var lastBlock *common.Block
	var err error

	if lastFileNum, err = retrieveLastFileSuffix(rootDir); err != nil {
		return nil, err
	}
	logger.Debugf("Last file number found = %d", lastFileNum)

	if lastFileNum == -1 {
		cpInfo := &checkpointInfo{0, 0, true, 0}
		logger.Debugf("No block file found")
		return cpInfo, nil
	}

	fileInfo := getFileInfoOrPanic(rootDir, lastFileNum)
	logger.Debugf("Last Block file info: FileName=[%s], FileSize=[%d]", fileInfo.Name(), fileInfo.Size())
	if lastBlockBytes, endOffsetLastBlock, numBlocksInFile, err = scanForLastCompleteBlock(rootDir, lastFileNum, 0); err != nil {
		logger.Errorf("Error scanning last file [num=%d]: %s", lastFileNum, err)
		return nil, err
	}

	if numBlocksInFile == 0 && lastFileNum > 0 {
		secondLastFileNum := lastFileNum - 1
		fileInfo := getFileInfoOrPanic(rootDir, secondLastFileNum)
		logger.Debugf("Second last Block file info: FileName=[%s], FileSize=[%d]", fileInfo.Name(), fileInfo.Size())
		if lastBlockBytes, _, _, err = scanForLastCompleteBlock(rootDir, secondLastFileNum, 0); err != nil {
			logger.Errorf("Error scanning second last file [num=%d]: %s", secondLastFileNum, err)
			return nil, err
		}
	}

	if lastBlockBytes != nil {
		if lastBlock, err = deserializeBlock(lastBlockBytes); err != nil {
			logger.Errorf("Error deserializing last block: %s. Block bytes length: %d", err, len(lastBlockBytes))
			return nil, err
		}
		lastBlockNumber = lastBlock.Header.Number
	}

	cpInfo := &checkpointInfo{
		lastBlockNumber:          lastBlockNumber,
		latestFileChunksize:      int(endOffsetLastBlock),
		latestFileChunkSuffixNum: lastFileNum,
		isChainEmpty:             lastFileNum == 0 && numBlocksInFile == 0,
	}
	logger.Debugf("Checkpoint info constructed from file system = %s", spew.Sdump(cpInfo))
	return cpInfo, nil
}

// binarySearchFileNumForBlock locates the file number that contains the given block number.
// This function assumes that the caller invokes this function with a block number that has been commited
// For any uncommitted block, this function returns the last file present
func binarySearchFileNumForBlock(rootDir string, blockNum uint64) (int, error) {
	cpInfo, err := constructCheckpointInfoFromBlockFiles(rootDir)
	if err != nil {
		return -1, err
	}

	beginFile := 0
	endFile := cpInfo.latestFileChunkSuffixNum

	for endFile != beginFile {
		searchFile := beginFile + (endFile-beginFile)/2 + 1
		n, err := retrieveFirstBlockNumFromFile(rootDir, searchFile)
		if err != nil {
			return -1, err
		}
		switch {
		case n == blockNum:
			return searchFile, nil
		case n > blockNum:
			endFile = searchFile - 1
		case n < blockNum:
			beginFile = searchFile
		}
	}
	return beginFile, nil
}

func retrieveFirstBlockNumFromFile(rootDir string, fileNum int) (uint64, error) {
	s, err := newBlockfileStream(rootDir, fileNum, 0)
	if err != nil {
		return 0, err
	}
	defer s.close()
	bb, err := s.nextBlockBytes()
	if err != nil {
		return 0, err
	}
	blockInfo, err := extractSerializedBlockInfo(bb)
	if err != nil {
		return 0, err
	}
	return blockInfo.blockHeader.Number, nil
}

func retrieveLastFileSuffix(rootDir string) (int, error) {
	logger.Debugf("retrieveLastFileSuffix()")
	biggestFileNum := -1
	filesInfo, err := ioutil.ReadDir(rootDir)
	if err != nil {
		return -1, errors.Wrapf(err, "error reading dir %s", rootDir)
	}
	for _, fileInfo := range filesInfo {
		name := fileInfo.Name()
		if fileInfo.IsDir() || !isBlockFileName(name) {
			logger.Debugf("Skipping File name = %s", name)
			continue
		}
		fileSuffix := strings.TrimPrefix(name, blockfilePrefix)
		fileNum, err := strconv.Atoi(fileSuffix)
		if err != nil {
			return -1, err
		}
		if fileNum > biggestFileNum {
			biggestFileNum = fileNum
		}
	}
	logger.Debugf("retrieveLastFileSuffix() - biggestFileNum = %d", biggestFileNum)
	return biggestFileNum, err
}

func isBlockFileName(name string) bool {
	return strings.HasPrefix(name, blockfilePrefix)
}

func getFileInfoOrPanic(rootDir string, fileNum int) os.FileInfo {
	filePath := deriveBlockfilePath(rootDir, fileNum)
	fileInfo, err := os.Lstat(filePath)
	if err != nil {
		panic(errors.Wrapf(err, "error retrieving file info for file number %d", fileNum))
	}
	return fileInfo
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/vaughan0/go-ini"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func logf(fmt string, v ...interface{}) {
	log.Printf(fmt, v...)
}

// Return the value of an ENV var, or the fallback value if the ENV var is empty/undefined.
func getenv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

// Check if the connection to tcp://addr is readable.
func read(addr string) error {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return err
	}
	defer conn.Close()
	if _, err = conn.Read(make([]byte, 1)); err != nil {
		return err
	}
	return nil
}

// Check if an addr can be successfully connected.
func ping(addr string) bool {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return false
	}
	defer conn.Close()
	return true
}

// Download the url to the dest path.
func download(dest, url string) error {
	rsp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()

	// Create the dest dir.
	if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
		return err
	}

	f, err := os.Create(fmt.Sprintf("%s.download", dest))
	if err != nil {
		return err
	}
	defer os.Remove(f.Name())

	if _, err := io.Copy(f, rsp.Body); err != nil {
		// TODO: display download progress?
		return err
	}
	if err := f.Close(); err != nil {
		return err
	}
	if err := os.Rename(f.Name(), dest); err != nil {
		return err
	}
	return nil
}

// Get latest release tag name (e.g. "v0.6.0") from a repo on GitHub.
func getLatestReleaseName(url string) (string, error) {
	rsp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer rsp.Body.Close()

	var t []struct {
		TagName string `json:"tag_name"`
	}
	if err := json.NewDecoder(rsp.Body).Decode(&t); err != nil {
		return "", err
	}
	if len(t) == 0 {
		return "", fmt.Errorf("no releases found")
	}
	return t[0].TagName, nil
}

type cfgImport struct {
	cf ini.File
}

func (f cfgImport) Get(section, key, defaultstr string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	if value, ok := f.cf.Get(section, key); ok {
		return os.ExpandEnv(value)
	}
	return defaultstr
}

var readConfigfile = func(filename string) (string, error) {
	value, err := ioutil.ReadFile(filename)
	return string(value), err
}

var getConfigfile = func() (cfgImport, error) {
	var cfg cfgImport
	cfgStr, err := readConfigfile(B2D.Dir + "/profile")
	if err != nil {
		return cfg, err
	}

	cfgini, err := ini.Load(strings.NewReader(cfgStr))
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
		return cfg, err
	}
	cfg = cfgImport{cf: cfgini}

	return cfg, err
}
<commit_msg>fix filepath.join for Windows<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/vaughan0/go-ini"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func logf(fmt string, v ...interface{}) {
	log.Printf(fmt, v...)
}

// Return the value of an ENV var, or the fallback value if the ENV var is empty/undefined.
func getenv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

// Check if the connection to tcp://addr is readable.
func read(addr string) error {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return err
	}
	defer conn.Close()
	if _, err = conn.Read(make([]byte, 1)); err != nil {
		return err
	}
	return nil
}

// Check if an addr can be successfully connected.
func ping(addr string) bool {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return false
	}
	defer conn.Close()
	return true
}

// Download the url to the dest path.
func download(dest, url string) error {
	rsp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()

	// Create the dest dir.
	if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
		return err
	}

	f, err := os.Create(fmt.Sprintf("%s.download", dest))
	if err != nil {
		return err
	}
	defer os.Remove(f.Name())

	if _, err := io.Copy(f, rsp.Body); err != nil {
		// TODO: display download progress?
		return err
	}
	if err := f.Close(); err != nil {
		return err
	}
	if err := os.Rename(f.Name(), dest); err != nil {
		return err
	}
	return nil
}

// Get latest release tag name (e.g. "v0.6.0") from a repo on GitHub.
func getLatestReleaseName(url string) (string, error) {
	rsp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer rsp.Body.Close()

	var t []struct {
		TagName string `json:"tag_name"`
	}
	if err := json.NewDecoder(rsp.Body).Decode(&t); err != nil {
		return "", err
	}
	if len(t) == 0 {
		return "", fmt.Errorf("no releases found")
	}
	return t[0].TagName, nil
}

type cfgImport struct {
	cf ini.File
}

func (f cfgImport) Get(section, key, defaultstr string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	if value, ok := f.cf.Get(section, key); ok {
		return os.ExpandEnv(value)
	}
	return defaultstr
}

var readConfigfile = func(filename string) (string, error) {
	value, err := ioutil.ReadFile(filename)
	return string(value), err
}

var getConfigfile = func() (cfgImport, error) {
	var cfg cfgImport
	filename := filepath.Join(B2D.Dir, "profile")
	cfgStr, err := readConfigfile(filename)
	if err != nil {
		return cfg, err
	}

	cfgini, err := ini.Load(strings.NewReader(cfgStr))
	if err != nil {
		log.Fatalf("Failed to parse %s: %s", filename, err)
		return cfg, err
	}
	cfg = cfgImport{cf: cfgini}

	return cfg, err
}
<|endoftext|>


<commit_before>package account

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"
)

func ListChannels(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	query := helpers.GetQuery(u)

	accountId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	a := &models.Account{Id: accountId}
	channels, err := a.FetchChannels(query)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(channels)
}
<commit_msg>Social: add unfollow api handler<commit_after>package account

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"
)

func ListChannels(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	query := helpers.GetQuery(u)

	accountId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	a := &models.Account{Id: accountId}
	channels, err := a.FetchChannels(query)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(channels)
}
func Unfollow(u *url.URL, h http.Header, req *models.Account) (int, http.Header, interface{}, error) {
	targetId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	if err := req.Unfollow(targetId); err != nil {
		return helpers.NewBadRequestResponse()
	}

	// req shouldnt be returned?
	return helpers.NewOKResponse(req)
}
<|endoftext|>


<commit_before>package popular

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"
	"socialapi/workers/helper"
	"socialapi/workers/populartopic/populartopic"
	"strconv"
	"time"
)

func ListTopics(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	query := helpers.GetQuery(u)

	statisticName := u.Query().Get("statisticName")

	now := time.Now().UTC()
	// dateNumber is changing according to the statisticName
	// if it is monthly statistic, it will be month number March->3
	// if it is weekly statistic, it will be week number 48th week -> 48
	var dateNumber int
	year, month, _ := now.Date()

	if statisticName == "monthly" {
		dateNumber = int(month)
	} else {
		statisticName = "weekly"
		_, dateNumber = now.ISOWeek()
	}

	key := populartopic.PreparePopularTopicKey(
		query.GroupName,
		statisticName,
		year,
		dateNumber,
	)

	redisConn := helper.MustGetRedisConn()
	// limit-1 is important, because redis is using 0 based index
	topics, err := redisConn.SortedSetReverseRange(key, query.Skip, query.Skip+query.Limit-1)
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	popularTopicIds := make([]int64, 0)
	for _, topic := range topics {
		val, err := strconv.ParseInt(string(topic.([]uint8)), 10, 64)
		if err == nil {
			popularTopicIds = append(popularTopicIds, val)
		}
	}

	popularTopicIds, err = extendPopularTopicsIfNeeded(query, popularTopicIds)
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	c := models.NewChannel()
	popularTopics, err := c.FetchByIds(popularTopicIds)
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	res := models.PopulateChannelContainer(popularTopics, query.AccountId)
	return helpers.NewOKResponse(res)
}

func extendPopularTopicsIfNeeded(query *models.Query, popularTopics []int64) ([]int64, error) {
	toBeAddedItemCount := query.Limit - len(popularTopics)

	if toBeAddedItemCount > 0 {
		normalChannels, err := fetchMoreChannels(query.GroupName, query.Limit)
		if err != nil {
			return popularTopics, err
		}

		for _, normalChannel := range normalChannels {
			exists := false
			for _, popularTopicId := range popularTopics {
				if normalChannel.Id == popularTopicId {
					exists = true
					break
				}
			}

			if !exists {
				popularTopics = append(popularTopics, normalChannel.Id)
				toBeAddedItemCount--
				if toBeAddedItemCount == 0 {
					break
				}
			}
		}
	}

	return popularTopics, nil
}

func fetchMoreChannels(group string, count int) ([]models.Channel, error) {
	q := models.NewQuery()
	q.GroupName = group
	q.Limit = count
	q.Type = models.Channel_TYPE_TOPIC
	q.SetDefaults()
	c := models.NewChannel()
	return c.List(q)
}
<commit_msg>Social: change function name<commit_after>package popular

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"
	"socialapi/workers/helper"
	"socialapi/workers/populartopic/populartopic"
	"strconv"
	"time"
)

func ListTopics(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	query := helpers.GetQuery(u)

	statisticName := u.Query().Get("statisticName")

	now := time.Now().UTC()
	// dateNumber is changing according to the statisticName
	// if it is monthly statistic, it will be month number March->3
	// if it is weekly statistic, it will be week number 48th week -> 48
	var dateNumber int
	year, month, _ := now.Date()

	if statisticName == "monthly" {
		dateNumber = int(month)
	} else {
		statisticName = "weekly"
		_, dateNumber = now.ISOWeek()
	}

	key := populartopic.PreparePopularTopicKey(
		query.GroupName,
		statisticName,
		year,
		dateNumber,
	)

	redisConn := helper.MustGetRedisConn()
	// limit-1 is important, because redis is using 0 based index
	topics, err := redisConn.SortedSetReverseRange(key, query.Skip, query.Skip+query.Limit-1)
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	popularTopicIds := make([]int64, 0)
	for _, topic := range topics {
		val, err := strconv.ParseInt(string(topic.([]uint8)), 10, 64)
		if err == nil {
			popularTopicIds = append(popularTopicIds, val)
		}
	}

	popularTopicIds, err = extendPopularTopicsIfNeeded(query, popularTopicIds)
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	c := models.NewChannel()
	popularTopics, err := c.FetchByIds(popularTopicIds)
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	res := models.PopulateChannelContainers(popularTopics, query.AccountId)
	return helpers.NewOKResponse(res)
}

func extendPopularTopicsIfNeeded(query *models.Query, popularTopics []int64) ([]int64, error) {
	toBeAddedItemCount := query.Limit - len(popularTopics)

	if toBeAddedItemCount > 0 {
		normalChannels, err := fetchMoreChannels(query.GroupName, query.Limit)
		if err != nil {
			return popularTopics, err
		}

		for _, normalChannel := range normalChannels {
			exists := false
			for _, popularTopicId := range popularTopics {
				if normalChannel.Id == popularTopicId {
					exists = true
					break
				}
			}

			if !exists {
				popularTopics = append(popularTopics, normalChannel.Id)
				toBeAddedItemCount--
				if toBeAddedItemCount == 0 {
					break
				}
			}
		}
	}

	return popularTopics, nil
}

func fetchMoreChannels(group string, count int) ([]models.Channel, error) {
	q := models.NewQuery()
	q.GroupName = group
	q.Limit = count
	q.Type = models.Channel_TYPE_TOPIC
	q.SetDefaults()
	c := models.NewChannel()
	return c.List(q)
}
<|endoftext|>


<commit_before>// Copyright 2019 The Terraformer Authors.
//
// 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 azure

import (
	"context"
	"log"

	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
	"github.com/Azure/go-autorest/autorest"
	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
	"github.com/hashicorp/go-azure-helpers/authentication"
)

type VirtualMachineGenerator struct {
	AzureService
}

func (g VirtualMachineGenerator) createResources(virtualMachineListResultPage compute.VirtualMachineListResultPage) []terraform_utils.Resource {
	var resources []terraform_utils.Resource
	for virtualMachineListResultPage.NotDone() {
		vms := virtualMachineListResultPage.Values()
		for _, vm := range vms {
			resources = append(resources, terraform_utils.NewSimpleResource(
				*vm.ID,
				*vm.Name,
				"azurerm_virtual_machine",
				"azurerm",
				[]string{}))
		}
		if err := virtualMachineListResultPage.Next(); err != nil {
			log.Println(err)
			break
		}
	}
	return resources
}

func (g *VirtualMachineGenerator) InitResources() error {
	ctx := context.Background()
	vmClient := compute.NewVirtualMachinesClient(g.Args["config"].(authentication.Config).SubscriptionID)

	vmClient.Authorizer = g.Args["authorizer"].(autorest.Authorizer)
	output, err := vmClient.ListAll(ctx)
	if err != nil {
		return err
	}
	g.Resources = g.createResources(output)
	return nil
}
<commit_msg>Replace the deprecated azurerm_virtual_machine<commit_after>// Copyright 2019 The Terraformer Authors.
//
// 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 azure

import (
	"context"
	"log"

	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
	"github.com/Azure/go-autorest/autorest"
	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
	"github.com/hashicorp/go-azure-helpers/authentication"
)

type VirtualMachineGenerator struct {
	AzureService
}

func (g VirtualMachineGenerator) createResources(virtualMachineListResultPage compute.VirtualMachineListResultPage) []terraform_utils.Resource {
	var resources []terraform_utils.Resource
	for virtualMachineListResultPage.NotDone() {
		vms := virtualMachineListResultPage.Values()
		for _, vm := range vms {
			var newResource terraform_utils.Resource
			if vm.VirtualMachineProperties.OsProfile.WindowsConfiguration != nil {
				newResource = terraform_utils.NewSimpleResource(
					*vm.ID,
					*vm.Name,
					"azurerm_windows_virtual_machine",
					"azurerm",
					[]string{})
			} else {
				newResource = terraform_utils.NewSimpleResource(
					*vm.ID,
					*vm.Name,
					"azurerm_linux_virtual_machine",
					"azurerm",
					[]string{})
			}

			resources = append(resources, newResource)
		}
		if err := virtualMachineListResultPage.Next(); err != nil {
			log.Println(err)
			break
		}
	}
	return resources
}

func (g *VirtualMachineGenerator) InitResources() error {
	ctx := context.Background()
	vmClient := compute.NewVirtualMachinesClient(g.Args["config"].(authentication.Config).SubscriptionID)

	vmClient.Authorizer = g.Args["authorizer"].(autorest.Authorizer)
	output, err := vmClient.ListAll(ctx)
	if err != nil {
		return err
	}
	g.Resources = g.createResources(output)
	return nil
}
<|endoftext|>


<commit_before>package dclass

// A DataType declares the type of data stored by a Parameter.
type DataType int

const (
	Int8Type DataType = iota
	Int16Type
	Int32Type
	Int64Type
	Uint8Type
	Uint16Type
	Uint32Type
	Uint64Type
	FloatType
	StringType
	BlobType
	CharType
	StructType
)

// An Error is a dclass package specific error
type Error string

// implements Error interface
func (err Error) Error() string {
	return string(err)
}

// implements Stringer interface
func (err Error) String() string {
	return string(err)
}

func runtimeError(msg string) Error {
	return Error("runtime error: " + msg)
}

type Hashable interface {
	Hash() uint64
}

// A KeywordList is any dctype that has an associated keyword list.  The most common KeywordLists
// are: a File object with its list of declared keywords and a Field with its list of enabled keywords.
type KeywordList interface {
	// AddKeyword adds the keyword argument to the set of keywords in the list.
	AddKeyword(keyword string)

	// AddKeywords performs a union of the KeywordList argument into this KeywordList.
	AddKeywords(list KeywordList)

	// CompareKeywords compares two KeywordLists and returns true if they contain the same set of
	// keywords. Order does not matter.
	CompareKeywords(list KeywordList) bool

	// HasKeyword returns whether the keyword argument exists in the list.
	HasKeyword(keyword string) bool

	// Keywords returns the list of keywords as a slice
	Keywords() []string

	// Length returns the length of the keyword list
	Length() int
}

// type keywords is a string slice satisfying the KeywordList interface.
type keywords []string

// implementing KeywordList
func (k keywords) AddKeyword(keyword string) {
	if !k.HasKeyword(keyword) {
		k = append(k, keyword)
	}
}

// implementing KeywordList
func (k keywords) AddKeywords(list KeywordList) {
	for _, keyword := range list.Keywords() {
		k.AddKeyword(keyword)
	}
}

// implementing KeywordList
func (k keywords) CompareKeywords(list KeywordList) bool {
	if len(k) != len(list.Keywords()) {
		return false
	}
	for _, keyword := range k {
		if !list.HasKeyword(keyword) {
			return false
		}
	}
	return true
}

// implementing KeywordList
func (k keywords) HasKeyword(keyword string) bool {
	for _, word := range k {
		if keyword == word {
			return true
		}
	}
	return false
}

// implementing KeywordList
func (k keywords) Keywords() []string {
	return []string(k)
}

// implementing KeywordList
func (k keywords) Length() int {
	return len(k)
}
<commit_msg>KeywordList: Rename Length to NumKeywords, improving heritability.<commit_after>package dclass

// A DataType declares the type of data stored by a Parameter.
type DataType int

const (
	Int8Type DataType = iota
	Int16Type
	Int32Type
	Int64Type
	Uint8Type
	Uint16Type
	Uint32Type
	Uint64Type
	FloatType
	StringType
	BlobType
	CharType
	StructType
)

// An Error is a dclass package specific error
type Error string

// implements Error interface
func (err Error) Error() string {
	return string(err)
}

// implements Stringer interface
func (err Error) String() string {
	return string(err)
}

func runtimeError(msg string) Error {
	return Error("runtime error: " + msg)
}

type Hashable interface {
	Hash() uint64
}

// A KeywordList is any dctype that has an associated keyword list.  The most common KeywordLists
// are: a File object with its list of declared keywords and a Field with its list of enabled keywords.
type KeywordList interface {
	// AddKeyword adds the keyword argument to the set of keywords in the list.
	AddKeyword(keyword string)

	// AddKeywords performs a union of the KeywordList argument into this KeywordList.
	AddKeywords(list KeywordList)

	// CompareKeywords compares two KeywordLists and returns true if they contain the same set of
	// keywords. Order does not matter.
	CompareKeywords(list KeywordList) bool

	// HasKeyword returns whether the keyword argument exists in the list.
	HasKeyword(keyword string) bool

	// Keywords returns the list of keywords as a slice
	Keywords() []string

	// NumKeywords returns the length of the keyword list
	NumKeywords() int
}

// type keywords is a string slice satisfying the KeywordList interface.
type keywords []string

// implementing KeywordList
func (k keywords) AddKeyword(keyword string) {
	if !k.HasKeyword(keyword) {
		k = append(k, keyword)
	}
}

// implementing KeywordList
func (k keywords) AddKeywords(list KeywordList) {
	for _, keyword := range list.Keywords() {
		k.AddKeyword(keyword)
	}
}

// implementing KeywordList
func (k keywords) CompareKeywords(list KeywordList) bool {
	if len(k) != len(list.Keywords()) {
		return false
	}
	for _, keyword := range k {
		if !list.HasKeyword(keyword) {
			return false
		}
	}
	return true
}

// implementing KeywordList
func (k keywords) HasKeyword(keyword string) bool {
	for _, word := range k {
		if keyword == word {
			return true
		}
	}
	return false
}

// implementing KeywordList
func (k keywords) Keywords() []string {
	return []string(k)
}

// implementing KeywordList
func (k keywords) NumKeywords() int {
	return len(k)
}
<|endoftext|>


<commit_before><commit_msg>Include seconds in timestamp for late errors (#3661)<commit_after><|endoftext|>


<commit_before>// +build !windows

package termbox

import "fmt"
import "os"
import "os/signal"
import "syscall"
import "runtime"

// public API

// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
//      err := termbox.Init()
//      if err != nil {
//              panic(err)
//      }
//      defer termbox.Close()
func Init() error {
	var err error

	out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
	if err != nil {
		return err
	}
	in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
	if err != nil {
		return err
	}

	err = setup_term()
	if err != nil {
		return fmt.Errorf("termbox: error while reading terminfo data: %v", err)
	}

	signal.Notify(sigwinch, syscall.SIGWINCH)
	signal.Notify(sigio, syscall.SIGIO)

	_, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
	if err != nil {
		return err
	}
	_, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
	if runtime.GOOS != "darwin" && err != nil {
		return err
	}
	err = tcgetattr(out.Fd(), &orig_tios)
	if err != nil {
		return err
	}

	tios := orig_tios
	tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK |
		syscall_ISTRIP | syscall_INLCR | syscall_IGNCR |
		syscall_ICRNL | syscall_IXON
	tios.Oflag &^= syscall_OPOST
	tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON |
		syscall_ISIG | syscall_IEXTEN
	tios.Cflag &^= syscall_CSIZE | syscall_PARENB
	tios.Cflag |= syscall_CS8
	tios.Cc[syscall_VMIN] = 1
	tios.Cc[syscall_VTIME] = 0

	err = tcsetattr(out.Fd(), &tios)
	if err != nil {
		return err
	}

	out.WriteString(funcs[t_enter_ca])
	out.WriteString(funcs[t_enter_keypad])
	out.WriteString(funcs[t_hide_cursor])
	out.WriteString(funcs[t_clear_screen])

	termw, termh = get_term_size(out.Fd())
	back_buffer.init(termw, termh)
	front_buffer.init(termw, termh)
	back_buffer.clear()
	front_buffer.clear()

	go func() {
		buf := make([]byte, 128)
		for {
			select {
			case <-sigio:
				for {
					n, err := syscall.Read(in, buf)
					if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
						break
					}
					input_comm <- input_event{buf[:n], err}
					ie := <-input_comm
					buf = ie.data[:128]
				}
			case <-quit:
				return
			}
		}
	}()

	return nil
}

// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
	quit <- 1
	out.WriteString(funcs[t_show_cursor])
	out.WriteString(funcs[t_sgr0])
	out.WriteString(funcs[t_clear_screen])
	out.WriteString(funcs[t_exit_ca])
	out.WriteString(funcs[t_exit_keypad])
	tcsetattr(out.Fd(), &orig_tios)

	out.Close()
	syscall.Close(in)

	// reset the state, so that on next Init() it will work again
	termw = 0
	termh = 0
	input_mode = InputEsc
	out = nil
	in = 0
	lastfg = attr_invalid
	lastbg = attr_invalid
	lastx = coord_invalid
	lasty = coord_invalid
	cursor_x = cursor_hidden
	cursor_y = cursor_hidden
	foreground = ColorDefault
	background = ColorDefault
}

// Synchronizes the internal back buffer with the terminal.
func Flush() error {
	// invalidate cursor position
	lastx = coord_invalid
	lasty = coord_invalid

	update_size_maybe()

	for y := 0; y < front_buffer.height; y++ {
		line_offset := y * front_buffer.width
		for x := 0; x < front_buffer.width; {
			cell_offset := line_offset + x
			back := &back_buffer.cells[cell_offset]
			front := &front_buffer.cells[cell_offset]
			w := rune_width(back.Ch)
			if *back == *front {
				x += w
				continue
			}
			if back.Ch < ' ' {
				back.Ch = ' '
			}
			*front = *back
			send_attr(back.Fg, back.Bg)

			if w == 2 && x == front_buffer.width - 1 {
				// there's not enough space for 2-cells rune,
				// let's just put a space in there
				send_char(x, y, ' ')
			} else {
				send_char(x, y, back.Ch)
				if w == 2 {
					next := cell_offset + 1
					front_buffer.cells[next] = Cell{
						Ch: 0,
						Fg: back.Fg,
						Bg: back.Bg,
					}
				}
			}
			x += w
		}
	}
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
	return flush()
}

// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
	if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_show_cursor])
	}

	if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_hide_cursor])
	}

	cursor_x, cursor_y = x, y
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
}

// The shortcut for SetCursor(-1, -1).
func HideCursor() {
	SetCursor(cursor_hidden, cursor_hidden)
}

// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
	if x < 0 || x >= back_buffer.width {
		return
	}
	if y < 0 || y >= back_buffer.height {
		return
	}

	back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}

// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
	return back_buffer.cells
}

// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
	var event Event

	// try to extract event from input buffer, return on success
	event.Type = EventKey
	if extract_event(&event) {
		return event
	}

	for {
		select {
		case ev := <-input_comm:
			if ev.err != nil {
				return Event{Type: EventError, Err: ev.err}
			}

			inbuf = append(inbuf, ev.data...)
			input_comm <- ev
			if extract_event(&event) {
				return event
			}
		case <-sigwinch:
			event.Type = EventResize
			event.Width, event.Height = get_term_size(out.Fd())
			return event
		}
	}
	panic("unreachable")
}

// Returns the size of the internal back buffer (which is the same as
// terminal's window size in characters).
func Size() (int, int) {
	return termw, termh
}

// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
	foreground, background = fg, bg
	err := update_size_maybe()
	back_buffer.clear()
	return err
}

// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
	if mode != InputCurrent {
		input_mode = mode
	}
	return input_mode
}
<commit_msg>Oops, the previous patch will trigger a lot of unnecessary redraws.<commit_after>// +build !windows

package termbox

import "fmt"
import "os"
import "os/signal"
import "syscall"
import "runtime"

// public API

// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
//      err := termbox.Init()
//      if err != nil {
//              panic(err)
//      }
//      defer termbox.Close()
func Init() error {
	var err error

	out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
	if err != nil {
		return err
	}
	in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
	if err != nil {
		return err
	}

	err = setup_term()
	if err != nil {
		return fmt.Errorf("termbox: error while reading terminfo data: %v", err)
	}

	signal.Notify(sigwinch, syscall.SIGWINCH)
	signal.Notify(sigio, syscall.SIGIO)

	_, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
	if err != nil {
		return err
	}
	_, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
	if runtime.GOOS != "darwin" && err != nil {
		return err
	}
	err = tcgetattr(out.Fd(), &orig_tios)
	if err != nil {
		return err
	}

	tios := orig_tios
	tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK |
		syscall_ISTRIP | syscall_INLCR | syscall_IGNCR |
		syscall_ICRNL | syscall_IXON
	tios.Oflag &^= syscall_OPOST
	tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON |
		syscall_ISIG | syscall_IEXTEN
	tios.Cflag &^= syscall_CSIZE | syscall_PARENB
	tios.Cflag |= syscall_CS8
	tios.Cc[syscall_VMIN] = 1
	tios.Cc[syscall_VTIME] = 0

	err = tcsetattr(out.Fd(), &tios)
	if err != nil {
		return err
	}

	out.WriteString(funcs[t_enter_ca])
	out.WriteString(funcs[t_enter_keypad])
	out.WriteString(funcs[t_hide_cursor])
	out.WriteString(funcs[t_clear_screen])

	termw, termh = get_term_size(out.Fd())
	back_buffer.init(termw, termh)
	front_buffer.init(termw, termh)
	back_buffer.clear()
	front_buffer.clear()

	go func() {
		buf := make([]byte, 128)
		for {
			select {
			case <-sigio:
				for {
					n, err := syscall.Read(in, buf)
					if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
						break
					}
					input_comm <- input_event{buf[:n], err}
					ie := <-input_comm
					buf = ie.data[:128]
				}
			case <-quit:
				return
			}
		}
	}()

	return nil
}

// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
	quit <- 1
	out.WriteString(funcs[t_show_cursor])
	out.WriteString(funcs[t_sgr0])
	out.WriteString(funcs[t_clear_screen])
	out.WriteString(funcs[t_exit_ca])
	out.WriteString(funcs[t_exit_keypad])
	tcsetattr(out.Fd(), &orig_tios)

	out.Close()
	syscall.Close(in)

	// reset the state, so that on next Init() it will work again
	termw = 0
	termh = 0
	input_mode = InputEsc
	out = nil
	in = 0
	lastfg = attr_invalid
	lastbg = attr_invalid
	lastx = coord_invalid
	lasty = coord_invalid
	cursor_x = cursor_hidden
	cursor_y = cursor_hidden
	foreground = ColorDefault
	background = ColorDefault
}

// Synchronizes the internal back buffer with the terminal.
func Flush() error {
	// invalidate cursor position
	lastx = coord_invalid
	lasty = coord_invalid

	update_size_maybe()

	for y := 0; y < front_buffer.height; y++ {
		line_offset := y * front_buffer.width
		for x := 0; x < front_buffer.width; {
			cell_offset := line_offset + x
			back := &back_buffer.cells[cell_offset]
			front := &front_buffer.cells[cell_offset]
			if back.Ch < ' ' {
				back.Ch = ' '
			}
			w := rune_width(back.Ch)
			if *back == *front {
				x += w
				continue
			}
			*front = *back
			send_attr(back.Fg, back.Bg)

			if w == 2 && x == front_buffer.width - 1 {
				// there's not enough space for 2-cells rune,
				// let's just put a space in there
				send_char(x, y, ' ')
			} else {
				send_char(x, y, back.Ch)
				if w == 2 {
					next := cell_offset + 1
					front_buffer.cells[next] = Cell{
						Ch: 0,
						Fg: back.Fg,
						Bg: back.Bg,
					}
				}
			}
			x += w
		}
	}
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
	return flush()
}

// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
	if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_show_cursor])
	}

	if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_hide_cursor])
	}

	cursor_x, cursor_y = x, y
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
}

// The shortcut for SetCursor(-1, -1).
func HideCursor() {
	SetCursor(cursor_hidden, cursor_hidden)
}

// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
	if x < 0 || x >= back_buffer.width {
		return
	}
	if y < 0 || y >= back_buffer.height {
		return
	}

	back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}

// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
	return back_buffer.cells
}

// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
	var event Event

	// try to extract event from input buffer, return on success
	event.Type = EventKey
	if extract_event(&event) {
		return event
	}

	for {
		select {
		case ev := <-input_comm:
			if ev.err != nil {
				return Event{Type: EventError, Err: ev.err}
			}

			inbuf = append(inbuf, ev.data...)
			input_comm <- ev
			if extract_event(&event) {
				return event
			}
		case <-sigwinch:
			event.Type = EventResize
			event.Width, event.Height = get_term_size(out.Fd())
			return event
		}
	}
	panic("unreachable")
}

// Returns the size of the internal back buffer (which is the same as
// terminal's window size in characters).
func Size() (int, int) {
	return termw, termh
}

// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
	foreground, background = fg, bg
	err := update_size_maybe()
	back_buffer.clear()
	return err
}

// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
	if mode != InputCurrent {
		input_mode = mode
	}
	return input_mode
}
<|endoftext|>


<commit_before>package factom

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/FactomProject/FactomCode/wallet"
)

var server string = "http://localhost:8083"

func sha(b []byte) []byte {
	s := sha256.New()
	s.Write(b)
	return s.Sum(nil)
}

// PrintEntry is a helper function for debugging entry transport and encoding
func PrintEntry(e *Entry) {
	fmt.Println("ChainID:", hex.EncodeToString(e.ChainID))
	fmt.Println("ExtIDs:")
	for i := range e.ExtIDs {
		fmt.Println("	", string(e.ExtIDs[i]))
	}
	fmt.Println("Data:", string(e.Data))
}

// SetServer specifies the address of the server recieving the factom messages.
// It should be depricated by the final release once the p2p network has been
// implimented
func SetServer(s string) {
	server = s
}

// NewEntry creates a factom entry. It is supplied a string chain id, a []byte
// of data, and a series of string external ids for entry lookup
func NewEntry(cid string, eids []string, data []byte) (e *Entry, err error) {
	e = new(Entry)
	e.ChainID, err = hex.DecodeString(cid)
	if err != nil {
		return nil, err
	}
	e.Data = data
	for _, v := range eids {
		e.ExtIDs = append(e.ExtIDs, []byte(v))
	}
	return
}

// NewChain creates a factom chain from a []string chain name and a new entry
// to be the first entry of the new chain from []byte data, and a series of
// string external ids
func NewChain(name []string, eids []string, data []byte) (c *Chain, err error) {
	c = new(Chain)
	for _, v := range name {
		c.Name = append(c.Name, []byte(v))
	}
	c.GenerateID()
	e := c.FirstEntry
	e.ChainID = c.ChainID
	e.Data = data
	for _, v := range eids {
		e.ExtIDs = append(e.ExtIDs, []byte(v))
	}
	return
}

// CommitEntry sends a message to the factom network containing a hash of the
// entry to be used to verify the later RevealEntry.
func CommitEntry(e *Entry) error {
	var msg bytes.Buffer

	msg.Write([]byte{byte(time.Now().Unix())})
	msg.Write(sha(e.MarshalBinary()))
	sig := wallet.SignData(msg.Bytes())

	// msg.Bytes should be a int64 timestamp followed by a binary entry

	data := url.Values{
		"datatype":  {"commitentry"},
		"format":    {"binary"},
		"signature": {hex.EncodeToString((*sig.Sig)[:])},
		"data":      {hex.EncodeToString(msg.Bytes())},
	}
	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// RevealEntry sends a message to the factom network containing the binary
// encoded entry for the server to add it to the factom blockchain. The entry
// will be rejected if a CommitEntry was not done.
func RevealEntry(e *Entry) error {
	data := url.Values{
		"datatype": {"entry"},
		"format":   {"binary"},
		"entry":    {e.Hex()},
	}
	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// CommitChain sends a message to the factom network containing a series of
// hashes to be used to verify the later RevealChain.
func CommitChain(c *Chain) error {
	data := url.Values{
		"datatype": {"chainhash"},
		"format":   {"binary"},
		"data":     {c.Hash()},
	}

	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// RevealChain sends a message to the factom network containing the binary
// encoded first entry for a chain to be used by the server to add a new factom
// chain. It will be rejected if a CommitChain was not done.
func RevealChain(c *Chain) error {
	data := url.Values{
		"datatype": {"entry"},
		"format":   {"binary"},
		"data":     {c.FirstEntry.Hex()},
	}
	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// Submit wraps CommitEntry and RevealEntry. Submit takes a FactomWriter (an
// entry is a FactomWriter) and does a commit and reveal for the entry adding
// it to the factom blockchain.
func Submit(f FactomWriter) (err error) {
	e := f.CreateFactomEntry()
//	err = CommitEntry(e)
//	if err != nil {
//		return err
//	}
	err = RevealEntry(e)
	if err != nil {
		return err
	}
	return nil
}

// CreateChain takes a FactomChainer (a Chain is a FactomChainer) and calls
// commit and reveal to create the factom chain on the network.
func CreateChain(f FactomChainer) error {
	c := f.CreateFactomChain()
	err := CommitChain(c)
	if err != nil {
		return err
	}
	time.Sleep(1 * time.Minute)
	err = RevealChain(c)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>CommitEntry<commit_after>package factom

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/FactomProject/FactomCode/wallet"
)

var server string = "http://localhost:8083"

func sha(b []byte) []byte {
	s := sha256.New()
	s.Write(b)
	return s.Sum(nil)
}

// PrintEntry is a helper function for debugging entry transport and encoding
func PrintEntry(e *Entry) {
	fmt.Println("ChainID:", hex.EncodeToString(e.ChainID))
	fmt.Println("ExtIDs:")
	for i := range e.ExtIDs {
		fmt.Println("	", string(e.ExtIDs[i]))
	}
	fmt.Println("Data:", string(e.Data))
}

// SetServer specifies the address of the server recieving the factom messages.
// It should be depricated by the final release once the p2p network has been
// implimented
func SetServer(s string) {
	server = s
}

// NewEntry creates a factom entry. It is supplied a string chain id, a []byte
// of data, and a series of string external ids for entry lookup
func NewEntry(cid string, eids []string, data []byte) (e *Entry, err error) {
	e = new(Entry)
	e.ChainID, err = hex.DecodeString(cid)
	if err != nil {
		return nil, err
	}
	e.Data = data
	for _, v := range eids {
		e.ExtIDs = append(e.ExtIDs, []byte(v))
	}
	return
}

// NewChain creates a factom chain from a []string chain name and a new entry
// to be the first entry of the new chain from []byte data, and a series of
// string external ids
func NewChain(name []string, eids []string, data []byte) (c *Chain, err error) {
	c = new(Chain)
	for _, v := range name {
		c.Name = append(c.Name, []byte(v))
	}
	c.GenerateID()
	e := c.FirstEntry
	e.ChainID = c.ChainID
	e.Data = data
	for _, v := range eids {
		e.ExtIDs = append(e.ExtIDs, []byte(v))
	}
	return
}

// CommitEntry sends a message to the factom network containing a hash of the
// entry to be used to verify the later RevealEntry.
func CommitEntry(e *Entry) error {
	var msg bytes.Buffer

	msg.Write([]byte{byte(time.Now().Unix())})
	msg.WriteString(e.Hash())
	sig := wallet.SignData(msg.Bytes())

	// msg.Bytes should be a int64 timestamp followed by a binary entry

	data := url.Values{
		"datatype":  {"commitentry"},
		"format":    {"binary"},
		"signature": {hex.EncodeToString((*sig.Sig)[:])},
		"data":      {hex.EncodeToString(msg.Bytes())},
	}
	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// RevealEntry sends a message to the factom network containing the binary
// encoded entry for the server to add it to the factom blockchain. The entry
// will be rejected if a CommitEntry was not done.
func RevealEntry(e *Entry) error {
	data := url.Values{
		"datatype": {"entry"},
		"format":   {"binary"},
		"entry":    {e.Hex()},
	}
	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// CommitChain sends a message to the factom network containing a series of
// hashes to be used to verify the later RevealChain.
func CommitChain(c *Chain) error {
	data := url.Values{
		"datatype": {"chainhash"},
		"format":   {"binary"},
		"data":     {c.Hash()},
	}

	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// RevealChain sends a message to the factom network containing the binary
// encoded first entry for a chain to be used by the server to add a new factom
// chain. It will be rejected if a CommitChain was not done.
func RevealChain(c *Chain) error {
	data := url.Values{
		"datatype": {"entry"},
		"format":   {"binary"},
		"data":     {c.FirstEntry.Hex()},
	}
	_, err := http.PostForm(server, data)
	if err != nil {
		return err
	}
	return nil
}

// Submit wraps CommitEntry and RevealEntry. Submit takes a FactomWriter (an
// entry is a FactomWriter) and does a commit and reveal for the entry adding
// it to the factom blockchain.
func Submit(f FactomWriter) (err error) {
	e := f.CreateFactomEntry()
//	err = CommitEntry(e)
//	if err != nil {
//		return err
//	}
	err = RevealEntry(e)
	if err != nil {
		return err
	}
	return nil
}

// CreateChain takes a FactomChainer (a Chain is a FactomChainer) and calls
// commit and reveal to create the factom chain on the network.
func CreateChain(f FactomChainer) error {
	c := f.CreateFactomChain()
	err := CommitChain(c)
	if err != nil {
		return err
	}
	time.Sleep(1 * time.Minute)
	err = RevealChain(c)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package data

import (
	"github.com/uhero/rest-api/models"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"reflect"
	"testing"
)

func TestGetAllCategories(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	category1 := models.Category{
		Id:       1,
		Name:     "Summary",
		ParentId: 0,
	}
	category2 := models.Category{
		Id:       2,
		Name:     "Income",
		ParentId: 1,
	}
	categoryResult := sqlmock.NewRows([]string{"id", "name", "parentId"}).
		AddRow(category1.Id, category1.Name, category1.ParentId).
		AddRow(category2.Id, category2.Name, category2.ParentId)
	mock.ExpectQuery("SELECT id, name, parent_id FROM categories").
		WillReturnRows(categoryResult)

	categoryRepository := CategoryRepository{DB: db}

	categories, err := categoryRepository.GetAll()
	if err != nil {
		t.Fail()
	}
	if len(categories) != 2 ||
		!reflect.DeepEqual(categories[0], category1) ||
		!reflect.DeepEqual(categories[1], category2) {
		t.Fail()
	}
}

<commit_msg>tests passing<commit_after>package data

import (
	"github.com/uhero/rest-api/models"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"
	"database/sql"
)

func TestGetAllCategories(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	category1 := models.CategoryWithAncestry{
		Id:       1,
		Name:     "Summary",
		Ancestry: sql.NullString{Valid: false},
	}
	category2 := models.CategoryWithAncestry{
		Id:       2,
		Name:     "Income",
		Ancestry: sql.NullString{Valid: true, String: "1"},
	}
	categoryResult := sqlmock.NewRows([]string{"id", "name", "ancestry"}).
		AddRow(category1.Id, category1.Name, nil).
		AddRow(category2.Id, category2.Name, category2.Ancestry.String)
	mock.ExpectQuery("SELECT id, name, ancestry FROM categories").
		WillReturnRows(categoryResult)

	categoryRepository := CategoryRepository{DB: db}

	categories, err := categoryRepository.GetAllCategories()
	if err != nil {
		t.Fail()
	}
	if len(categories) != 2 ||
		categories[0].Name != category1.Name ||
		categories[1].Id != category2.Id {
		t.Fail()
	}
}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime/pprof"
	"sync"
)

func main() {

	f, err := os.Create("/tmp/cpuprof")
	if err != nil {
		panic(err)
	}
	pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()

	app := cli.App("restorage", "A RESTful storage API with pluggable backends")
	port := app.IntOpt("port", 8080, "Port to listen on")

	app.Command("elastic", "use the elastic search backend", func(cmd *cli.Cmd) {
		url := cmd.StringArg("URL", "", "elastic search endpoint url")
		cmd.Action = func() {
			println(*url)
			serve(NewElasticEngine(*url), *port)
		}

	})

	app.Command("mongo", "use the mongodb backend", func(cmd *cli.Cmd) {
		hostports := cmd.StringArg("HOSTS", "", "hostname1:port1,hostname2:port2...")
		cmd.Action = func() {
			serve(NewMongoEngine("store", *hostports), *port)
		}

	})

	app.Run(os.Args)

}

func serve(engine Engine, port int) {
	ah := apiHandlers{engine}

	m := mux.NewRouter()
	http.Handle("/", handlers.CombinedLoggingHandler(os.Stdout, m))

	m.HandleFunc("/{collection}/_count", ah.countHandler).Methods("GET")
	m.HandleFunc("/{collection}/{id}", ah.idReadHandler).Methods("GET")
	m.HandleFunc("/{collection}/{id}", ah.idWriteHandler).Methods("PUT")
	m.HandleFunc("/{collection}/", ah.dropHandler).Methods("DELETE")
	m.HandleFunc("/{collection}/", ah.putAllHandler).Methods("PUT")
	m.HandleFunc("/{collection}/", ah.dumpAll).Methods("GET")

	go func() {
		fmt.Printf("listening on %d\n", port)
		err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
		if err != nil {
			log.Printf("web server failed: %v\n", err)
		}
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// wait for ctrl-c
	<-c
	println("exiting")
	engine.Close()

	f, err := os.Create("/tmp/memprof")
	if err != nil {
		panic(err)
	}

	pprof.WriteHeapProfile(f)
	f.Close()

	return
}

type apiHandlers struct {
	engine Engine
}

func (ah *apiHandlers) idReadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	collection := vars["collection"]

	found, art, err := ah.engine.Load(collection, id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	if !found {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf("document with id %s was not found\n", id)))
		return
	}
	w.Header().Add("Content-Type", "application/json")
	enc := json.NewEncoder(w)
	enc.Encode(art)
}

func (ah *apiHandlers) putAllHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]

	errCh := make(chan error, 2)
	docCh := make(chan Document)

	var wg sync.WaitGroup

	wg.Add(1)

	go func() {
		defer wg.Done()
		defer close(docCh)

		dec := json.NewDecoder(r.Body) //TODO: bufio?
		for {
			var doc Document
			err := dec.Decode(&doc)
			if err == io.EOF {
				return
			}
			if err != nil {
				errCh <- err
				log.Printf("failed to decode json. aborting: %v\n", err.Error())
				return
			}
			docCh <- doc
		}

	}()

	for x := 0; x < 8; x++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for doc := range docCh {
				err := ah.engine.Write(collection, getID(doc), doc)
				if err != nil {
					errCh <- err
					return
				}
			}
		}()
	}

	wg.Wait()

	select {
	case err := <-errCh:
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	default:
		println("returning normally")
		return
	}

}

func getID(doc Document) string {
	// TODO: obviously this should be parameterised
	if id, ok := doc["uuid"].(string); ok {
		return id
	}
	if id, ok := doc["id"].(string); ok {
		return id
	}
	panic("no id")
}

func (ah *apiHandlers) idWriteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	collection := vars["collection"]

	var doc Document
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&doc)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if getID(doc) != id {
		http.Error(w, "id does not match", http.StatusBadRequest)
		return
	}

	err = ah.engine.Write(collection, id, doc)
	if err != nil {
		http.Error(w, fmt.Sprintf("write failed:\n%v\n", err), http.StatusInternalServerError)
		return
	}
}

func (ah *apiHandlers) dropHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]
	ah.engine.Drop(collection)
}

func (ah *apiHandlers) dumpAll(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]

	enc := json.NewEncoder(w)
	stop := make(chan struct{})
	defer close(stop)
	all, err := ah.engine.All(collection, stop)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	for doc := range all {
		enc.Encode(doc)
		fmt.Fprint(w, "\n")
	}
}

func (ah *apiHandlers) countHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]
	fmt.Fprintf(w, "%d\n", ah.engine.Count(collection))
}
<commit_msg>allow setting of the mongodb database name<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime/pprof"
	"sync"
)

func main() {

	f, err := os.Create("/tmp/cpuprof")
	if err != nil {
		panic(err)
	}
	pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()

	app := cli.App("restorage", "A RESTful storage API with pluggable backends")
	port := app.IntOpt("port", 8080, "Port to listen on")

	app.Command("elastic", "use the elastic search backend", func(cmd *cli.Cmd) {
		url := cmd.StringArg("URL", "", "elastic search endpoint url")
		cmd.Action = func() {
			println(*url)
			serve(NewElasticEngine(*url), *port)
		}

	})

	app.Command("mongo", "use the mongodb backend", func(cmd *cli.Cmd) {
		hostports := cmd.StringArg("HOSTS", "", "hostname1:port1,hostname2:port2...")
		dbname := cmd.StringOpt("dbname", "store", "database name")
		cmd.Action = func() {
			serve(NewMongoEngine(*dbname, *hostports), *port)
		}

	})

	app.Run(os.Args)

}

func serve(engine Engine, port int) {
	ah := apiHandlers{engine}

	m := mux.NewRouter()
	http.Handle("/", handlers.CombinedLoggingHandler(os.Stdout, m))

	m.HandleFunc("/{collection}/_count", ah.countHandler).Methods("GET")
	m.HandleFunc("/{collection}/{id}", ah.idReadHandler).Methods("GET")
	m.HandleFunc("/{collection}/{id}", ah.idWriteHandler).Methods("PUT")
	m.HandleFunc("/{collection}/", ah.dropHandler).Methods("DELETE")
	m.HandleFunc("/{collection}/", ah.putAllHandler).Methods("PUT")
	m.HandleFunc("/{collection}/", ah.dumpAll).Methods("GET")

	go func() {
		fmt.Printf("listening on %d\n", port)
		err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
		if err != nil {
			log.Printf("web server failed: %v\n", err)
		}
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// wait for ctrl-c
	<-c
	println("exiting")
	engine.Close()

	f, err := os.Create("/tmp/memprof")
	if err != nil {
		panic(err)
	}

	pprof.WriteHeapProfile(f)
	f.Close()

	return
}

type apiHandlers struct {
	engine Engine
}

func (ah *apiHandlers) idReadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	collection := vars["collection"]

	found, art, err := ah.engine.Load(collection, id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	if !found {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf("document with id %s was not found\n", id)))
		return
	}
	w.Header().Add("Content-Type", "application/json")
	enc := json.NewEncoder(w)
	enc.Encode(art)
}

func (ah *apiHandlers) putAllHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]

	errCh := make(chan error, 2)
	docCh := make(chan Document)

	var wg sync.WaitGroup

	wg.Add(1)

	go func() {
		defer wg.Done()
		defer close(docCh)

		dec := json.NewDecoder(r.Body) //TODO: bufio?
		for {
			var doc Document
			err := dec.Decode(&doc)
			if err == io.EOF {
				return
			}
			if err != nil {
				errCh <- err
				log.Printf("failed to decode json. aborting: %v\n", err.Error())
				return
			}
			docCh <- doc
		}

	}()

	for x := 0; x < 8; x++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for doc := range docCh {
				err := ah.engine.Write(collection, getID(doc), doc)
				if err != nil {
					errCh <- err
					return
				}
			}
		}()
	}

	wg.Wait()

	select {
	case err := <-errCh:
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	default:
		println("returning normally")
		return
	}

}

func getID(doc Document) string {
	// TODO: obviously this should be parameterised
	if id, ok := doc["uuid"].(string); ok {
		return id
	}
	if id, ok := doc["id"].(string); ok {
		return id
	}
	panic("no id")
}

func (ah *apiHandlers) idWriteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	collection := vars["collection"]

	var doc Document
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&doc)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if getID(doc) != id {
		http.Error(w, "id does not match", http.StatusBadRequest)
		return
	}

	err = ah.engine.Write(collection, id, doc)
	if err != nil {
		http.Error(w, fmt.Sprintf("write failed:\n%v\n", err), http.StatusInternalServerError)
		return
	}
}

func (ah *apiHandlers) dropHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]
	ah.engine.Drop(collection)
}

func (ah *apiHandlers) dumpAll(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]

	enc := json.NewEncoder(w)
	stop := make(chan struct{})
	defer close(stop)
	all, err := ah.engine.All(collection, stop)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	for doc := range all {
		enc.Encode(doc)
		fmt.Fprint(w, "\n")
	}
}

func (ah *apiHandlers) countHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	collection := vars["collection"]
	fmt.Fprintf(w, "%d\n", ah.engine.Count(collection))
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"html/template"
	"net/http"
	"fmt"
)

var templates = template.Must(template.ParseFiles("templates/index.html"))

func index(w http.ResponseWriter, r *http.Request) {
	err := templates.ExecuteTemplate(w, "index.html", "")
	if err != nil {
		panic(err)
	}
}

var i = 0

func counter(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "{\"counter\":%d}", i);
	i += 1
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", index)
	r.HandleFunc("/counter", counter)
	r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))

	err := http.ListenAndServe(":8080", r)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Apply gofmt<commit_after>package main

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"html/template"
	"net/http"
)

var templates = template.Must(template.ParseFiles("templates/index.html"))

func index(w http.ResponseWriter, r *http.Request) {
	err := templates.ExecuteTemplate(w, "index.html", "")
	if err != nil {
		panic(err)
	}
}

var i = 0

func counter(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "{\"counter\":%d}", i);
	i += 1
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", index)
	r.HandleFunc("/counter", counter)
	r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))

	err := http.ListenAndServe(":8080", r)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/samuelkadolph/go/phidgets"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

type Config struct {
	Doors           []*Door
	FeedbackTimeout int
	Secret          string
}

type Door struct {
	ID           string
	Lock         *int
	LockFeedback *int
	Mag          *int
	MagFeedback  *int
	Name         string

	lockCond  *sync.Cond
	lockMutex *sync.Mutex
	magCond   *sync.Cond
	magMutex  *sync.Mutex
}

type hash map[string]interface{}

var config *Config
var ifk *phidgets.InterfaceKit

var host = flag.String("host", "", "Host for the server to listen on")
var configPath = flag.String("config", "./config.json", "Path to config file")
var port = flag.Int("port", 4567, "Port for the server to listen on")

func DoorIndex(w http.ResponseWriter, r *http.Request) {
	response(w, 200, config.Doors)
}

func DoorMagDisengage(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}
	if !checkDoorMag(w, r, d) {
		return
	}

	if err := d.MagDisengage(); err != nil {
		response(w, 200, hash{"success": false, "error": err})
	} else {
		response(w, 200, hash{"success": true})
	}
}

func DoorMagEngage(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}
	if !checkDoorMag(w, r, d) {
		return
	}

	if err := d.MagEngage(); err != nil {
		response(w, 200, hash{"success": false, "error": err})
	} else {
		response(w, 200, hash{"success": true})
	}
}

func DoorUnlock(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}
	if d.Lock == nil {
		response(w, 422, hash{"error": "door does not support opening"})
		return
	}

	ch, err := d.Unlock()

	if err == nil {
		err = <-ch
	}

	if err != nil {
		response(w, 200, hash{"success": false, "error": err})
	} else {
		response(w, 200, hash{"success": true})
	}
}

func DoorShow(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}

	response(w, 200, d)
}

func NotFound(w http.ResponseWriter, r *http.Request) {
	response(w, 404, hash{"error": "not found"})
}

func Root(w http.ResponseWriter, r *http.Request) {
	response(w, 200, hash{"hi": true})
}

func (d *Door) LockStatus() string {
	var err error
	var s bool

	if d.LockFeedback != nil {
		s, err = ifk.Inputs[*d.LockFeedback].State()
	} else if d.Lock != nil {
		s, err = ifk.Outputs[*d.Lock].State()
	} else {
		return "unsupported"
	}

	if err != nil {
		return "error"
	} else if s {
		return "unlocked"
	}

	return "locked"
}

func (d *Door) MagDisengage() error {
	return ifk.Outputs[*d.Mag].SetState(false)
}

func (d *Door) MagEngage() error {
	return ifk.Outputs[*d.Mag].SetState(true)
}

func (d *Door) MagStatus() string {
	var err error
	var s bool

	if d.MagFeedback != nil {
		s, err = ifk.Inputs[*d.MagFeedback].State()
	} else if d.Mag != nil {
		s, err = ifk.Outputs[*d.Mag].State()
	} else {
		return "unsupported"
	}

	if err != nil {
		return "error"
	} else if s {
		return "engaged"
	}

	return "disengaged"
}

func (d *Door) MarshalJSON() ([]byte, error) {
	o := make(map[string]interface{})

	o["id"] = d.ID
	o["lock"] = d.LockStatus()
	o["mag"] = d.MagStatus()
	o["name"] = d.Name

	return json.Marshal(o)
}

func (d *Door) Unlock() (<-chan error, error) {
	var err error

	ch := make(chan error, 1)

	if err = ifk.Outputs[*d.Lock].SetState(true); err != nil {
		return nil, err
	}

	time.Sleep(200 * time.Millisecond)

	if err = ifk.Outputs[*d.Lock].SetState(false); err != nil {
		return nil, err
	}

	ch <- nil

	return ch, nil
}

func checkDoor(w http.ResponseWriter, r *http.Request, o **Door) bool {
	v := mux.Vars(r)
	d := findDoor(v["door"])

	if d == nil {
		response(w, 404, hash{"error": "door not found"})
		return false
	}

	*o = d
	return true
}

func checkDoorMag(w http.ResponseWriter, r *http.Request, d *Door) bool {
	if d.Mag == nil {
		response(w, 422, hash{"error": "door does not support mag"})
		return false
	}

	return true
}

func checkSecret(w http.ResponseWriter, r *http.Request) bool {
	r.ParseForm()

	if config.Secret != r.Form.Get("secret") {
		response(w, 403, hash{"error": "bad secret"})
		return false
	}

	return true
}

func findDoor(id string) *Door {
	for _, d := range config.Doors {
		if d.ID == id {
			return d
		}
	}
	return nil
}

func response(w http.ResponseWriter, s int, b interface{}) {
	o, _ := json.Marshal(b)

	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(o)))
	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(s)
	w.Write(o)
}

func loadConfig(path string) (*Config, error) {
	var config Config
	var err error
	var file []byte

	if file, err = ioutil.ReadFile(path); err != nil {
		return nil, err
	}

	if err = json.Unmarshal(file, &config); err != nil {
		return nil, err
	}

	if config.Doors == nil {
		config.Doors = make([]*Door, 0)
	}

	return &config, nil
}

func loadInterfaceKit() (*phidgets.InterfaceKit, error) {
	var err error
	var ifk *phidgets.InterfaceKit

	if ifk, err = phidgets.NewInterfaceKit(); err != nil {
		return nil, err
	}

	if err = ifk.Open(phidgets.Any); err != nil {
		return nil, err
	}

	// if err = ifk.WaitForAttachment(2 * time.Second); err != nil {
	// 	return nil, err
	// }

	return ifk, nil
}

func main() {
	var err error

	flag.Parse()

	if config, err = loadConfig(*configPath); err != nil {
		log.Fatalf("Error while loading config - %s", err)
	}

	if ifk, err = loadInterfaceKit(); err != nil {
		log.Fatalf("Error while loading interface kit - %s", err)
	}

	r := mux.NewRouter()
	r.NewRoute().Methods("POST").Path("/doors/{door}/mag/disengage").Handler(http.HandlerFunc(DoorMagDisengage))
	r.NewRoute().Methods("POST").Path("/doors/{door}/mag/engage").Handler(http.HandlerFunc(DoorMagEngage))
	r.NewRoute().Methods("POST").Path("/doors/{door}/open").Handler(http.HandlerFunc(DoorUnlock))
	r.NewRoute().Methods("POST").Path("/doors/{door}/unlock").Handler(http.HandlerFunc(DoorUnlock))
	r.NewRoute().Methods("GET").Path("/doors/{door}").Handler(http.HandlerFunc(DoorShow))
	r.NewRoute().Methods("GET").Path("/doors").Handler(http.HandlerFunc(DoorIndex))
	r.NewRoute().Methods("GET").Path("/").Handler(http.HandlerFunc(Root))
	r.NewRoute().Handler(http.HandlerFunc(NotFound))

	a := fmt.Sprintf("%s:%d", *host, *port)
	h := handlers.CombinedLoggingHandler(os.Stdout, r)

	log.Fatalf("Error while starting server %s", http.ListenAndServe(a, h))
}
<commit_msg>Add Floor to Door and make Name optional<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/samuelkadolph/go/phidgets"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

type Config struct {
	Doors           []*Door
	FeedbackTimeout int
	Secret          string
}

type Door struct {
	Floor        *string
	ID           string
	Lock         *int
	LockFeedback *int
	Mag          *int
	MagFeedback  *int
	Name         *string

	lockCond  *sync.Cond
	lockMutex *sync.Mutex
	magCond   *sync.Cond
	magMutex  *sync.Mutex
}

type hash map[string]interface{}

var config *Config
var ifk *phidgets.InterfaceKit

var host = flag.String("host", "", "Host for the server to listen on")
var configPath = flag.String("config", "./config.json", "Path to config file")
var port = flag.Int("port", 4567, "Port for the server to listen on")

func DoorIndex(w http.ResponseWriter, r *http.Request) {
	response(w, 200, config.Doors)
}

func DoorMagDisengage(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}
	if !checkDoorMag(w, r, d) {
		return
	}

	if err := d.MagDisengage(); err != nil {
		response(w, 200, hash{"success": false, "error": err})
	} else {
		response(w, 200, hash{"success": true})
	}
}

func DoorMagEngage(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}
	if !checkDoorMag(w, r, d) {
		return
	}

	if err := d.MagEngage(); err != nil {
		response(w, 200, hash{"success": false, "error": err})
	} else {
		response(w, 200, hash{"success": true})
	}
}

func DoorUnlock(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}
	if d.Lock == nil {
		response(w, 422, hash{"error": "door does not support opening"})
		return
	}

	ch, err := d.Unlock()

	if err == nil {
		err = <-ch
	}

	if err != nil {
		response(w, 200, hash{"success": false, "error": err})
	} else {
		response(w, 200, hash{"success": true})
	}
}

func DoorShow(w http.ResponseWriter, r *http.Request) {
	var d *Door

	if !checkSecret(w, r) {
		return
	}
	if !checkDoor(w, r, &d) {
		return
	}

	response(w, 200, d)
}

func NotFound(w http.ResponseWriter, r *http.Request) {
	response(w, 404, hash{"error": "not found"})
}

func Root(w http.ResponseWriter, r *http.Request) {
	response(w, 200, hash{"hi": true})
}

func (d *Door) LockStatus() string {
	var err error
	var s bool

	if d.LockFeedback != nil {
		s, err = ifk.Inputs[*d.LockFeedback].State()
	} else if d.Lock != nil {
		s, err = ifk.Outputs[*d.Lock].State()
	} else {
		return "unsupported"
	}

	if err != nil {
		return "error"
	} else if s {
		return "unlocked"
	}

	return "locked"
}

func (d *Door) MagDisengage() error {
	return ifk.Outputs[*d.Mag].SetState(false)
}

func (d *Door) MagEngage() error {
	return ifk.Outputs[*d.Mag].SetState(true)
}

func (d *Door) MagStatus() string {
	var err error
	var s bool

	if d.MagFeedback != nil {
		s, err = ifk.Inputs[*d.MagFeedback].State()
	} else if d.Mag != nil {
		s, err = ifk.Outputs[*d.Mag].State()
	} else {
		return "unsupported"
	}

	if err != nil {
		return "error"
	} else if s {
		return "engaged"
	}

	return "disengaged"
}

func (d *Door) MarshalJSON() ([]byte, error) {
	o := make(map[string]interface{})

	o["id"] = d.ID
	o["lock"] = d.LockStatus()
	o["mag"] = d.MagStatus()
	o["name"] = d.Name

	return json.Marshal(o)
}

func (d *Door) Unlock() (<-chan error, error) {
	var err error

	ch := make(chan error, 1)

	if err = ifk.Outputs[*d.Lock].SetState(true); err != nil {
		return nil, err
	}

	time.Sleep(200 * time.Millisecond)

	if err = ifk.Outputs[*d.Lock].SetState(false); err != nil {
		return nil, err
	}

	ch <- nil

	return ch, nil
}

func checkDoor(w http.ResponseWriter, r *http.Request, o **Door) bool {
	v := mux.Vars(r)
	d := findDoor(v["door"])

	if d == nil {
		response(w, 404, hash{"error": "door not found"})
		return false
	}

	*o = d
	return true
}

func checkDoorMag(w http.ResponseWriter, r *http.Request, d *Door) bool {
	if d.Mag == nil {
		response(w, 422, hash{"error": "door does not support mag"})
		return false
	}

	return true
}

func checkSecret(w http.ResponseWriter, r *http.Request) bool {
	r.ParseForm()

	if config.Secret != r.Form.Get("secret") {
		response(w, 403, hash{"error": "bad secret"})
		return false
	}

	return true
}

func findDoor(id string) *Door {
	for _, d := range config.Doors {
		if d.ID == id {
			return d
		}
	}
	return nil
}

func response(w http.ResponseWriter, s int, b interface{}) {
	o, _ := json.Marshal(b)

	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(o)))
	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(s)
	w.Write(o)
}

func loadConfig(path string) (*Config, error) {
	var config Config
	var err error
	var file []byte

	if file, err = ioutil.ReadFile(path); err != nil {
		return nil, err
	}

	if err = json.Unmarshal(file, &config); err != nil {
		return nil, err
	}

	if config.Doors == nil {
		config.Doors = make([]*Door, 0)
	}

	return &config, nil
}

func loadInterfaceKit() (*phidgets.InterfaceKit, error) {
	var err error
	var ifk *phidgets.InterfaceKit

	if ifk, err = phidgets.NewInterfaceKit(); err != nil {
		return nil, err
	}

	if err = ifk.Open(phidgets.Any); err != nil {
		return nil, err
	}

	// if err = ifk.WaitForAttachment(2 * time.Second); err != nil {
	// 	return nil, err
	// }

	return ifk, nil
}

func main() {
	var err error

	flag.Parse()

	if config, err = loadConfig(*configPath); err != nil {
		log.Fatalf("Error while loading config - %s", err)
	}

	if ifk, err = loadInterfaceKit(); err != nil {
		log.Fatalf("Error while loading interface kit - %s", err)
	}

	r := mux.NewRouter()
	r.NewRoute().Methods("POST").Path("/doors/{door}/mag/disengage").Handler(http.HandlerFunc(DoorMagDisengage))
	r.NewRoute().Methods("POST").Path("/doors/{door}/mag/engage").Handler(http.HandlerFunc(DoorMagEngage))
	r.NewRoute().Methods("POST").Path("/doors/{door}/open").Handler(http.HandlerFunc(DoorUnlock))
	r.NewRoute().Methods("POST").Path("/doors/{door}/unlock").Handler(http.HandlerFunc(DoorUnlock))
	r.NewRoute().Methods("GET").Path("/doors/{door}").Handler(http.HandlerFunc(DoorShow))
	r.NewRoute().Methods("GET").Path("/doors").Handler(http.HandlerFunc(DoorIndex))
	r.NewRoute().Methods("GET").Path("/").Handler(http.HandlerFunc(Root))
	r.NewRoute().Handler(http.HandlerFunc(NotFound))

	a := fmt.Sprintf("%s:%d", *host, *port)
	h := handlers.CombinedLoggingHandler(os.Stdout, r)

	log.Fatalf("Error while starting server %s", http.ListenAndServe(a, h))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/training_project/database"
	"github.com/training_project/model/shop"
	redis "gopkg.in/redis.v4"
)

func main() {
	testCallingMethod()
}

func testCallingMethod() {
	listConnection := database.SystemConnection()
	//getting redis connection
	redisConn := listConnection["redis"].(*redis.Client)

	activeSeller := model.ActiveSeller{
		Date:   "11-01-2017",
		ShopId: 124,
	}
	activeSeller.GetConn(redisConn)
	activeSeller.InsertActiveSeller()
}

//func testRedis() {
//	database.InitRedisDb()
//	//insert Seller 1
//	database.InsertActiveSellerDaily(1)
//	database.InsertActiveSellerDaily(4)
//	database.InsertActiveSellerDaily(5)
//	database.InsertActiveSellerDaily(8)
//	database.InsertActiveSellerDaily(11)
//	database.InsertActiveSellerDaily(111)
//	database.InsertActiveSellerDaily(211)
//	database.InsertActiveSellerDaily(1211)
//	database.InsertActiveSellerDaily(91211)
//	database.InsertActiveSellerDaily(4294967295)
//	database.InsertActiveSellerDaily(4294967296)
//	database.InsertActiveSellerDaily(9294967296)
//
//	t := time.Now().Local()
//	formatTime := t.Format("2006-01-02")
//	database.GetActiveSellerByte(formatTime)
//
//}
//
//func testAPI() {
//	database.InitMysqlDb()
//	log.Printf("App starting ...")
//	router := httprouter.New()
//
//	router.GET("/v1/talks", handler.ReadTalks)
//	router.POST("/v1/talks", handler.WriteTalks)
//
//	log.Printf("App listen on 3000")
//	log.Fatal(http.ListenAndServe(":3000", router))
//}
<commit_msg>beautify main<commit_after>package main

import (
	"github.com/training_project/database"
	"github.com/training_project/model/shop"
	redis "gopkg.in/redis.v4"
)

func main() {
	//getting list of all the connection
	listConnection := database.SystemConnection()

	//getting redis connection
	redisConn := listConnection["redis"].(*redis.Client)

	activeSeller := model.ActiveSeller{
		Date:   "11-01-2017",
		ShopId: 124,
	}

	activeSeller.GetConn(redisConn)
	activeSeller.InsertActiveSeller()
}
<|endoftext|>


<commit_before>// Package grpchelper wraps the adaptors implemented by package grpcclientidentity into a less flexible API
// which, however, ensures that the individual adaptor primitive's expectations are met and hence do not panic.
package grpchelper

import (
	"context"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"

	"github.com/zrepl/zrepl/logger"
	"github.com/zrepl/zrepl/rpc/grpcclientidentity"
	"github.com/zrepl/zrepl/rpc/netadaptor"
	"github.com/zrepl/zrepl/transport"
)

// The following constants are relevant for interoperability.
// We use the same values for client & server, because zrepl is more
// symmetrical ("one source, one sink") instead of the typical
// gRPC scenario ("many clients, single server")
const (
	StartKeepalivesAfterInactivityDuration = 5 * time.Second
	KeepalivePeerTimeout                   = 10 * time.Second
)

type Logger = logger.Logger

// ClientConn is an easy-to-use wrapper around the Dialer and TransportCredentials interface
// to produce a grpc.ClientConn
func ClientConn(cn transport.Connecter, log Logger) *grpc.ClientConn {
	ka := grpc.WithKeepaliveParams(keepalive.ClientParameters{
		Time:                StartKeepalivesAfterInactivityDuration,
		Timeout:             KeepalivePeerTimeout,
		PermitWithoutStream: true,
	})
	dialerOption := grpc.WithDialer(grpcclientidentity.NewDialer(log, cn))
	cred := grpc.WithTransportCredentials(grpcclientidentity.NewTransportCredentials(log))
	ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) // FIXME constant
	defer cancel()
	cc, err := grpc.DialContext(ctx, "doesn't matter done by dialer", dialerOption, cred, ka)
	if err != nil {
		log.WithError(err).Error("cannot create gRPC client conn (non-blocking)")
		// It's ok to panic here: the we call grpc.DialContext without the
		// (grpc.WithBlock) dial option, and at the time of writing, the grpc
		// docs state that no connection attempt is made in that case.
		// Hence, any error that occurs is due to DialOptions or similar,
		// and thus indicative of an implementation error.
		panic(err)
	}
	return cc
}

// NewServer is a convenience interface around the TransportCredentials and Interceptors interface.
func NewServer(authListenerFactory transport.AuthenticatedListenerFactory, clientIdentityKey interface{}, logger grpcclientidentity.Logger) (srv *grpc.Server, serve func() error, err error) {
	ka := grpc.KeepaliveParams(keepalive.ServerParameters{
		Time:    StartKeepalivesAfterInactivityDuration,
		Timeout: KeepalivePeerTimeout,
	})
	tcs := grpcclientidentity.NewTransportCredentials(logger)
	unary, stream := grpcclientidentity.NewInterceptors(logger, clientIdentityKey)
	srv = grpc.NewServer(grpc.Creds(tcs), grpc.UnaryInterceptor(unary), grpc.StreamInterceptor(stream), ka)

	serve = func() error {
		l, err := authListenerFactory()
		if err != nil {
			return err
		}
		if err := srv.Serve(netadaptor.New(l, logger)); err != nil {
			return err
		}
		return nil
	}

	return srv, serve, nil
}
<commit_msg>rpc/grpcclientidentity: remove hard-coded deadline in listener adatper causing crash<commit_after>// Package grpchelper wraps the adaptors implemented by package grpcclientidentity into a less flexible API
// which, however, ensures that the individual adaptor primitive's expectations are met and hence do not panic.
package grpchelper

import (
	"context"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"

	"github.com/zrepl/zrepl/logger"
	"github.com/zrepl/zrepl/rpc/grpcclientidentity"
	"github.com/zrepl/zrepl/rpc/netadaptor"
	"github.com/zrepl/zrepl/transport"
)

// The following constants are relevant for interoperability.
// We use the same values for client & server, because zrepl is more
// symmetrical ("one source, one sink") instead of the typical
// gRPC scenario ("many clients, single server")
const (
	StartKeepalivesAfterInactivityDuration = 5 * time.Second
	KeepalivePeerTimeout                   = 10 * time.Second
)

type Logger = logger.Logger

// ClientConn is an easy-to-use wrapper around the Dialer and TransportCredentials interface
// to produce a grpc.ClientConn
func ClientConn(cn transport.Connecter, log Logger) *grpc.ClientConn {
	ka := grpc.WithKeepaliveParams(keepalive.ClientParameters{
		Time:                StartKeepalivesAfterInactivityDuration,
		Timeout:             KeepalivePeerTimeout,
		PermitWithoutStream: true,
	})
	dialerOption := grpc.WithDialer(grpcclientidentity.NewDialer(log, cn))
	cred := grpc.WithTransportCredentials(grpcclientidentity.NewTransportCredentials(log))
	cc, err := grpc.DialContext(context.Background(), "doesn't matter done by dialer", dialerOption, cred, ka)
	if err != nil {
		log.WithError(err).Error("cannot create gRPC client conn (non-blocking)")
		// It's ok to panic here: the we call grpc.DialContext without the
		// (grpc.WithBlock) dial option, and at the time of writing, the grpc
		// docs state that no connection attempt is made in that case.
		// Hence, any error that occurs is due to DialOptions or similar,
		// and thus indicative of an implementation error.
		panic(err)
	}
	return cc
}

// NewServer is a convenience interface around the TransportCredentials and Interceptors interface.
func NewServer(authListenerFactory transport.AuthenticatedListenerFactory, clientIdentityKey interface{}, logger grpcclientidentity.Logger) (srv *grpc.Server, serve func() error, err error) {
	ka := grpc.KeepaliveParams(keepalive.ServerParameters{
		Time:    StartKeepalivesAfterInactivityDuration,
		Timeout: KeepalivePeerTimeout,
	})
	tcs := grpcclientidentity.NewTransportCredentials(logger)
	unary, stream := grpcclientidentity.NewInterceptors(logger, clientIdentityKey)
	srv = grpc.NewServer(grpc.Creds(tcs), grpc.UnaryInterceptor(unary), grpc.StreamInterceptor(stream), ka)

	serve = func() error {
		l, err := authListenerFactory()
		if err != nil {
			return err
		}
		if err := srv.Serve(netadaptor.New(l, logger)); err != nil {
			return err
		}
		return nil
	}

	return srv, serve, nil
}
<|endoftext|>