<commit_before>package orderer

import (
	"fmt"
	"net"
	"os"
	"strings"

	context "golang.org/x/net/context"

	"time"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/orderer/proto"
	"github.com/ncodes/cocoon/core/scheduler"
	"github.com/ncodes/cocoon/core/types"
	"github.com/ncodes/cstructs"
	logging "github.com/op/go-logging"
	"google.golang.org/grpc"
)

var log = logging.MustGetLogger("orderer")

// SetLogLevel sets the log level of the logger
func SetLogLevel(l logging.Level) {
	logging.SetLevel(l, log.Module)
}

// DiscoverOrderers fetches a list of orderer service addresses
// via consul service discovery API. For development purpose,
// If DEV_ORDERER_ADDR is set, it will fetch the orderer
// address from the env variable.
func DiscoverOrderers() ([]string, error) {

	if len(os.Getenv("DEV_ORDERER_ADDR")) > 0 {
		return []string{os.Getenv("DEV_ORDERER_ADDR")}, nil
	}

	ds := scheduler.NomadServiceDiscovery{
		ConsulAddr: util.Env("CONSUL_ADDR", "localhost:8500"),
		Protocol:   "http",
	}

	_orderers, err := ds.GetByID("orderers", nil)
	if err != nil {
		return []string{}, nil
	}

	var orderers []string
	for _, orderer := range _orderers {
		orderers = append(orderers, fmt.Sprintf("%s:%f", orderer.IP, orderer.Port))
	}

	return orderers, nil
}

// DialOrderer returns a connection to a orderer from a list of addresses. It randomly
// picks an orderer address from the list for orderers.
func DialOrderer(ordererAddrs []string) (*grpc.ClientConn, error) {
	var ordererAddr string

	if len(ordererAddrs) == 0 {
		return nil, fmt.Errorf("no known orderer address")
	} else if len(ordererAddrs) == 1 {
		ordererAddr = ordererAddrs[0]
	} else {
		ordererAddr = ordererAddrs[util.RandNum(0, len(ordererAddrs))]
	}

	client, err := grpc.Dial(ordererAddr, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	return client, nil
}

// Orderer defines a transaction ordering, block creation
// and inclusion module
type Orderer struct {
	server     *grpc.Server
	store      types.Store
	blockchain types.Blockchain
	endedCh    chan bool
}

// NewOrderer creates a new Orderer object
func NewOrderer() *Orderer {
	return new(Orderer)
}

// Start starts the order service
func (od *Orderer) Start(addr, storeConStr string, endedCh chan bool) {

	od.endedCh = endedCh

	lis, err := net.Listen("tcp", fmt.Sprintf("%s", addr))
	if err != nil {
		log.Fatalf("failed to listen on port=%s. Err: %s", strings.Split(addr, ":")[1], err)
	}

	time.AfterFunc(2*time.Second, func() {

		log.Infof("Started orderer GRPC server on port %s", strings.Split(addr, ":")[1])

		// establish connection to store backend
		_, err := od.store.Connect(storeConStr)
		if err != nil {
			log.Info(err)
			od.Stop(1)
			return
		}

		// initialize store
		if od.store == nil {
			log.Error("Store implementation not set")
			od.Stop(1)
			return
		}

		err = od.store.Init(od.store.MakeLedgerName("", types.GetGlobalLedgerName()))
		if err != nil {
			log.Info(err)
			od.Stop(1)
			return
		}

		if od.blockchain == nil {
			log.Error("Blockchain implementation not set")
			od.Stop(1)
			return
		}

		// establish connection to blockchain backend
		if od.blockchain != nil {
			_, err = od.blockchain.Connect(storeConStr)
			if err != nil {
				log.Info(err)
				od.Stop(1)
				return
			}
		}

		// initialize the blockchain
		err = od.blockchain.Init(od.blockchain.MakeChainName("", types.GetGlobalChainName()))
		if err != nil {
			log.Info(err)
			od.Stop(1)
			return
		}

		log.Info("Backend successfully connnected")
	})

	od.server = grpc.NewServer()
	proto.RegisterOrdererServer(od.server, od)
	od.server.Serve(lis)
}

// Stop stops the orderer and returns an exit code.
func (od *Orderer) Stop(exitCode int) int {
	od.server.Stop()
	od.store.Close()
	close(od.endedCh)
	return exitCode
}

// SetStore sets the store implementation to use.
func (od *Orderer) SetStore(ch types.Store) {
	log.Infof("Setting store implementation named %s", ch.GetImplmentationName())
	od.store = ch
}

// SetBlockchain sets the blockchain implementation
func (od *Orderer) SetBlockchain(b types.Blockchain) {
	log.Infof("Setting blockchain implementation named %s", b.GetImplmentationName())
	od.blockchain = b
}

// CreateLedger creates a new ledger
func (od *Orderer) CreateLedger(ctx context.Context, params *proto.CreateLedgerParams) (*proto.Ledger, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetName())

	var createChainFunc func() error
	if params.Chained {
		createChainFunc = func() error {
			_, err := od.blockchain.CreateChain(name, params.Public)
			return err
		}
	}

	ledger, err := od.store.CreateLedgerThen(name, params.GetChained(), params.GetPublic(), createChainFunc)
	if err != nil {
		return nil, err
	}

	// replace hashed name to user readable name
	ledger.Name = params.GetName()

	var protoLedger proto.Ledger
	cstructs.Copy(ledger, &protoLedger)

	return &protoLedger, nil
}

// GetLedger returns a ledger
func (od *Orderer) GetLedger(ctx context.Context, params *proto.GetLedgerParams) (*proto.Ledger, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetName())
	ledger, err := od.store.GetLedger(name)
	if err != nil {
		return nil, err
	} else if ledger == nil && err == nil {
		return nil, types.ErrLedgerNotFound
	}

	// replace hashed name to user readable name
	ledger.Name = params.GetName()

	var protoLedger proto.Ledger
	cstructs.Copy(ledger, &protoLedger)

	return &protoLedger, nil
}

// Put creates a new transaction
func (od *Orderer) Put(ctx context.Context, params *proto.PutTransactionParams) (*proto.PutResult, error) {

	start := time.Now()

	// check if ledger exists
	ledgerName := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedgerName())
	ledger, err := od.store.GetLedger(ledgerName)
	if err != nil {
		return nil, err
	} else if err == nil && ledger == nil {
		return nil, types.ErrLedgerNotFound
	}

	// copy individual tx from []proto.Transaction to []types.Transaction
	// and set transactions key and block id
	blockID := util.Sha256(util.UUID4())
	var transactions = make([]*types.Transaction, len(params.GetTransactions()))
	for i, protoTx := range params.GetTransactions() {
		var tx = types.Transaction{}
		cstructs.Copy(protoTx, &tx)
		tx.Key = od.store.MakeTxKey(params.GetCocoonID(), tx.Key)
		tx.BlockID = blockID
		transactions[i] = &tx
	}

	var block *proto.Block
	var createBlockFunc func() error
	if ledger.Chained {
		block = &proto.Block{}

		createBlockFunc = func() error {
			var err error
			retryDelay := time.Duration(2) * time.Second
			common.ReRunOnError(func() error {
				b, _err := od.blockchain.CreateBlock(blockID, ledgerName, transactions)
				if b != nil {
					block.Id = b.ID
					block.ChainName = b.ChainName
					block.Hash = b.Hash
					block.Number = int64(b.Number)
					block.PrevBlockHash = b.PrevBlockHash
					block.Transactions = b.Transactions
					block.CreatedAt = b.CreatedAt
				}

				err = _err

				// If error is not a duplicate previous block hash error, don't re-run.
				// return nil to end the re-run routine
				if _err != nil && !types.IsDuplicatePrevBlockHashError(_err) {
					return nil
				}

				return _err
			}, 5, &retryDelay)
			return err
		}
	}

	err = od.store.PutThen(ledgerName, transactions, createBlockFunc)
	if err != nil {
		return nil, err
	}

	log.Debug("Put(): Time taken: ", time.Since(start))

	return &proto.PutResult{
		Added: int32(len(transactions)),
		Block: block,
	}, nil
}

// Get returns a transaction with a matching key
func (od *Orderer) Get(ctx context.Context, params *proto.GetParams) (*proto.Transaction, error) {

	start := time.Now()

	ledger, err := od.GetLedger(ctx, &proto.GetLedgerParams{
		CocoonID: params.GetCocoonID(),
		Name:     params.GetLedger(),
	})
	if err != nil {
		log.Error("Something bad happened")
		return nil, err
	}

	ledgerName := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	key := od.store.MakeTxKey(params.GetCocoonID(), params.GetKey())
	tx, err := od.store.Get(ledgerName, key)
	if err != nil {
		return nil, err
	} else if tx == nil && err == nil {
		return nil, types.ErrTxNotFound
	}

	if ledger.Chained {
		block, err := od.blockchain.GetBlock(ledgerName, tx.BlockID)
		if err != nil {
			log.Error(err)
			return nil, fmt.Errorf("failed to populate block to transaction")
		} else if block == nil && err == nil {
			return nil, fmt.Errorf("orphaned transaction")
		}

		tx.Block = block
		tx.BlockID = ""
	}

	var protoTx proto.Transaction
	cstructs.Copy(tx, &protoTx)

	log.Debug("Get(): Time taken: ", time.Since(start))

	return &protoTx, nil
}

// GetByID finds and returns a transaction with a matching id
func (od *Orderer) GetByID(ctx context.Context, params *proto.GetParams) (*proto.Transaction, error) {

	ledger, err := od.GetLedger(ctx, &proto.GetLedgerParams{
		CocoonID: params.GetCocoonID(),
		Name:     params.GetLedger(),
	})
	if err != nil {
		return nil, err
	}

	ledgerName := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	tx, err := od.store.GetByID(ledgerName, params.GetId())
	if err != nil {
		return nil, err
	} else if tx == nil && err == nil {
		return nil, types.ErrTxNotFound
	}

	if ledger.Chained {
		block, err := od.blockchain.GetBlock(ledgerName, tx.BlockID)
		if err != nil {
			log.Error(err)
			return nil, fmt.Errorf("failed to populate block to transaction")
		} else if block == nil && err == nil {
			return nil, fmt.Errorf("orphaned transaction")
		}

		tx.Block = block
		tx.BlockID = ""
	}

	var protoTx proto.Transaction
	cstructs.Copy(tx, &protoTx)

	return &protoTx, nil
}

// GetBlockByID returns a block by its id and chain/ledger name
func (od *Orderer) GetBlockByID(ctx context.Context, params *proto.GetBlockParams) (*proto.Block, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	ledger, err := od.store.GetLedger(name)
	if err != nil {
		return nil, err
	} else if ledger == nil && err == nil {
		return nil, types.ErrLedgerNotFound
	}

	blk, err := od.blockchain.GetBlock(name, params.GetId())
	if err != nil {
		return nil, err
	} else if blk == nil && err == nil {
		return nil, types.ErrBlockNotFound
	}

	var protoBlk proto.Block
	cstructs.Copy(blk, &protoBlk)

	return &protoBlk, nil
}

// GetRange fetches transactions between a range of keys
func (od *Orderer) GetRange(ctx context.Context, params *proto.GetRangeParams) (*proto.Transactions, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	ledger, err := od.store.GetLedger(name)
	if err != nil {
		return nil, err
	} else if ledger == nil && err == nil {
		return nil, types.ErrLedgerNotFound
	}

	if len(params.GetStartKey()) > 0 {
		params.StartKey = od.store.MakeTxKey(params.GetCocoonID(), params.GetStartKey())
	}

	if len(params.GetEndKey()) > 0 {
		if len(params.GetStartKey()) > 0 {
			params.EndKey = od.store.MakeTxKey(params.GetCocoonID(), params.GetEndKey())
		} else {
			params.EndKey = od.store.MakeTxKey(params.GetCocoonID(), "%"+params.GetEndKey())
		}
	}

	txs, err := od.store.GetRange(name, params.GetStartKey(), params.GetEndKey(), params.GetInclusive(), int(params.GetLimit()), int(params.GetOffset()))
	if err != nil {
		return nil, err
	}

	// copy individual tx from []types.Transaction to []proto.Transaction
	var protoTxs = make([]*proto.Transaction, len(txs))
	for i, tx := range txs {
		var protoTx = proto.Transaction{}
		cstructs.Copy(tx, &protoTx)
		protoTxs[i] = &protoTx
	}

	return &proto.Transactions{
		Transactions: protoTxs,
	}, nil
}
<commit_msg>debug<commit_after>package orderer

import (
	"fmt"
	"net"
	"os"
	"strings"

	context "golang.org/x/net/context"

	"time"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/orderer/proto"
	"github.com/ncodes/cocoon/core/scheduler"
	"github.com/ncodes/cocoon/core/types"
	"github.com/ncodes/cstructs"
	logging "github.com/op/go-logging"
	"google.golang.org/grpc"
)

var log = logging.MustGetLogger("orderer")

// SetLogLevel sets the log level of the logger
func SetLogLevel(l logging.Level) {
	logging.SetLevel(l, log.Module)
}

// DiscoverOrderers fetches a list of orderer service addresses
// via consul service discovery API. For development purpose,
// If DEV_ORDERER_ADDR is set, it will fetch the orderer
// address from the env variable.
func DiscoverOrderers() ([]string, error) {

	if len(os.Getenv("DEV_ORDERER_ADDR")) > 0 {
		return []string{os.Getenv("DEV_ORDERER_ADDR")}, nil
	}

	ds := scheduler.NomadServiceDiscovery{
		ConsulAddr: util.Env("CONSUL_ADDR", "localhost:8500"),
		Protocol:   "http",
	}

	_orderers, err := ds.GetByID("orderers", nil)
	if err != nil {
		return []string{}, nil
	}

	var orderers []string
	for _, orderer := range _orderers {
		orderers = append(orderers, fmt.Sprintf("%s:%f", orderer.IP, orderer.Port))
	}

	return orderers, nil
}

// DialOrderer returns a connection to a orderer from a list of addresses. It randomly
// picks an orderer address from the list for orderers.
func DialOrderer(ordererAddrs []string) (*grpc.ClientConn, error) {
	var ordererAddr string

	if len(ordererAddrs) == 0 {
		return nil, fmt.Errorf("no known orderer address")
	} else if len(ordererAddrs) == 1 {
		ordererAddr = ordererAddrs[0]
	} else {
		ordererAddr = ordererAddrs[util.RandNum(0, len(ordererAddrs))]
	}

	client, err := grpc.Dial(ordererAddr, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	return client, nil
}

// Orderer defines a transaction ordering, block creation
// and inclusion module
type Orderer struct {
	server     *grpc.Server
	store      types.Store
	blockchain types.Blockchain
	endedCh    chan bool
}

// NewOrderer creates a new Orderer object
func NewOrderer() *Orderer {
	return new(Orderer)
}

// Start starts the order service
func (od *Orderer) Start(addr, storeConStr string, endedCh chan bool) {

	od.endedCh = endedCh

	lis, err := net.Listen("tcp", fmt.Sprintf("%s", addr))
	if err != nil {
		log.Fatalf("failed to listen on port=%s. Err: %s", strings.Split(addr, ":")[1], err)
	}

	time.AfterFunc(2*time.Second, func() {

		log.Infof("Started orderer GRPC server on port %s", strings.Split(addr, ":")[1])

		// establish connection to store backend
		_, err := od.store.Connect(storeConStr)
		if err != nil {
			log.Info(err)
			od.Stop(1)
			return
		}

		// initialize store
		if od.store == nil {
			log.Error("Store implementation not set")
			od.Stop(1)
			return
		}

		err = od.store.Init(od.store.MakeLedgerName("", types.GetGlobalLedgerName()))
		if err != nil {
			log.Info(err)
			od.Stop(1)
			return
		}

		if od.blockchain == nil {
			log.Error("Blockchain implementation not set")
			od.Stop(1)
			return
		}

		// establish connection to blockchain backend
		if od.blockchain != nil {
			_, err = od.blockchain.Connect(storeConStr)
			if err != nil {
				log.Info(err)
				od.Stop(1)
				return
			}
		}

		// initialize the blockchain
		err = od.blockchain.Init(od.blockchain.MakeChainName("", types.GetGlobalChainName()))
		if err != nil {
			log.Info(err)
			od.Stop(1)
			return
		}

		log.Info("Backend successfully connnected")
	})

	od.server = grpc.NewServer()
	proto.RegisterOrdererServer(od.server, od)
	od.server.Serve(lis)
}

// Stop stops the orderer and returns an exit code.
func (od *Orderer) Stop(exitCode int) int {
	od.server.Stop()
	od.store.Close()
	close(od.endedCh)
	return exitCode
}

// SetStore sets the store implementation to use.
func (od *Orderer) SetStore(ch types.Store) {
	log.Infof("Setting store implementation named %s", ch.GetImplmentationName())
	od.store = ch
}

// SetBlockchain sets the blockchain implementation
func (od *Orderer) SetBlockchain(b types.Blockchain) {
	log.Infof("Setting blockchain implementation named %s", b.GetImplmentationName())
	od.blockchain = b
}

// CreateLedger creates a new ledger
func (od *Orderer) CreateLedger(ctx context.Context, params *proto.CreateLedgerParams) (*proto.Ledger, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetName())

	var createChainFunc func() error
	if params.Chained {
		createChainFunc = func() error {
			_, err := od.blockchain.CreateChain(name, params.Public)
			return err
		}
	}

	ledger, err := od.store.CreateLedgerThen(name, params.GetChained(), params.GetPublic(), createChainFunc)
	if err != nil {
		return nil, err
	}

	// replace hashed name to user readable name
	ledger.Name = params.GetName()

	var protoLedger proto.Ledger
	cstructs.Copy(ledger, &protoLedger)

	return &protoLedger, nil
}

// GetLedger returns a ledger
func (od *Orderer) GetLedger(ctx context.Context, params *proto.GetLedgerParams) (*proto.Ledger, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetName())
	ledger, err := od.store.GetLedger(name)
	if err != nil {
		return nil, err
	} else if ledger == nil && err == nil {
		return nil, types.ErrLedgerNotFound
	}

	// replace hashed name to user readable name
	ledger.Name = params.GetName()

	var protoLedger proto.Ledger
	cstructs.Copy(ledger, &protoLedger)

	return &protoLedger, nil
}

// Put creates a new transaction
func (od *Orderer) Put(ctx context.Context, params *proto.PutTransactionParams) (*proto.PutResult, error) {

	start := time.Now()

	// check if ledger exists
	ledgerName := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedgerName())
	ledger, err := od.store.GetLedger(ledgerName)
	if err != nil {
		return nil, err
	} else if err == nil && ledger == nil {
		return nil, types.ErrLedgerNotFound
	}

	// copy individual tx from []proto.Transaction to []types.Transaction
	// and set transactions key and block id
	blockID := util.Sha256(util.UUID4())
	var transactions = make([]*types.Transaction, len(params.GetTransactions()))
	for i, protoTx := range params.GetTransactions() {
		var tx = types.Transaction{}
		cstructs.Copy(protoTx, &tx)
		tx.Key = od.store.MakeTxKey(params.GetCocoonID(), tx.Key)
		tx.BlockID = blockID
		transactions[i] = &tx
	}

	var block *proto.Block
	var createBlockFunc func() error
	if ledger.Chained {
		block = &proto.Block{}

		createBlockFunc = func() error {
			var err error
			retryDelay := time.Duration(2) * time.Second
			common.ReRunOnError(func() error {
				b, _err := od.blockchain.CreateBlock(blockID, ledgerName, transactions)
				if b != nil {
					block.Id = b.ID
					block.ChainName = b.ChainName
					block.Hash = b.Hash
					block.Number = int64(b.Number)
					block.PrevBlockHash = b.PrevBlockHash
					block.Transactions = b.Transactions
					block.CreatedAt = b.CreatedAt
				}

				err = _err

				// If error is not a duplicate previous block hash error, don't re-run.
				// return nil to end the re-run routine
				if _err != nil && !types.IsDuplicatePrevBlockHashError(_err) {
					return nil
				}

				return _err
			}, 5, &retryDelay)
			return err
		}
	}

	err = od.store.PutThen(ledgerName, transactions, createBlockFunc)
	if err != nil {
		return nil, err
	}

	log.Debug("Put(): Time taken: ", time.Since(start))

	return &proto.PutResult{
		Added: int32(len(transactions)),
		Block: block,
	}, nil
}

// Get returns a transaction with a matching key
func (od *Orderer) Get(ctx context.Context, params *proto.GetParams) (*proto.Transaction, error) {

	start := time.Now()
	log.Error("Get it")

	ledger, err := od.GetLedger(ctx, &proto.GetLedgerParams{
		CocoonID: params.GetCocoonID(),
		Name:     params.GetLedger(),
	})
	if err != nil {
		return nil, err
	}

	ledgerName := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	key := od.store.MakeTxKey(params.GetCocoonID(), params.GetKey())
	tx, err := od.store.Get(ledgerName, key)
	if err != nil {
		return nil, err
	} else if tx == nil && err == nil {
		return nil, types.ErrTxNotFound
	}

	if ledger.Chained {
		block, err := od.blockchain.GetBlock(ledgerName, tx.BlockID)
		if err != nil {
			log.Error(err)
			return nil, fmt.Errorf("failed to populate block to transaction")
		} else if block == nil && err == nil {
			return nil, fmt.Errorf("orphaned transaction")
		}

		tx.Block = block
		tx.BlockID = ""
	}

	var protoTx proto.Transaction
	cstructs.Copy(tx, &protoTx)

	log.Debug("Get(): Time taken: ", time.Since(start))

	return &protoTx, nil
}

// GetByID finds and returns a transaction with a matching id
func (od *Orderer) GetByID(ctx context.Context, params *proto.GetParams) (*proto.Transaction, error) {

	ledger, err := od.GetLedger(ctx, &proto.GetLedgerParams{
		CocoonID: params.GetCocoonID(),
		Name:     params.GetLedger(),
	})
	if err != nil {
		return nil, err
	}

	ledgerName := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	tx, err := od.store.GetByID(ledgerName, params.GetId())
	if err != nil {
		return nil, err
	} else if tx == nil && err == nil {
		return nil, types.ErrTxNotFound
	}

	if ledger.Chained {
		block, err := od.blockchain.GetBlock(ledgerName, tx.BlockID)
		if err != nil {
			log.Error(err)
			return nil, fmt.Errorf("failed to populate block to transaction")
		} else if block == nil && err == nil {
			return nil, fmt.Errorf("orphaned transaction")
		}

		tx.Block = block
		tx.BlockID = ""
	}

	var protoTx proto.Transaction
	cstructs.Copy(tx, &protoTx)

	return &protoTx, nil
}

// GetBlockByID returns a block by its id and chain/ledger name
func (od *Orderer) GetBlockByID(ctx context.Context, params *proto.GetBlockParams) (*proto.Block, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	ledger, err := od.store.GetLedger(name)
	if err != nil {
		return nil, err
	} else if ledger == nil && err == nil {
		return nil, types.ErrLedgerNotFound
	}

	blk, err := od.blockchain.GetBlock(name, params.GetId())
	if err != nil {
		return nil, err
	} else if blk == nil && err == nil {
		return nil, types.ErrBlockNotFound
	}

	var protoBlk proto.Block
	cstructs.Copy(blk, &protoBlk)

	return &protoBlk, nil
}

// GetRange fetches transactions between a range of keys
func (od *Orderer) GetRange(ctx context.Context, params *proto.GetRangeParams) (*proto.Transactions, error) {

	name := od.store.MakeLedgerName(params.GetCocoonID(), params.GetLedger())
	ledger, err := od.store.GetLedger(name)
	if err != nil {
		return nil, err
	} else if ledger == nil && err == nil {
		return nil, types.ErrLedgerNotFound
	}

	if len(params.GetStartKey()) > 0 {
		params.StartKey = od.store.MakeTxKey(params.GetCocoonID(), params.GetStartKey())
	}

	if len(params.GetEndKey()) > 0 {
		if len(params.GetStartKey()) > 0 {
			params.EndKey = od.store.MakeTxKey(params.GetCocoonID(), params.GetEndKey())
		} else {
			params.EndKey = od.store.MakeTxKey(params.GetCocoonID(), "%"+params.GetEndKey())
		}
	}

	txs, err := od.store.GetRange(name, params.GetStartKey(), params.GetEndKey(), params.GetInclusive(), int(params.GetLimit()), int(params.GetOffset()))
	if err != nil {
		return nil, err
	}

	// copy individual tx from []types.Transaction to []proto.Transaction
	var protoTxs = make([]*proto.Transaction, len(txs))
	for i, tx := range txs {
		var protoTx = proto.Transaction{}
		cstructs.Copy(tx, &protoTx)
		protoTxs[i] = &protoTx
	}

	return &proto.Transactions{
		Transactions: protoTxs,
	}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google Inc.

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 sqlparser

import (
	"bytes"
	"encoding/json"
	"reflect"
	"testing"
	"unsafe"

	"github.com/xwb1989/sqlparser/dependency/sqltypes"
)

func TestAppend(t *testing.T) {
	query := "select * from t where a = 1"
	tree, err := Parse(query)
	if err != nil {
		t.Error(err)
	}
	var b bytes.Buffer
	Append(&b, tree)
	got := b.String()
	want := query
	if got != want {
		t.Errorf("Append: %s, want %s", got, want)
	}
	Append(&b, tree)
	got = b.String()
	want = query + query
	if got != want {
		t.Errorf("Append: %s, want %s", got, want)
	}
}

func TestSelect(t *testing.T) {
	tree, err := Parse("select * from t where a = 1")
	if err != nil {
		t.Error(err)
	}
	expr := tree.(*Select).Where.Expr

	sel := &Select{}
	sel.AddWhere(expr)
	buf := NewTrackedBuffer(nil)
	sel.Where.Format(buf)
	want := " where a = 1"
	if buf.String() != want {
		t.Errorf("where: %q, want %s", buf.String(), want)
	}
	sel.AddWhere(expr)
	buf = NewTrackedBuffer(nil)
	sel.Where.Format(buf)
	want = " where a = 1 and a = 1"
	if buf.String() != want {
		t.Errorf("where: %q, want %s", buf.String(), want)
	}
	sel = &Select{}
	sel.AddHaving(expr)
	buf = NewTrackedBuffer(nil)
	sel.Having.Format(buf)
	want = " having a = 1"
	if buf.String() != want {
		t.Errorf("having: %q, want %s", buf.String(), want)
	}
	sel.AddHaving(expr)
	buf = NewTrackedBuffer(nil)
	sel.Having.Format(buf)
	want = " having a = 1 and a = 1"
	if buf.String() != want {
		t.Errorf("having: %q, want %s", buf.String(), want)
	}

	// OR clauses must be parenthesized.
	tree, err = Parse("select * from t where a = 1 or b = 1")
	if err != nil {
		t.Error(err)
	}
	expr = tree.(*Select).Where.Expr
	sel = &Select{}
	sel.AddWhere(expr)
	buf = NewTrackedBuffer(nil)
	sel.Where.Format(buf)
	want = " where (a = 1 or b = 1)"
	if buf.String() != want {
		t.Errorf("where: %q, want %s", buf.String(), want)
	}
	sel = &Select{}
	sel.AddHaving(expr)
	buf = NewTrackedBuffer(nil)
	sel.Having.Format(buf)
	want = " having (a = 1 or b = 1)"
	if buf.String() != want {
		t.Errorf("having: %q, want %s", buf.String(), want)
	}
}

func TestRemoveHints(t *testing.T) {
	tree, err := Parse("select * from t use index (i)")
	if err != nil {
		t.Fatal(err)
	}
	sel := tree.(*Select)
	sel.From = TableExprs{
		sel.From[0].(*AliasedTableExpr).RemoveHints(),
	}
	buf := NewTrackedBuffer(nil)
	sel.Format(buf)
	if got, want := buf.String(), "select * from t"; got != want {
		t.Errorf("stripped query: %s, want %s", got, want)
	}
}

func TestAddOrder(t *testing.T) {
	src, err := Parse("select foo, bar from baz order by foo")
	if err != nil {
		t.Error(err)
	}
	order := src.(*Select).OrderBy[0]
	dst, err := Parse("select * from t")
	if err != nil {
		t.Error(err)
	}
	dst.(*Select).AddOrder(order)
	buf := NewTrackedBuffer(nil)
	dst.Format(buf)
	want := "select * from t order by foo asc"
	if buf.String() != want {
		t.Errorf("order: %q, want %s", buf.String(), want)
	}
	dst, err = Parse("select * from t union select * from s")
	if err != nil {
		t.Error(err)
	}
	dst.(*Union).AddOrder(order)
	buf = NewTrackedBuffer(nil)
	dst.Format(buf)
	want = "select * from t union select * from s order by foo asc"
	if buf.String() != want {
		t.Errorf("order: %q, want %s", buf.String(), want)
	}
}

func TestSetLimit(t *testing.T) {
	src, err := Parse("select foo, bar from baz limit 4")
	if err != nil {
		t.Error(err)
	}
	limit := src.(*Select).Limit
	dst, err := Parse("select * from t")
	if err != nil {
		t.Error(err)
	}
	dst.(*Select).SetLimit(limit)
	buf := NewTrackedBuffer(nil)
	dst.Format(buf)
	want := "select * from t limit 4"
	if buf.String() != want {
		t.Errorf("limit: %q, want %s", buf.String(), want)
	}
	dst, err = Parse("select * from t union select * from s")
	if err != nil {
		t.Error(err)
	}
	dst.(*Union).SetLimit(limit)
	buf = NewTrackedBuffer(nil)
	dst.Format(buf)
	want = "select * from t union select * from s limit 4"
	if buf.String() != want {
		t.Errorf("order: %q, want %s", buf.String(), want)
	}
}

func TestWhere(t *testing.T) {
	var w *Where
	buf := NewTrackedBuffer(nil)
	w.Format(buf)
	if buf.String() != "" {
		t.Errorf("w.Format(nil): %q, want \"\"", buf.String())
	}
	w = NewWhere(WhereStr, nil)
	buf = NewTrackedBuffer(nil)
	w.Format(buf)
	if buf.String() != "" {
		t.Errorf("w.Format(&Where{nil}: %q, want \"\"", buf.String())
	}
}

func TestIsAggregate(t *testing.T) {
	f := FuncExpr{Name: NewColIdent("avg")}
	if !f.IsAggregate() {
		t.Error("IsAggregate: false, want true")
	}

	f = FuncExpr{Name: NewColIdent("Avg")}
	if !f.IsAggregate() {
		t.Error("IsAggregate: false, want true")
	}

	f = FuncExpr{Name: NewColIdent("foo")}
	if f.IsAggregate() {
		t.Error("IsAggregate: true, want false")
	}
}

func TestExprFromValue(t *testing.T) {
	tcases := []struct {
		in  sqltypes.Value
		out SQLNode
		err string
	}{{
		in:  sqltypes.NULL,
		out: &NullVal{},
	}, {
		in:  sqltypes.NewInt64(1),
		out: NewIntVal([]byte("1")),
	}, {
		in:  sqltypes.NewFloat64(1.1),
		out: NewFloatVal([]byte("1.1")),
	}, {
		in:  sqltypes.MakeTrusted(sqltypes.Decimal, []byte("1.1")),
		out: NewFloatVal([]byte("1.1")),
	}, {
		in:  sqltypes.NewVarChar("aa"),
		out: NewStrVal([]byte("aa")),
	}, {
		in:  sqltypes.MakeTrusted(sqltypes.Expression, []byte("rand()")),
		err: "cannot convert value EXPRESSION(rand()) to AST",
	}}
	for _, tcase := range tcases {
		got, err := ExprFromValue(tcase.in)
		if tcase.err != "" {
			if err == nil || err.Error() != tcase.err {
				t.Errorf("ExprFromValue(%v) err: %v, want %s", tcase.in, err, tcase.err)
			}
			continue
		}
		if err != nil {
			t.Error(err)
		}
		if got, want := got, tcase.out; !reflect.DeepEqual(got, want) {
			t.Errorf("ExprFromValue(%v): %v, want %s", tcase.in, got, want)
		}
	}
}

func TestColNameEqual(t *testing.T) {
	var c1, c2 *ColName
	if c1.Equal(c2) {
		t.Error("nil columns equal, want unequal")
	}
	c1 = &ColName{
		Name: NewColIdent("aa"),
	}
	c2 = &ColName{
		Name: NewColIdent("bb"),
	}
	if c1.Equal(c2) {
		t.Error("columns equal, want unequal")
	}
	c2.Name = NewColIdent("aa")
	if !c1.Equal(c2) {
		t.Error("columns unequal, want equal")
	}
}

func TestColIdent(t *testing.T) {
	str := NewColIdent("Ab")
	if str.String() != "Ab" {
		t.Errorf("String=%s, want Ab", str.String())
	}
	if str.String() != "Ab" {
		t.Errorf("Val=%s, want Ab", str.String())
	}
	if str.Lowered() != "ab" {
		t.Errorf("Val=%s, want ab", str.Lowered())
	}
	if !str.Equal(NewColIdent("aB")) {
		t.Error("str.Equal(NewColIdent(aB))=false, want true")
	}
	if !str.EqualString("ab") {
		t.Error("str.EqualString(ab)=false, want true")
	}
	str = NewColIdent("")
	if str.Lowered() != "" {
		t.Errorf("Val=%s, want \"\"", str.Lowered())
	}
}

func TestColIdentMarshal(t *testing.T) {
	str := NewColIdent("Ab")
	b, err := json.Marshal(str)
	if err != nil {
		t.Fatal(err)
	}
	got := string(b)
	want := `"Ab"`
	if got != want {
		t.Errorf("json.Marshal()= %s, want %s", got, want)
	}
	var out ColIdent
	if err := json.Unmarshal(b, &out); err != nil {
		t.Errorf("Unmarshal err: %v, want nil", err)
	}
	if !reflect.DeepEqual(out, str) {
		t.Errorf("Unmarshal: %v, want %v", out, str)
	}
}

func TestColIdentSize(t *testing.T) {
	size := unsafe.Sizeof(NewColIdent(""))
	want := 2 * unsafe.Sizeof("")
	if size != want {
		t.Errorf("Size of ColIdent: %d, want 32", want)
	}
}

func TestTableIdentMarshal(t *testing.T) {
	str := NewTableIdent("Ab")
	b, err := json.Marshal(str)
	if err != nil {
		t.Fatal(err)
	}
	got := string(b)
	want := `"Ab"`
	if got != want {
		t.Errorf("json.Marshal()= %s, want %s", got, want)
	}
	var out TableIdent
	if err := json.Unmarshal(b, &out); err != nil {
		t.Errorf("Unmarshal err: %v, want nil", err)
	}
	if !reflect.DeepEqual(out, str) {
		t.Errorf("Unmarshal: %v, want %v", out, str)
	}
}

func TestHexDecode(t *testing.T) {
	testcase := []struct {
		in, out string
	}{{
		in:  "313233",
		out: "123",
	}, {
		in:  "ag",
		out: "encoding/hex: invalid byte: U+0067 'g'",
	}, {
		in:  "777",
		out: "encoding/hex: odd length hex string",
	}}
	for _, tc := range testcase {
		out, err := newHexVal(tc.in).HexDecode()
		if err != nil {
			if err.Error() != tc.out {
				t.Errorf("Decode(%q): %v, want %s", tc.in, err, tc.out)
			}
			continue
		}
		if !bytes.Equal(out, []byte(tc.out)) {
			t.Errorf("Decode(%q): %s, want %s", tc.in, out, tc.out)
		}
	}
}

func TestCompliantName(t *testing.T) {
	testcases := []struct {
		in, out string
	}{{
		in:  "aa",
		out: "aa",
	}, {
		in:  "1a",
		out: "_a",
	}, {
		in:  "a1",
		out: "a1",
	}, {
		in:  "a.b",
		out: "a_b",
	}, {
		in:  ".ab",
		out: "_ab",
	}}
	for _, tc := range testcases {
		out := NewColIdent(tc.in).CompliantName()
		if out != tc.out {
			t.Errorf("ColIdent(%s).CompliantNamt: %s, want %s", tc.in, out, tc.out)
		}
		out = NewTableIdent(tc.in).CompliantName()
		if out != tc.out {
			t.Errorf("TableIdent(%s).CompliantNamt: %s, want %s", tc.in, out, tc.out)
		}
	}
}

func TestColumns_FindColumn(t *testing.T) {
	cols := Columns{NewColIdent("a"), NewColIdent("c"), NewColIdent("b"), NewColIdent("0")}

	testcases := []struct {
		in  string
		out int
	}{{
		in:  "a",
		out: 0,
	}, {
		in:  "b",
		out: 2,
	},
		{
			in:  "0",
			out: 3,
		},
		{
			in:  "f",
			out: -1,
		}}

	for _, tc := range testcases {
		val := cols.FindColumn(NewColIdent(tc.in))
		if val != tc.out {
			t.Errorf("FindColumn(%s): %d, want %d", tc.in, val, tc.out)
		}
	}
}
<commit_msg>bugs: add test for 'force index' for RemoveHints<commit_after>/*
Copyright 2017 Google Inc.

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 sqlparser

import (
	"bytes"
	"encoding/json"
	"reflect"
	"testing"
	"unsafe"

	"github.com/xwb1989/sqlparser/dependency/sqltypes"
)

func TestAppend(t *testing.T) {
	query := "select * from t where a = 1"
	tree, err := Parse(query)
	if err != nil {
		t.Error(err)
	}
	var b bytes.Buffer
	Append(&b, tree)
	got := b.String()
	want := query
	if got != want {
		t.Errorf("Append: %s, want %s", got, want)
	}
	Append(&b, tree)
	got = b.String()
	want = query + query
	if got != want {
		t.Errorf("Append: %s, want %s", got, want)
	}
}

func TestSelect(t *testing.T) {
	tree, err := Parse("select * from t where a = 1")
	if err != nil {
		t.Error(err)
	}
	expr := tree.(*Select).Where.Expr

	sel := &Select{}
	sel.AddWhere(expr)
	buf := NewTrackedBuffer(nil)
	sel.Where.Format(buf)
	want := " where a = 1"
	if buf.String() != want {
		t.Errorf("where: %q, want %s", buf.String(), want)
	}
	sel.AddWhere(expr)
	buf = NewTrackedBuffer(nil)
	sel.Where.Format(buf)
	want = " where a = 1 and a = 1"
	if buf.String() != want {
		t.Errorf("where: %q, want %s", buf.String(), want)
	}
	sel = &Select{}
	sel.AddHaving(expr)
	buf = NewTrackedBuffer(nil)
	sel.Having.Format(buf)
	want = " having a = 1"
	if buf.String() != want {
		t.Errorf("having: %q, want %s", buf.String(), want)
	}
	sel.AddHaving(expr)
	buf = NewTrackedBuffer(nil)
	sel.Having.Format(buf)
	want = " having a = 1 and a = 1"
	if buf.String() != want {
		t.Errorf("having: %q, want %s", buf.String(), want)
	}

	// OR clauses must be parenthesized.
	tree, err = Parse("select * from t where a = 1 or b = 1")
	if err != nil {
		t.Error(err)
	}
	expr = tree.(*Select).Where.Expr
	sel = &Select{}
	sel.AddWhere(expr)
	buf = NewTrackedBuffer(nil)
	sel.Where.Format(buf)
	want = " where (a = 1 or b = 1)"
	if buf.String() != want {
		t.Errorf("where: %q, want %s", buf.String(), want)
	}
	sel = &Select{}
	sel.AddHaving(expr)
	buf = NewTrackedBuffer(nil)
	sel.Having.Format(buf)
	want = " having (a = 1 or b = 1)"
	if buf.String() != want {
		t.Errorf("having: %q, want %s", buf.String(), want)
	}
}

func TestRemoveHints(t *testing.T) {
	for _, query := range []string{
		"select * from t use index (i)",
		"select * from t force index (i)",
	} {
		tree, err := Parse(query)
		if err != nil {
			t.Fatal(err)
		}
		sel := tree.(*Select)
		sel.From = TableExprs{
			sel.From[0].(*AliasedTableExpr).RemoveHints(),
		}
		buf := NewTrackedBuffer(nil)
		sel.Format(buf)
		if got, want := buf.String(), "select * from t"; got != want {
			t.Errorf("stripped query: %s, want %s", got, want)
		}
	}
}

func TestAddOrder(t *testing.T) {
	src, err := Parse("select foo, bar from baz order by foo")
	if err != nil {
		t.Error(err)
	}
	order := src.(*Select).OrderBy[0]
	dst, err := Parse("select * from t")
	if err != nil {
		t.Error(err)
	}
	dst.(*Select).AddOrder(order)
	buf := NewTrackedBuffer(nil)
	dst.Format(buf)
	want := "select * from t order by foo asc"
	if buf.String() != want {
		t.Errorf("order: %q, want %s", buf.String(), want)
	}
	dst, err = Parse("select * from t union select * from s")
	if err != nil {
		t.Error(err)
	}
	dst.(*Union).AddOrder(order)
	buf = NewTrackedBuffer(nil)
	dst.Format(buf)
	want = "select * from t union select * from s order by foo asc"
	if buf.String() != want {
		t.Errorf("order: %q, want %s", buf.String(), want)
	}
}

func TestSetLimit(t *testing.T) {
	src, err := Parse("select foo, bar from baz limit 4")
	if err != nil {
		t.Error(err)
	}
	limit := src.(*Select).Limit
	dst, err := Parse("select * from t")
	if err != nil {
		t.Error(err)
	}
	dst.(*Select).SetLimit(limit)
	buf := NewTrackedBuffer(nil)
	dst.Format(buf)
	want := "select * from t limit 4"
	if buf.String() != want {
		t.Errorf("limit: %q, want %s", buf.String(), want)
	}
	dst, err = Parse("select * from t union select * from s")
	if err != nil {
		t.Error(err)
	}
	dst.(*Union).SetLimit(limit)
	buf = NewTrackedBuffer(nil)
	dst.Format(buf)
	want = "select * from t union select * from s limit 4"
	if buf.String() != want {
		t.Errorf("order: %q, want %s", buf.String(), want)
	}
}

func TestWhere(t *testing.T) {
	var w *Where
	buf := NewTrackedBuffer(nil)
	w.Format(buf)
	if buf.String() != "" {
		t.Errorf("w.Format(nil): %q, want \"\"", buf.String())
	}
	w = NewWhere(WhereStr, nil)
	buf = NewTrackedBuffer(nil)
	w.Format(buf)
	if buf.String() != "" {
		t.Errorf("w.Format(&Where{nil}: %q, want \"\"", buf.String())
	}
}

func TestIsAggregate(t *testing.T) {
	f := FuncExpr{Name: NewColIdent("avg")}
	if !f.IsAggregate() {
		t.Error("IsAggregate: false, want true")
	}

	f = FuncExpr{Name: NewColIdent("Avg")}
	if !f.IsAggregate() {
		t.Error("IsAggregate: false, want true")
	}

	f = FuncExpr{Name: NewColIdent("foo")}
	if f.IsAggregate() {
		t.Error("IsAggregate: true, want false")
	}
}

func TestExprFromValue(t *testing.T) {
	tcases := []struct {
		in  sqltypes.Value
		out SQLNode
		err string
	}{{
		in:  sqltypes.NULL,
		out: &NullVal{},
	}, {
		in:  sqltypes.NewInt64(1),
		out: NewIntVal([]byte("1")),
	}, {
		in:  sqltypes.NewFloat64(1.1),
		out: NewFloatVal([]byte("1.1")),
	}, {
		in:  sqltypes.MakeTrusted(sqltypes.Decimal, []byte("1.1")),
		out: NewFloatVal([]byte("1.1")),
	}, {
		in:  sqltypes.NewVarChar("aa"),
		out: NewStrVal([]byte("aa")),
	}, {
		in:  sqltypes.MakeTrusted(sqltypes.Expression, []byte("rand()")),
		err: "cannot convert value EXPRESSION(rand()) to AST",
	}}
	for _, tcase := range tcases {
		got, err := ExprFromValue(tcase.in)
		if tcase.err != "" {
			if err == nil || err.Error() != tcase.err {
				t.Errorf("ExprFromValue(%v) err: %v, want %s", tcase.in, err, tcase.err)
			}
			continue
		}
		if err != nil {
			t.Error(err)
		}
		if got, want := got, tcase.out; !reflect.DeepEqual(got, want) {
			t.Errorf("ExprFromValue(%v): %v, want %s", tcase.in, got, want)
		}
	}
}

func TestColNameEqual(t *testing.T) {
	var c1, c2 *ColName
	if c1.Equal(c2) {
		t.Error("nil columns equal, want unequal")
	}
	c1 = &ColName{
		Name: NewColIdent("aa"),
	}
	c2 = &ColName{
		Name: NewColIdent("bb"),
	}
	if c1.Equal(c2) {
		t.Error("columns equal, want unequal")
	}
	c2.Name = NewColIdent("aa")
	if !c1.Equal(c2) {
		t.Error("columns unequal, want equal")
	}
}

func TestColIdent(t *testing.T) {
	str := NewColIdent("Ab")
	if str.String() != "Ab" {
		t.Errorf("String=%s, want Ab", str.String())
	}
	if str.String() != "Ab" {
		t.Errorf("Val=%s, want Ab", str.String())
	}
	if str.Lowered() != "ab" {
		t.Errorf("Val=%s, want ab", str.Lowered())
	}
	if !str.Equal(NewColIdent("aB")) {
		t.Error("str.Equal(NewColIdent(aB))=false, want true")
	}
	if !str.EqualString("ab") {
		t.Error("str.EqualString(ab)=false, want true")
	}
	str = NewColIdent("")
	if str.Lowered() != "" {
		t.Errorf("Val=%s, want \"\"", str.Lowered())
	}
}

func TestColIdentMarshal(t *testing.T) {
	str := NewColIdent("Ab")
	b, err := json.Marshal(str)
	if err != nil {
		t.Fatal(err)
	}
	got := string(b)
	want := `"Ab"`
	if got != want {
		t.Errorf("json.Marshal()= %s, want %s", got, want)
	}
	var out ColIdent
	if err := json.Unmarshal(b, &out); err != nil {
		t.Errorf("Unmarshal err: %v, want nil", err)
	}
	if !reflect.DeepEqual(out, str) {
		t.Errorf("Unmarshal: %v, want %v", out, str)
	}
}

func TestColIdentSize(t *testing.T) {
	size := unsafe.Sizeof(NewColIdent(""))
	want := 2 * unsafe.Sizeof("")
	if size != want {
		t.Errorf("Size of ColIdent: %d, want 32", want)
	}
}

func TestTableIdentMarshal(t *testing.T) {
	str := NewTableIdent("Ab")
	b, err := json.Marshal(str)
	if err != nil {
		t.Fatal(err)
	}
	got := string(b)
	want := `"Ab"`
	if got != want {
		t.Errorf("json.Marshal()= %s, want %s", got, want)
	}
	var out TableIdent
	if err := json.Unmarshal(b, &out); err != nil {
		t.Errorf("Unmarshal err: %v, want nil", err)
	}
	if !reflect.DeepEqual(out, str) {
		t.Errorf("Unmarshal: %v, want %v", out, str)
	}
}

func TestHexDecode(t *testing.T) {
	testcase := []struct {
		in, out string
	}{{
		in:  "313233",
		out: "123",
	}, {
		in:  "ag",
		out: "encoding/hex: invalid byte: U+0067 'g'",
	}, {
		in:  "777",
		out: "encoding/hex: odd length hex string",
	}}
	for _, tc := range testcase {
		out, err := newHexVal(tc.in).HexDecode()
		if err != nil {
			if err.Error() != tc.out {
				t.Errorf("Decode(%q): %v, want %s", tc.in, err, tc.out)
			}
			continue
		}
		if !bytes.Equal(out, []byte(tc.out)) {
			t.Errorf("Decode(%q): %s, want %s", tc.in, out, tc.out)
		}
	}
}

func TestCompliantName(t *testing.T) {
	testcases := []struct {
		in, out string
	}{{
		in:  "aa",
		out: "aa",
	}, {
		in:  "1a",
		out: "_a",
	}, {
		in:  "a1",
		out: "a1",
	}, {
		in:  "a.b",
		out: "a_b",
	}, {
		in:  ".ab",
		out: "_ab",
	}}
	for _, tc := range testcases {
		out := NewColIdent(tc.in).CompliantName()
		if out != tc.out {
			t.Errorf("ColIdent(%s).CompliantNamt: %s, want %s", tc.in, out, tc.out)
		}
		out = NewTableIdent(tc.in).CompliantName()
		if out != tc.out {
			t.Errorf("TableIdent(%s).CompliantNamt: %s, want %s", tc.in, out, tc.out)
		}
	}
}

func TestColumns_FindColumn(t *testing.T) {
	cols := Columns{NewColIdent("a"), NewColIdent("c"), NewColIdent("b"), NewColIdent("0")}

	testcases := []struct {
		in  string
		out int
	}{{
		in:  "a",
		out: 0,
	}, {
		in:  "b",
		out: 2,
	},
		{
			in:  "0",
			out: 3,
		},
		{
			in:  "f",
			out: -1,
		}}

	for _, tc := range testcases {
		val := cols.FindColumn(NewColIdent(tc.in))
		if val != tc.out {
			t.Errorf("FindColumn(%s): %d, want %d", tc.in, val, tc.out)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"io/ioutil"
	"sort"

	sysl "github.com/anz-bank/sysl/src/proto"
	parser "github.com/anz-bank/sysl/sysl2/naive"
	ebnfGrammar "github.com/anz-bank/sysl/sysl2/proto"
	"github.com/anz-bank/sysl/sysl2/sysl/eval"
	"github.com/anz-bank/sysl/sysl2/sysl/msg"
	"github.com/anz-bank/sysl/sysl2/sysl/parse"
	"github.com/anz-bank/sysl/sysl2/sysl/syslutil"
	"github.com/anz-bank/sysl/sysl2/sysl/validate"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
)

// Node can be string or node
type Node []interface{}

type CodeGenOutput struct {
	filename string
	output   Node
}

func getKeyFromValueMap(v *sysl.Value, key string) *sysl.Value {
	if m := v.GetMap(); m != nil {
		return m.Items[key]
	}
	return nil
}

func processChoice(
	g *ebnfGrammar.Grammar,
	obj *sysl.Value,
	choice *ebnfGrammar.Choice,
	logger *logrus.Logger,
) Node {
	var result Node

	for i, seq := range choice.Sequence {
		seqResult := Node{}
		fullScan := true
		for _, term := range seq.Term {
			switch x := term.Atom.Union.(type) {
			// String tokens dont have quantifiers
			case *ebnfGrammar.Atom_String_:
				seqResult = append(seqResult, x.String_)
			case *ebnfGrammar.Atom_Rulename:
				var ruleResult interface{}

				minc, maxc := parser.GetTermMinMaxCount(term)
				v := getKeyFromValueMap(obj, x.Rulename.Name)

				// raise error if required
				//  i.e.  no quantifier or +
				//        and missing from obj map
				if minc > 0 && v == nil {
					fullScan = false
					break
				}

				// skip if rule has
				//    quantifier == * or ?
				//    and does not exist in obj map
				if minc == 0 && v == nil {
					continue
				}

				if maxc > 1 {
					var valueList []*sysl.Value
					switch vv := v.Value.(type) {
					case *sysl.Value_List_:
						valueList = vv.List.Value
					case *sysl.Value_Set:
						valueList = vv.Set.Value
					default:
						logger.Warnf("Expecting a collection type, got %T for rule %s", vv, x.Rulename.Name)
						fullScan = false
					}
					ruleInstances := Node{}

					for _, valueItem := range valueList {
						// Drill down the rule
						node := processRule(g, valueItem, x.Rulename.Name, logger)
						// Check post-conditions
						if len(node) == 0 {
							fullScan = false
							break
						}
						ruleInstances = append(ruleInstances, node)
					}
					ruleResult = ruleInstances
				} else { // maxc == 1
					// Drill down the rule
					if v.GetList() != nil || v.GetSet() != nil {
						logger.Warnf("Got List or Set instead of map")
					}
					node := processRule(g, v, x.Rulename.Name, logger)
					// Check post-conditions
					if len(node) == 0 {
						logger.Warnf("could not process rule: ( %s )", x.Rulename.Name)
						fullScan = false
						break
					}
					if s, ok := node[0].(string); ok && len(node) == 1 {
						ruleResult = s
					} else {
						ruleResult = node
					}
				}
				seqResult = append(seqResult, ruleResult)
			case *ebnfGrammar.Atom_Choices:
				// minc, maxc := parser.GetMinMaxCount(term)
				node := processChoice(g, obj, x.Choices, logger)
				if len(node) == 0 {
					logger.Warnf("could not process Choice\n")
					fullScan = false
					break
				}
				seqResult = append(seqResult, node)
			default:
				logger.Warningf("processChoice: choice %d : %T", i, x)
				panic("Unexpected atom type")
			}
			if !fullScan {
				break
			}
		}
		if fullScan {
			result = append(result, seqResult)
		}
	}
	return result
}

func processRule(g *ebnfGrammar.Grammar, obj *sysl.Value, ruleName string, logger *logrus.Logger) Node {
	var str string
	if x := obj.GetMap(); x != nil {
		for key := range x.Items {
			str += key + ", "
		}
	}
	// logrus.Debugf("processRule: %s, obj keys (%s)", ruleName, str)
	rule := g.Rules[ruleName]
	if rule == nil {
		root := Node{}
		if eval.IsCollectionType(obj) {
			return nil
		}
		// Should we convert int and bools to string and return?
		return append(root, obj.GetS())
	}
	root := processChoice(g, obj, rule.Choices, logger)
	return root
}

func readGrammar(filename, grammarName, startRule string) (*ebnfGrammar.Grammar, error) {
	dat, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return parser.ParseEBNF(string(dat), grammarName, startRule), nil
}

// applyTranformToModel loads applies the transform to input model
func applyTranformToModel(
	modelName, transformAppName, viewName string,
	model, transform *sysl.Module,
) (*sysl.Value, error) {
	modelApp := model.Apps[modelName]
	view := transform.Apps[transformAppName].Views[viewName]
	if view == nil {
		return nil, errors.Errorf("Cannot execute missing view: %s, in app %s", viewName, transformAppName)
	}
	s := eval.Scope{}
	s.AddApp("app", modelApp)
	s.AddModule("module", model)
	var result *sysl.Value
	// assume args are
	//  app <: sysl.App and
	//  type <: sysl.Type
	//  typeName <: string
	//  deps <: sequence of sysl.App

	if perTypeTransform(view.Param) {
		result = eval.MakeValueList()
		var tNames []string
		for tName := range modelApp.Types {
			tNames = append(tNames, tName)
		}
		sort.Strings(tNames)
		for _, tName := range tNames {
			t := modelApp.Types[tName]
			s["typeName"] = eval.MakeValueString(tName)
			s["type"] = eval.TypeToValue(t)
			eval.AppendItemToValueList(result.GetList(), eval.EvaluateView(transform, transformAppName, viewName, s))
		}
	} else {
		result = eval.EvaluateView(transform, transformAppName, viewName, s)
	}

	return result, nil
}

func perTypeTransform(params []*sysl.Param) bool {
	paramMap := make(map[string]struct{})

	for _, p := range params {
		paramMap[p.Name] = struct{}{}
	}

	if _, has := paramMap["app"]; has {
		if _, has := paramMap["type"]; has {
			return true
		}
	} else {
		panic("Expecting at least an app <: sysl.App")
	}
	return false
}

// Serialize serializes node to string
func Serialize(w io.Writer, delim string, node Node) error {
	for _, n := range node {
		switch x := n.(type) {
		case string:
			if _, err := io.WriteString(w, x+delim); err != nil {
				return err
			}
		case Node:
			if err := Serialize(w, delim, x); err != nil {
				return err
			}
		}
	}
	return nil
}

// GenerateCode transform input sysl model to code in the target language described by
// grammar and a sysl transform
func GenerateCode(
	codegenParams *CmdContextParamCodegen,
	model *sysl.Module, modelAppName string,
	fs afero.Fs, logger *logrus.Logger) ([]*CodeGenOutput, error) {
	var codeOutput []*CodeGenOutput

	logger.Debugf("root-transform: %s\n", codegenParams.rootTransform)
	logger.Debugf("transform: %s\n", codegenParams.transform)
	logger.Debugf("grammar: %s\n", codegenParams.grammar)
	logger.Debugf("start: %s\n", codegenParams.start)

	transformFs := syslutil.NewChrootFs(fs, codegenParams.rootTransform)
	tfmParser := parse.NewParser()
	tx, transformAppName, err := parse.LoadAndGetDefaultApp(codegenParams.transform, transformFs, tfmParser)
	if err != nil {
		return nil, err
	}

	g, err := readGrammar(codegenParams.grammar, "gen", codegenParams.start)
	if err != nil {
		return nil, err
	}

	grammarSysl, err := validate.LoadGrammar(codegenParams.grammar, fs)
	if err != nil {
		msg.NewMsg(msg.WarnValidationSkipped, []string{err.Error()}).LogMsg()
	} else {
		validator := validate.NewValidator(grammarSysl, tx.GetApps()[transformAppName], tfmParser)
		validator.Validate(codegenParams.start)
		validator.LogMessages()
	}

	fileNames, err := applyTranformToModel(modelAppName, transformAppName, "filename", model, tx)
	if err != nil {
		return nil, err
	}
	result, err := applyTranformToModel(modelAppName, transformAppName, g.Start, model, tx)
	if err != nil {
		return nil, err
	}
	switch {
	case fileNames.GetMap() != nil:
		filename := fileNames.GetMap().Items["filename"].GetS()
		logger.Println(filename)

		if result.GetMap() != nil {
			r := processRule(g, result, g.Start, logger)
			codeOutput = append(codeOutput, &CodeGenOutput{filename, r})
		} else if result.GetList() != nil {
			for _, v := range result.GetList().Value {
				r := processRule(g, v, g.Start, logger)
				codeOutput = append(codeOutput, &CodeGenOutput{filename, r})
			}
		}
	case fileNames.GetList() != nil && result.GetList() != nil:
		fileValues := fileNames.GetList().Value
		for i, v := range result.GetList().Value {
			filename := fileValues[i].GetMap().Items["filename"].GetS()
			r := processRule(g, v, g.Start, logger)
			codeOutput = append(codeOutput, &CodeGenOutput{filename, r})
		}
	default:
		panic("Unexpected combination for filenames and transformation results")
	}

	return codeOutput, nil
}

func outputToFiles(output []*CodeGenOutput, fs afero.Fs) error {
	for _, o := range output {
		f, err := fs.Create(o.filename)
		if err != nil {
			return errors.Wrapf(err, "unable to create %q", o.filename)
		}
		logrus.Warningln("Writing file: " + f.Name())
		if err := Serialize(f, " ", o.output); err != nil {
			return errors.Wrapf(err, "error writing to %q", o.filename)
		}
		if err := f.Close(); err != nil {
			return errors.Wrapf(err, "error closing %q", o.filename)
		}
	}
	return nil
}
<commit_msg>Factor out repeated code lines to a function<commit_after>package main

import (
	"io"
	"io/ioutil"
	"sort"

	sysl "github.com/anz-bank/sysl/src/proto"
	parser "github.com/anz-bank/sysl/sysl2/naive"
	ebnfGrammar "github.com/anz-bank/sysl/sysl2/proto"
	"github.com/anz-bank/sysl/sysl2/sysl/eval"
	"github.com/anz-bank/sysl/sysl2/sysl/msg"
	"github.com/anz-bank/sysl/sysl2/sysl/parse"
	"github.com/anz-bank/sysl/sysl2/sysl/syslutil"
	"github.com/anz-bank/sysl/sysl2/sysl/validate"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
)

// Node can be string or node
type Node []interface{}

type CodeGenOutput struct {
	filename string
	output   Node
}

func getKeyFromValueMap(v *sysl.Value, key string) *sysl.Value {
	if m := v.GetMap(); m != nil {
		return m.Items[key]
	}
	return nil
}

func processChoice(
	g *ebnfGrammar.Grammar,
	obj *sysl.Value,
	choice *ebnfGrammar.Choice,
	logger *logrus.Logger,
) Node {
	var result Node

	for i, seq := range choice.Sequence {
		seqResult := Node{}
		fullScan := true
		for _, term := range seq.Term {
			switch x := term.Atom.Union.(type) {
			// String tokens dont have quantifiers
			case *ebnfGrammar.Atom_String_:
				seqResult = append(seqResult, x.String_)
			case *ebnfGrammar.Atom_Rulename:
				var ruleResult interface{}

				minc, maxc := parser.GetTermMinMaxCount(term)
				v := getKeyFromValueMap(obj, x.Rulename.Name)

				// raise error if required
				//  i.e.  no quantifier or +
				//        and missing from obj map
				if minc > 0 && v == nil {
					fullScan = false
					break
				}

				// skip if rule has
				//    quantifier == * or ?
				//    and does not exist in obj map
				if minc == 0 && v == nil {
					continue
				}

				if maxc > 1 {
					var valueList []*sysl.Value
					switch vv := v.Value.(type) {
					case *sysl.Value_List_:
						valueList = vv.List.Value
					case *sysl.Value_Set:
						valueList = vv.Set.Value
					default:
						logger.Warnf("Expecting a collection type, got %T for rule %s", vv, x.Rulename.Name)
						fullScan = false
					}
					ruleInstances := Node{}

					for _, valueItem := range valueList {
						// Drill down the rule
						node := processRule(g, valueItem, x.Rulename.Name, logger)
						// Check post-conditions
						if len(node) == 0 {
							fullScan = false
							break
						}
						ruleInstances = append(ruleInstances, node)
					}
					ruleResult = ruleInstances
				} else { // maxc == 1
					// Drill down the rule
					if v.GetList() != nil || v.GetSet() != nil {
						logger.Warnf("Got List or Set instead of map")
					}
					node := processRule(g, v, x.Rulename.Name, logger)
					// Check post-conditions
					if len(node) == 0 {
						logger.Warnf("could not process rule: ( %s )", x.Rulename.Name)
						fullScan = false
						break
					}
					if s, ok := node[0].(string); ok && len(node) == 1 {
						ruleResult = s
					} else {
						ruleResult = node
					}
				}
				seqResult = append(seqResult, ruleResult)
			case *ebnfGrammar.Atom_Choices:
				// minc, maxc := parser.GetMinMaxCount(term)
				node := processChoice(g, obj, x.Choices, logger)
				if len(node) == 0 {
					logger.Warnf("could not process Choice\n")
					fullScan = false
					break
				}
				seqResult = append(seqResult, node)
			default:
				logger.Warningf("processChoice: choice %d : %T", i, x)
				panic("Unexpected atom type")
			}
			if !fullScan {
				break
			}
		}
		if fullScan {
			result = append(result, seqResult)
		}
	}
	return result
}

func processRule(g *ebnfGrammar.Grammar, obj *sysl.Value, ruleName string, logger *logrus.Logger) Node {
	var str string
	if x := obj.GetMap(); x != nil {
		for key := range x.Items {
			str += key + ", "
		}
	}
	// logrus.Debugf("processRule: %s, obj keys (%s)", ruleName, str)
	rule := g.Rules[ruleName]
	if rule == nil {
		root := Node{}
		if eval.IsCollectionType(obj) {
			return nil
		}
		// Should we convert int and bools to string and return?
		return append(root, obj.GetS())
	}
	root := processChoice(g, obj, rule.Choices, logger)
	return root
}

func readGrammar(filename, grammarName, startRule string) (*ebnfGrammar.Grammar, error) {
	dat, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return parser.ParseEBNF(string(dat), grammarName, startRule), nil
}

// applyTranformToModel loads applies the transform to input model
func applyTranformToModel(
	modelName, transformAppName, viewName string,
	model, transform *sysl.Module,
) (*sysl.Value, error) {
	modelApp := model.Apps[modelName]
	view := transform.Apps[transformAppName].Views[viewName]
	if view == nil {
		return nil, errors.Errorf("Cannot execute missing view: %s, in app %s", viewName, transformAppName)
	}
	s := eval.Scope{}
	s.AddApp("app", modelApp)
	s.AddModule("module", model)
	var result *sysl.Value
	// assume args are
	//  app <: sysl.App and
	//  type <: sysl.Type
	//  typeName <: string
	//  deps <: sequence of sysl.App

	if perTypeTransform(view.Param) {
		result = eval.MakeValueList()
		var tNames []string
		for tName := range modelApp.Types {
			tNames = append(tNames, tName)
		}
		sort.Strings(tNames)
		for _, tName := range tNames {
			t := modelApp.Types[tName]
			s["typeName"] = eval.MakeValueString(tName)
			s["type"] = eval.TypeToValue(t)
			eval.AppendItemToValueList(result.GetList(), eval.EvaluateView(transform, transformAppName, viewName, s))
		}
	} else {
		result = eval.EvaluateView(transform, transformAppName, viewName, s)
	}

	return result, nil
}

func perTypeTransform(params []*sysl.Param) bool {
	paramMap := make(map[string]struct{})

	for _, p := range params {
		paramMap[p.Name] = struct{}{}
	}

	if _, has := paramMap["app"]; has {
		if _, has := paramMap["type"]; has {
			return true
		}
	} else {
		panic("Expecting at least an app <: sysl.App")
	}
	return false
}

// Serialize serializes node to string
func Serialize(w io.Writer, delim string, node Node) error {
	for _, n := range node {
		switch x := n.(type) {
		case string:
			if _, err := io.WriteString(w, x+delim); err != nil {
				return err
			}
		case Node:
			if err := Serialize(w, delim, x); err != nil {
				return err
			}
		}
	}
	return nil
}

// GenerateCode transform input sysl model to code in the target language described by
// grammar and a sysl transform
func GenerateCode(
	codegenParams *CmdContextParamCodegen,
	model *sysl.Module, modelAppName string,
	fs afero.Fs, logger *logrus.Logger) ([]*CodeGenOutput, error) {
	var codeOutput []*CodeGenOutput

	logger.Debugf("root-transform: %s\n", codegenParams.rootTransform)
	logger.Debugf("transform: %s\n", codegenParams.transform)
	logger.Debugf("grammar: %s\n", codegenParams.grammar)
	logger.Debugf("start: %s\n", codegenParams.start)

	transformFs := syslutil.NewChrootFs(fs, codegenParams.rootTransform)
	tfmParser := parse.NewParser()
	tx, transformAppName, err := parse.LoadAndGetDefaultApp(codegenParams.transform, transformFs, tfmParser)
	if err != nil {
		return nil, err
	}

	g, err := readGrammar(codegenParams.grammar, "gen", codegenParams.start)
	if err != nil {
		return nil, err
	}

	grammarSysl, err := validate.LoadGrammar(codegenParams.grammar, fs)
	if err != nil {
		msg.NewMsg(msg.WarnValidationSkipped, []string{err.Error()}).LogMsg()
	} else {
		validator := validate.NewValidator(grammarSysl, tx.GetApps()[transformAppName], tfmParser)
		validator.Validate(codegenParams.start)
		validator.LogMessages()
	}

	fileNames, err := applyTranformToModel(modelAppName, transformAppName, "filename", model, tx)
	if err != nil {
		return nil, err
	}
	result, err := applyTranformToModel(modelAppName, transformAppName, g.Start, model, tx)
	if err != nil {
		return nil, err
	}
	switch {
	case fileNames.GetMap() != nil:
		filename := fileNames.GetMap().Items["filename"].GetS()
		logger.Println(filename)

		if result.GetMap() != nil {
			codeOutput = appendCodeOutput(g, result, logger, codeOutput, filename)
		} else if result.GetList() != nil {
			for _, v := range result.GetList().Value {
				codeOutput = appendCodeOutput(g, v, logger, codeOutput, filename)
			}
		}
	case fileNames.GetList() != nil && result.GetList() != nil:
		fileValues := fileNames.GetList().Value
		for i, v := range result.GetList().Value {
			filename := fileValues[i].GetMap().Items["filename"].GetS()
			codeOutput = appendCodeOutput(g, v, logger, codeOutput, filename)
		}
	default:
		panic("Unexpected combination for filenames and transformation results")
	}

	return codeOutput, nil
}

func appendCodeOutput(g *ebnfGrammar.Grammar, v *sysl.Value, logger *logrus.Logger, codeOutput []*CodeGenOutput, filename string) []*CodeGenOutput {
	r := processRule(g, v, g.Start, logger)
	codeOutput = append(codeOutput, &CodeGenOutput{filename, r})
	return codeOutput
}

func outputToFiles(output []*CodeGenOutput, fs afero.Fs) error {
	for _, o := range output {
		f, err := fs.Create(o.filename)
		if err != nil {
			return errors.Wrapf(err, "unable to create %q", o.filename)
		}
		logrus.Warningln("Writing file: " + f.Name())
		if err := Serialize(f, " ", o.output); err != nil {
			return errors.Wrapf(err, "error writing to %q", o.filename)
		}
		if err := f.Close(); err != nil {
			return errors.Wrapf(err, "error closing %q", o.filename)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package messages

import (
	"encoding/json"
)

type Oip041Wrapper struct {
	Oip041 Oip041 `json:"oip-041"`
}

type Oip041 struct {
	Artifact   Oip041Artifact   `json:"artifact"`
	Edit       Oip041Edit       `json:"editArtifact"`
	Deactivate Oip041Deactivate `json:"deactivateArtifact"`
	Transfer   Oip041Transfer   `json:"transferArtifact"`
	Signature  string           `json:"signature"`
	artSize    int
}

type Oip041ArtifactAPIResult struct {
	Block         int     `json:"block"`
	OIP041        Oip041  `json:"oip-041"`
	Tags          string  `json:"tags"`
	Timestamp     int64   `json:"timestamp"`
	Title         string  `json:"title"`
	TxID          string  `json:"txid"`
	Type          string  `json:"type"`
	Year          int     `json:"year"`
	Publisher     string  `json:"publisher"`
	PublisherName string  `json:"publisherName"`
	ArtCost       float64 `json:"artCost"`
	ArtSize       int     `json:"artSize"`
	PubFeeUSD     float64 `json:"pubFeeUSD"`
}

type Oip041Transfer struct {
	Reference string `json:"txid"`
	To        string `json:"to"`
	From      string `json:"from"`
	Timestamp int64  `json:"timestamp"`
}

type Oip041Deactivate struct {
	Reference string `json:"txid"`
	Timestamp int64  `json:"timestamp"`
}

type Oip041Edit struct {
	Patch     json.RawMessage `json:"patch"`
	Timestamp int64           `json:"timestamp"`
	TxID      string          `json:"txid"`
}

type Oip041Artifact struct {
	Publisher string        `json:"publisher"`
	Timestamp int64         `json:"timestamp"`
	Type      string        `json:"type"`
	Info      Oip041Info    `json:"info"`
	Storage   Oip041Storage `json:"storage"`
	Payment   Oip041Payment `json:"payment"`
}

type Oip041Info struct {
	Title           string          `json:"title"`
	Description     string          `json:"description"`
	Year            int             `json:"year"`
	ExtraInfo       json.RawMessage `json:"extraInfo"`
	ExtraInfoString string
}

type Oip041Payment struct {
	Fiat        string          `json:"fiat"`
	Scale       string          `json:"scale"`
	SugTip      []int           `json:"sugTip"`
	Tokens      Oip041Tokens    `json:"tokens"`
	Addresses   []Oip041Address `json:"addresses"`
	Retailer    int             `json:"retailer"`
	Promoter    int             `json:"promoter"`
	MaxDiscount int             `json:"maxdisc"`
}

type Oip041MusicExtraInfo struct {
	Artist            string   `json:"artist"`
	Company           string   `json:"company"`
	Composers         []string `json:"composers"`
	Copyright         string   `json:"copyright"`
	UsageProhibitions string   `json:"usageProhibitions"`
	UsageRights       string   `json:"usageRights"`
	Genre             string   `json:"genre"`
	Tags              []string `json:"tags"`
	ISRC              string   `json:"ISRC"`
}

type Oip041Storage struct {
	Network  string        `json:"network,omitempty"`
	Location string        `json:"location,omitempty"`
	Files    []Oip041Files `json:"files"`
}

type Oip041Files struct {
	DisallowBuy  bool    `json:"disBuy"`
	Dname        string  `json:"dname"`
	Duration     float64 `json:"duration,omitempty"`
	Fname        string  `json:"fname"`
	Fsize        int     `json:"fsize"`
	MinPlay      float64 `json:"minPlay"`
	SugPlay      float64 `json:"sugPlay"`
	Promo        float64 `json:"promo,omitempty"`
	Retail       float64 `json:"retail,omitempty"`
	PtpFT        int     `json:"ptpFT,omitempty"`
	PtpDT        int     `json:"ptpDT,omitempty"`
	PtpDA        int     `json:"ptpDA,omitempty"`
	Type         string  `json:"type"`
	TokenlyID    string  `json:"tokenlyID,omitempty"`
	DisallowPlay bool    `json:"disPlay"`
	MinBuy       float64 `json:"minBuy"`
	SugBuy       float64 `json:"sugBuy"`
	SubType      string  `json:"subtype"`
	// ToDo: Add per file granularity back, requires custom json marshalling to omit
	// Storage Oip041Storage `json:"storage"`
}

type Oip041Address struct {
	Token   string `json:"token"`
	Address string `json:"address"`
}

type Oip041Tokens map[string]int
<commit_msg>Omit empty file meta data<commit_after>package messages

import (
	"encoding/json"
)

type Oip041Wrapper struct {
	Oip041 Oip041 `json:"oip-041"`
}

type Oip041 struct {
	Artifact   Oip041Artifact   `json:"artifact"`
	Edit       Oip041Edit       `json:"editArtifact"`
	Deactivate Oip041Deactivate `json:"deactivateArtifact"`
	Transfer   Oip041Transfer   `json:"transferArtifact"`
	Signature  string           `json:"signature"`
	artSize    int
}

type Oip041ArtifactAPIResult struct {
	Block         int     `json:"block"`
	OIP041        Oip041  `json:"oip-041"`
	Tags          string  `json:"tags"`
	Timestamp     int64   `json:"timestamp"`
	Title         string  `json:"title"`
	TxID          string  `json:"txid"`
	Type          string  `json:"type"`
	Year          int     `json:"year"`
	Publisher     string  `json:"publisher"`
	PublisherName string  `json:"publisherName"`
	ArtCost       float64 `json:"artCost"`
	ArtSize       int     `json:"artSize"`
	PubFeeUSD     float64 `json:"pubFeeUSD"`
}

type Oip041Transfer struct {
	Reference string `json:"txid"`
	To        string `json:"to"`
	From      string `json:"from"`
	Timestamp int64  `json:"timestamp"`
}

type Oip041Deactivate struct {
	Reference string `json:"txid"`
	Timestamp int64  `json:"timestamp"`
}

type Oip041Edit struct {
	Patch     json.RawMessage `json:"patch"`
	Timestamp int64           `json:"timestamp"`
	TxID      string          `json:"txid"`
}

type Oip041Artifact struct {
	Publisher string        `json:"publisher"`
	Timestamp int64         `json:"timestamp"`
	Type      string        `json:"type"`
	Info      Oip041Info    `json:"info"`
	Storage   Oip041Storage `json:"storage"`
	Payment   Oip041Payment `json:"payment"`
}

type Oip041Info struct {
	Title           string          `json:"title"`
	Description     string          `json:"description"`
	Year            int             `json:"year"`
	ExtraInfo       json.RawMessage `json:"extraInfo"`
	ExtraInfoString string
}

type Oip041Payment struct {
	Fiat        string          `json:"fiat"`
	Scale       string          `json:"scale"`
	SugTip      []int           `json:"sugTip"`
	Tokens      Oip041Tokens    `json:"tokens"`
	Addresses   []Oip041Address `json:"addresses"`
	Retailer    int             `json:"retailer"`
	Promoter    int             `json:"promoter"`
	MaxDiscount int             `json:"maxdisc"`
}

type Oip041MusicExtraInfo struct {
	Artist            string   `json:"artist"`
	Company           string   `json:"company"`
	Composers         []string `json:"composers"`
	Copyright         string   `json:"copyright"`
	UsageProhibitions string   `json:"usageProhibitions"`
	UsageRights       string   `json:"usageRights"`
	Genre             string   `json:"genre"`
	Tags              []string `json:"tags"`
	ISRC              string   `json:"ISRC"`
}

type Oip041Storage struct {
	Network  string        `json:"network,omitempty"`
	Location string        `json:"location,omitempty"`
	Files    []Oip041Files `json:"files"`
}

type Oip041Files struct {
	DisallowBuy  bool    `json:"disBuy,omitempty"`
	Dname        string  `json:"dname,omitempty"`
	Duration     float64 `json:"duration,omitempty"`
	Fname        string  `json:"fname,omitempty"`
	Fsize        int     `json:"fsize,omitempty"`
	MinPlay      float64 `json:"minPlay,omitempty"`
	SugPlay      float64 `json:"sugPlay,omitempty"`
	Promo        float64 `json:"promo,omitempty"`
	Retail       float64 `json:"retail,omitempty"`
	PtpFT        int     `json:"ptpFT,omitempty"`
	PtpDT        int     `json:"ptpDT,omitempty"`
	PtpDA        int     `json:"ptpDA,omitempty"`
	Type         string  `json:"type,omitempty"`
	TokenlyID    string  `json:"tokenlyID,omitempty"`
	DisallowPlay bool    `json:"disPlay,omitempty"`
	MinBuy       float64 `json:"minBuy,omitempty"`
	SugBuy       float64 `json:"sugBuy,omitempty"`
	SubType      string  `json:"subtype,omitempty"`
	// ToDo: Add per file granularity back, requires custom json marshalling to omit
	// Storage Oip041Storage `json:"storage"`
}

type Oip041Address struct {
	Token   string `json:"token"`
	Address string `json:"address"`
}

type Oip041Tokens map[string]int
<|endoftext|>


<commit_before>package middlewares

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/solefaucet/sole-server/models"
)

// PersonalyAuthRequired rejects request if client ip is not in the list and signature not match
func PersonalyAuthRequired(whitelistIPs, appHash, secretKey string) gin.HandlerFunc {
	ips := make(map[string]struct{})
	for _, v := range strings.Split(whitelistIPs, ",") {
		ips[v] = struct{}{}
	}

	return func(c *gin.Context) {
		if _, ok := ips[c.ClientIP()]; !ok {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		httprequest, _ := httputil.DumpRequest(c.Request, true)
		data := fmt.Sprintf("%v:%v:%v", c.Query("user_id"), appHash, secretKey)
		if sign := fmt.Sprintf("%x", md5.Sum([]byte(data))); sign != c.Query("signature") {
			logrus.WithFields(logrus.Fields{
				"event":       models.EventPersonalyInvalidSignature,
				"user_id":     c.Query("user_id"),
				"signature":   sign,
				"q_signature": c.Query("signature"),
				"request":     string(httprequest),
			}).Error("signature not matched")
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		c.Next()
	}
}
<commit_msg>Bypass middleware for test<commit_after>package middlewares

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/solefaucet/sole-server/models"
)

// PersonalyAuthRequired rejects request if client ip is not in the list and signature not match
func PersonalyAuthRequired(whitelistIPs, appHash, secretKey string) gin.HandlerFunc {
	ips := make(map[string]struct{})
	for _, v := range strings.Split(whitelistIPs, ",") {
		ips[v] = struct{}{}
	}

	return func(c *gin.Context) {
		c.Next()
		return

		if _, ok := ips[c.ClientIP()]; !ok {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		httprequest, _ := httputil.DumpRequest(c.Request, true)
		data := fmt.Sprintf("%v:%v:%v", c.Query("user_id"), appHash, secretKey)
		if sign := fmt.Sprintf("%x", md5.Sum([]byte(data))); sign != c.Query("signature") {
			logrus.WithFields(logrus.Fields{
				"event":       models.EventPersonalyInvalidSignature,
				"user_id":     c.Query("user_id"),
				"signature":   sign,
				"q_signature": c.Query("signature"),
				"request":     string(httprequest),
			}).Error("signature not matched")
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		c.Next()
	}
}
<|endoftext|>


<commit_before>//   Copyright 2020 MSolution.IO
//
//   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 tagginges

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/olivere/elastic"
	"github.com/trackit/jsonlog"

	indexSource "github.com/trackit/trackit/aws/usageReports/es"
	"github.com/trackit/trackit/tagging/utils"
)

const urlFormat = "https://console.aws.amazon.com/es/home?region=%s#domain:resource=%s;action=dashboard"

// Process generates tagging reports from ES reports
func Process(ctx context.Context, userId int, resourceTypeString string) ([]utils.TaggingReportDocument, error) {
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	logger.Info("Processing reports.", map[string]interface{}{
		"type": resourceTypeString,
	})

	hits, err := fetchReports(ctx, userId)
	if err != nil {
		return nil, err
	}

	var documents []utils.TaggingReportDocument
	for _, hit := range hits {
		document, success := processHit(ctx, hit, resourceTypeString)
		if success {
			documents = append(documents, document)
		}
	}

	logger.Info("Reports processed.", map[string]interface{}{
		"type":  resourceTypeString,
		"count": len(documents),
	})
	return documents, nil
}

// processHit converts an elasticSearch hit into a TaggingReportDocument
// Second argument is true if operation is a success
func processHit(ctx context.Context, hit *elastic.SearchHit, resourceTypeString string) (utils.TaggingReportDocument, bool) {
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	var source indexSource.DomainReport
	err := json.Unmarshal(*hit.Source, &source)
	if err != nil {
		logger.Error("Could not process report.", map[string]interface{}{
			"type": resourceTypeString,
		})
		return utils.TaggingReportDocument{}, false
	}

	regionForURL := utils.GetRegionForURL(source.Domain.Region)

	document := utils.TaggingReportDocument{
		Account:      source.Account,
		ResourceID:   source.Domain.DomainID,
		ResourceType: resourceTypeString,
		Region:       source.Domain.Region,
		URL:          fmt.Sprintf(urlFormat, regionForURL, source.Domain.DomainID),
		Tags:         source.Domain.Tags,
	}
	return document, true
}
<commit_msg>Fixed Elasticsearch domain URL in tagging reports<commit_after>//   Copyright 2020 MSolution.IO
//
//   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 tagginges

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/olivere/elastic"
	"github.com/trackit/jsonlog"

	indexSource "github.com/trackit/trackit/aws/usageReports/es"
	"github.com/trackit/trackit/tagging/utils"
)

const urlFormat = "https://console.aws.amazon.com/es/home?region=%s#domain:resource=%s;action=dashboard;tab=TAB_OVERVIEW_ID"

// Process generates tagging reports from ES reports
func Process(ctx context.Context, userId int, resourceTypeString string) ([]utils.TaggingReportDocument, error) {
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	logger.Info("Processing reports.", map[string]interface{}{
		"type": resourceTypeString,
	})

	hits, err := fetchReports(ctx, userId)
	if err != nil {
		return nil, err
	}

	var documents []utils.TaggingReportDocument
	for _, hit := range hits {
		document, success := processHit(ctx, hit, resourceTypeString)
		if success {
			documents = append(documents, document)
		}
	}

	logger.Info("Reports processed.", map[string]interface{}{
		"type":  resourceTypeString,
		"count": len(documents),
	})
	return documents, nil
}

// processHit converts an elasticSearch hit into a TaggingReportDocument
// Second argument is true if operation is a success
func processHit(ctx context.Context, hit *elastic.SearchHit, resourceTypeString string) (utils.TaggingReportDocument, bool) {
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	var source indexSource.DomainReport
	err := json.Unmarshal(*hit.Source, &source)
	if err != nil {
		logger.Error("Could not process report.", map[string]interface{}{
			"type": resourceTypeString,
		})
		return utils.TaggingReportDocument{}, false
	}

	regionForURL := utils.GetRegionForURL(source.Domain.Region)

	document := utils.TaggingReportDocument{
		Account:      source.Account,
		ResourceID:   source.Domain.DomainID,
		ResourceType: resourceTypeString,
		Region:       source.Domain.Region,
		URL:          fmt.Sprintf(urlFormat, regionForURL, source.Domain.DomainID),
		Tags:         source.Domain.Tags,
	}
	return document, true
}
<|endoftext|>


<commit_before>package xff

import (
	"testing"

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

func TestParse_none(t *testing.T) {
	res := Parse("")
	assert.Equal(t, "", res)
}

func TestParse_localhost(t *testing.T) {
	res := Parse("127.0.0.1")
	assert.Equal(t, "", res)
}

func TestParse_invalid(t *testing.T) {
	res := Parse("invalid")
	assert.Equal(t, "", res)
}

func TestParse_invalid_sioux(t *testing.T) {
	res := Parse("123#1#2#3")
	assert.Equal(t, "", res)
}

func TestParse_invalid_private_lookalike(t *testing.T) {
	res := Parse("102.3.2.1")
	assert.Equal(t, "102.3.2.1", res)
}

func TestParse_valid(t *testing.T) {
	res := Parse("68.45.152.220")
	assert.Equal(t, "68.45.152.220", res)
}

func TestParse_multi_first(t *testing.T) {
	res := Parse("12.13.14.15, 68.45.152.220")
	assert.Equal(t, "12.13.14.15", res)
}

func TestParse_multi_last(t *testing.T) {
	res := Parse("192.168.110.162, 190.57.149.90")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_multi_with_invalid(t *testing.T) {
	res := Parse("192.168.110.162, invalid, 190.57.149.90")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_multi_with_invalid2(t *testing.T) {
	res := Parse("192.168.110.162, 190.57.149.90, invalid")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_multi_with_invalid_sioux(t *testing.T) {
	res := Parse("192.168.110.162, 190.57.149.90, 123#1#2#3")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_ipv6_with_port(t *testing.T) {
	res := Parse("2604:2000:71a9:bf00:f178:a500:9a2d:670d")
	assert.Equal(t, "2604:2000:71a9:bf00:f178:a500:9a2d:670d", res)
}
<commit_msg>Add some test for GetRemoteAddr<commit_after>package xff

import (
	"net/http"
	"testing"

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

func TestParse_none(t *testing.T) {
	res := Parse("")
	assert.Equal(t, "", res)
}

func TestParse_localhost(t *testing.T) {
	res := Parse("127.0.0.1")
	assert.Equal(t, "", res)
}

func TestParse_invalid(t *testing.T) {
	res := Parse("invalid")
	assert.Equal(t, "", res)
}

func TestParse_invalid_sioux(t *testing.T) {
	res := Parse("123#1#2#3")
	assert.Equal(t, "", res)
}

func TestParse_invalid_private_lookalike(t *testing.T) {
	res := Parse("102.3.2.1")
	assert.Equal(t, "102.3.2.1", res)
}

func TestParse_valid(t *testing.T) {
	res := Parse("68.45.152.220")
	assert.Equal(t, "68.45.152.220", res)
}

func TestParse_multi_first(t *testing.T) {
	res := Parse("12.13.14.15, 68.45.152.220")
	assert.Equal(t, "12.13.14.15", res)
}

func TestParse_multi_last(t *testing.T) {
	res := Parse("192.168.110.162, 190.57.149.90")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_multi_with_invalid(t *testing.T) {
	res := Parse("192.168.110.162, invalid, 190.57.149.90")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_multi_with_invalid2(t *testing.T) {
	res := Parse("192.168.110.162, 190.57.149.90, invalid")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_multi_with_invalid_sioux(t *testing.T) {
	res := Parse("192.168.110.162, 190.57.149.90, 123#1#2#3")
	assert.Equal(t, "190.57.149.90", res)
}

func TestParse_ipv6_with_port(t *testing.T) {
	res := Parse("2604:2000:71a9:bf00:f178:a500:9a2d:670d")
	assert.Equal(t, "2604:2000:71a9:bf00:f178:a500:9a2d:670d", res)
}

func TestGetRemoteAddr(t *testing.T) {
	assert.Equal(t, "1.2.3.4:1234", GetRemoteAddr(&http.Request{RemoteAddr: "1.2.3.4:1234"}))
	assert.Equal(t, "[2001:db8:0:1:1:1:1:1]:1234", GetRemoteAddr(&http.Request{RemoteAddr: "[2001:db8:0:1:1:1:1:1]:1234"}))
	assert.Equal(t, "[2001:db8:0:1:1:1:1:1]:1234", GetRemoteAddr(&http.Request{
		RemoteAddr: "1.2.3.4:1234",
		Header:     http.Header{"X-Forwarded-For": []string{"2001:db8:0:1:1:1:1:1"}},
	}))
}
<|endoftext|>


<commit_before>package saml

import (
	"crypto/rand"
	"crypto/rsa"
	"testing"
	"time"

	"github.com/lestrrat/go-libxml2"
	"github.com/lestrrat/go-saml/binding"
	"github.com/lestrrat/go-saml/ns"
	"github.com/lestrrat/go-xmlsec"
	"github.com/stretchr/testify/assert"
)

func TestAssertion_XML(t *testing.T) {
	a := Assertion{
		Conditions: Conditions{
			NotBefore:    time.Now(),
			NotOnOrAfter: time.Now(),
		},
		Version:      "2.0",
		ID:           "b07b804c-7c29-ea16-7300-4f3d6f7928ac",
		IssueInstant: time.Now(),
		Issuer:       "https://idp.example.org/SAML2",
		Subject: Subject{
			NameID: NameID{
				Format: NameIDFormatTransient,
				Value:  "3f7b3dcf-1674-4ecd-92c8-1544f346baf8",
			},
			SubjectConfirmation: SubjectConfirmation{
				InResponseTo: "aaf23196-1773-2113-474a-fe114412ab72",
				Recipient:    "https://sp.example.com/SAML2/SSO/POST",
				NotOnOrAfter: time.Now(),
			},
		},
		AuthnStatement: AuthnStatement{
			AuthnInstant: time.Now(),
			SessionIndex: "b07b804c-7c29-ea16-7300-4f3d6f7928ac",
			AuthnContext: AuthnContext{
				AuthnContextClassRef: PasswordProtectedTransport,
			},
		},
	}
	a.Conditions.AddAudienceRestriction(
		AudienceRestriction{
			Audience: []string{"https://sp.example.com/SAML2"},
		},
	)
	a.AddAttribute(Attribute{
		Attrs: map[string]string{
			"xmlns:" + ns.X500.Prefix:     ns.X500.URI,
			ns.X500.AddPrefix("Encoding"): "LDAP",
			"NameFormat":                  ns.NameFormatURI,
		},
		Name:         "urn:oid:1.3.6.1.4.1.5923.1.1.1.1",
		FriendlyName: "eduPersonAffiliation",
		Values: []AttributeValue{
			AttributeValue{
				Type:  "xs:string",
				Value: "member",
			},
			AttributeValue{
				Type:  "xs:string",
				Value: "staff",
			},
		},
	})

	xmlstr, err := a.Serialize()
	if !assert.NoError(t, err, "Serialize() succeeds") {
		return
	}

	p := libxml2.NewParser(libxml2.XMLParseDTDLoad | libxml2.XMLParseDTDAttr | libxml2.XMLParseNoEnt)
	c14ndoc, err := p.ParseString(xmlstr)
	if !assert.NoError(t, err, "Parse C14N XML doc succeeds") {
		return
	}
	defer c14ndoc.Free()

	root, err := c14ndoc.DocumentElement()
	if !assert.NoError(t, err, "DocumentElement succeeds") {
		return
	}

	privkey, err := rsa.GenerateKey(rand.Reader, 2048)
	if !assert.NoError(t, err, "GenerateKey succeeds") {
		return
	}

	s, err := NewGenericSign(xmlsec.RsaSha1, xmlsec.Enveloped, xmlsec.Sha1, xmlsec.ExclC14N)
	if !assert.NoError(t, err, "NewGenericSign succeeds") {
		return
	}
	s.Sign(root, privkey, "")
}

func TestAuthnRequest(t *testing.T) {
	ar := NewAuthnRequest()
	ar.ID = "809707f0030a5d00620c9d9df97f627afe9dcc24"
	ar.Version = "2.0"
	ar.IssueInstant = time.Now()
	ar.Issuer = "http://sp.example.com/metadata"
	ar.Destination = "http://idp.example.com/sso"
	ar.ProviderName = "FooProvider"
	ar.ProtocolBinding = binding.HTTPPost
	ar.AssertionConsumerServiceURL = "http://sp.example.com/acs"
	ar.NameIDPolicy = NewNameIDPolicy(NameIDFormatEmailAddress, true)
	ar.RequestedAuthnContext = NewRequestedAuthnContext(
		"exact",
		"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
	)

	xmlstr, err := ar.Serialize()
	if !assert.NoError(t, err, "Serialize() succeeds") {
		return
	}

	p := libxml2.NewParser(libxml2.XMLParseDTDLoad | libxml2.XMLParseDTDAttr | libxml2.XMLParseNoEnt)
	c14ndoc, err := p.ParseString(xmlstr)
	if !assert.NoError(t, err, "Parse C14N XML doc succeeds") {
		return
	}
	defer c14ndoc.Free()

	root, err := c14ndoc.DocumentElement()
	if !assert.NoError(t, err, "DocumentElement succeeds") {
		return
	}

	privkey, err := rsa.GenerateKey(rand.Reader, 2048)
	if !assert.NoError(t, err, "GenerateKey succeeds") {
		return
	}

	s, err := NewGenericSign(xmlsec.RsaSha1, xmlsec.Enveloped, xmlsec.Sha1, xmlsec.ExclC14N)
	if !assert.NoError(t, err, "NewGenericSign succeeds") {
		return
	}
	s.Sign(root, privkey, "urn:oasis:names:tc:SAML:2.0:protocol:AuthnRequest")

	t.Logf("%s", c14ndoc.Dump(true))
}<commit_msg>Use namespace tools<commit_after>package saml

import (
	"crypto/rand"
	"crypto/rsa"
	"testing"
	"time"

	"github.com/lestrrat/go-libxml2"
	"github.com/lestrrat/go-saml/binding"
	"github.com/lestrrat/go-saml/ns"
	"github.com/lestrrat/go-xmlsec"
	"github.com/stretchr/testify/assert"
)

func TestAssertion_XML(t *testing.T) {
	a := Assertion{
		Conditions: Conditions{
			NotBefore:    time.Now(),
			NotOnOrAfter: time.Now(),
		},
		Version:      "2.0",
		ID:           "b07b804c-7c29-ea16-7300-4f3d6f7928ac",
		IssueInstant: time.Now(),
		Issuer:       "https://idp.example.org/SAML2",
		Subject: Subject{
			NameID: NameID{
				Format: NameIDFormatTransient,
				Value:  "3f7b3dcf-1674-4ecd-92c8-1544f346baf8",
			},
			SubjectConfirmation: SubjectConfirmation{
				InResponseTo: "aaf23196-1773-2113-474a-fe114412ab72",
				Recipient:    "https://sp.example.com/SAML2/SSO/POST",
				NotOnOrAfter: time.Now(),
			},
		},
		AuthnStatement: AuthnStatement{
			AuthnInstant: time.Now(),
			SessionIndex: "b07b804c-7c29-ea16-7300-4f3d6f7928ac",
			AuthnContext: AuthnContext{
				AuthnContextClassRef: PasswordProtectedTransport,
			},
		},
	}
	a.Conditions.AddAudienceRestriction(
		AudienceRestriction{
			Audience: []string{"https://sp.example.com/SAML2"},
		},
	)
	a.AddAttribute(Attribute{
		Attrs: map[string]string{
			"xmlns:" + ns.X500.Prefix:     ns.X500.URI,
			ns.X500.AddPrefix("Encoding"): "LDAP",
			"NameFormat":                  ns.NameFormatURI,
		},
		Name:         "urn:oid:1.3.6.1.4.1.5923.1.1.1.1",
		FriendlyName: "eduPersonAffiliation",
		Values: []AttributeValue{
			AttributeValue{
				Type:  ns.XMLSchema.AddPrefix("string"),
				Value: "member",
			},
			AttributeValue{
				Type:  ns.XMLSchema.AddPrefix("string"),
				Value: "staff",
			},
		},
	})

	xmlstr, err := a.Serialize()
	if !assert.NoError(t, err, "Serialize() succeeds") {
		return
	}

	p := libxml2.NewParser(libxml2.XMLParseDTDLoad | libxml2.XMLParseDTDAttr | libxml2.XMLParseNoEnt)
	c14ndoc, err := p.ParseString(xmlstr)
	if !assert.NoError(t, err, "Parse C14N XML doc succeeds") {
		return
	}
	defer c14ndoc.Free()

	root, err := c14ndoc.DocumentElement()
	if !assert.NoError(t, err, "DocumentElement succeeds") {
		return
	}

	privkey, err := rsa.GenerateKey(rand.Reader, 2048)
	if !assert.NoError(t, err, "GenerateKey succeeds") {
		return
	}

	s, err := NewGenericSign(xmlsec.RsaSha1, xmlsec.Enveloped, xmlsec.Sha1, xmlsec.ExclC14N)
	if !assert.NoError(t, err, "NewGenericSign succeeds") {
		return
	}
	s.Sign(root, privkey, "")

	t.Logf("%s", c14ndoc.Dump(true))
}

func TestAuthnRequest(t *testing.T) {
	ar := NewAuthnRequest()
	ar.ID = "809707f0030a5d00620c9d9df97f627afe9dcc24"
	ar.Version = "2.0"
	ar.IssueInstant = time.Now()
	ar.Issuer = "http://sp.example.com/metadata"
	ar.Destination = "http://idp.example.com/sso"
	ar.ProviderName = "FooProvider"
	ar.ProtocolBinding = binding.HTTPPost
	ar.AssertionConsumerServiceURL = "http://sp.example.com/acs"
	ar.NameIDPolicy = NewNameIDPolicy(NameIDFormatEmailAddress, true)
	ar.RequestedAuthnContext = NewRequestedAuthnContext(
		"exact",
		"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
	)

	xmlstr, err := ar.Serialize()
	if !assert.NoError(t, err, "Serialize() succeeds") {
		return
	}

	p := libxml2.NewParser(libxml2.XMLParseDTDLoad | libxml2.XMLParseDTDAttr | libxml2.XMLParseNoEnt)
	c14ndoc, err := p.ParseString(xmlstr)
	if !assert.NoError(t, err, "Parse C14N XML doc succeeds") {
		return
	}
	defer c14ndoc.Free()

	root, err := c14ndoc.DocumentElement()
	if !assert.NoError(t, err, "DocumentElement succeeds") {
		return
	}

	privkey, err := rsa.GenerateKey(rand.Reader, 2048)
	if !assert.NoError(t, err, "GenerateKey succeeds") {
		return
	}

	s, err := NewGenericSign(xmlsec.RsaSha1, xmlsec.Enveloped, xmlsec.Sha1, xmlsec.ExclC14N)
	if !assert.NoError(t, err, "NewGenericSign succeeds") {
		return
	}
	s.Sign(root, privkey, "urn:oasis:names:tc:SAML:2.0:protocol:AuthnRequest")

	t.Logf("%s", c14ndoc.Dump(true))
}<|endoftext|>