<commit_before>package main

import (
	"log"
	"net/http"

	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
)

// This example show a complete (GET,PUT,POST,DELETE) conventional example of
// a REST Resource including documentation to be served by e.g. a Swagger UI
// It is recommended to create a Resource struct (UserResource) that can encapsulate
// an object that provide domain access (a DAO)
// It has a Register method including the complete Route mapping to methods together
// with all the appropriate documentation
//
// POST http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
// GET http://localhost:8080/users/1
//
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
// DELETE http://localhost:8080/users/1
//

type User struct {
	Id, Name string
}

type UserResource struct {
	// normally one would use DAO (data access object)
	users map[string]User
}

func (u UserResource) Register(container *restful.Container) {
	ws := new(restful.WebService)
	ws.
		Path("/users").
		Consumes(restful.MIME_XML, restful.MIME_JSON).
		Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well

	ws.Route(ws.GET("/{user-id}").To(u.findUser).
		// docs
		Doc("get a user").
		Operation("findUser").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
		Writes(User{})) // on the response

	ws.Route(ws.PUT("/{user-id}").To(u.updateUser).
		// docs
		Doc("update a user").
		Operation("updateUser").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
		Reads(User{})) // from the request

	ws.Route(ws.PUT("").To(u.createUser).
		// docs
		Doc("create a user").
		Operation("createUser").
		Reads(User{})) // from the request

	ws.Route(ws.DELETE("/{user-id}").To(u.removeUser).
		// docs
		Doc("delete a user").
		Operation("removeUser").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")))

	container.Add(ws)
}

// GET http://localhost:8080/users/1
//
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
	id := request.PathParameter("user-id")
	usr := u.users[id]
	if len(usr.Id) == 0 {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "User could not be found.")
	} else {
		response.WriteEntity(usr)
	}
}

// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {
	usr := new(User)
	err := request.ReadEntity(&usr)
	if err == nil {
		u.users[usr.Id] = *usr
		response.WriteEntity(usr)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusInternalServerError, err.Error())
	}
}

// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {
	usr := User{Id: request.PathParameter("user-id")}
	err := request.ReadEntity(&usr)
	if err == nil {
		u.users[usr.Id] = usr
		response.WriteHeader(http.StatusCreated)
		response.WriteEntity(usr)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusInternalServerError, err.Error())
	}
}

// DELETE http://localhost:8080/users/1
//
func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {
	id := request.PathParameter("user-id")
	delete(u.users, id)
}

func main() {
	wsContainer := restful.NewContainer()
	u := UserResource{map[string]User{}}
	u.Register(wsContainer)

	// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
	// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
	// Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field.
	config := swagger.Config{
		WebServices:    wsContainer.RegisteredWebServices(), // you control what services are visible
		WebServicesUrl: "http://localhost:8080",
		ApiPath:        "/apidocs.json",

		// Optionally, specifiy where the UI is located
		SwaggerPath:     "/apidocs/",
		SwaggerFilePath: "/Users/emicklei/Downloads/swagger-ui-1.1.7"}
	swagger.RegisterSwaggerService(config, wsContainer)

	log.Printf("start listening on localhost:8080")
	server := &http.Server{Addr: ":8080", Handler: wsContainer}
	log.Fatal(server.ListenAndServe())
}
<commit_msg>Use POST for create in example.  Use PUTiwhen you provide its public id,<commit_after>package main

import (
	"log"
	"net/http"
	"strconv"

	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
)

// This example show a complete (GET,PUT,POST,DELETE) conventional example of
// a REST Resource including documentation to be served by e.g. a Swagger UI
// It is recommended to create a Resource struct (UserResource) that can encapsulate
// an object that provide domain access (a DAO)
// It has a Register method including the complete Route mapping to methods together
// with all the appropriate documentation
//
// POST http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
// GET http://localhost:8080/users/1
//
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
// DELETE http://localhost:8080/users/1
//

type User struct {
	Id, Name string
}

type UserResource struct {
	// normally one would use DAO (data access object)
	users map[string]User
}

func (u UserResource) Register(container *restful.Container) {
	ws := new(restful.WebService)
	ws.
		Path("/users").
		Consumes(restful.MIME_XML, restful.MIME_JSON).
		Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well

	ws.Route(ws.GET("/{user-id}").To(u.findUser).
		// docs
		Doc("get a user").
		Operation("findUser").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
		Writes(User{})) // on the response

	ws.Route(ws.PUT("/{user-id}").To(u.updateUser).
		// docs
		Doc("update a user").
		Operation("updateUser").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
		Reads(User{})) // from the request

	ws.Route(ws.POST("").To(u.createUser).
		// docs
		Doc("create a user").
		Operation("createUser").
		Reads(User{})) // from the request

	ws.Route(ws.DELETE("/{user-id}").To(u.removeUser).
		// docs
		Doc("delete a user").
		Operation("removeUser").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")))

	container.Add(ws)
}

// GET http://localhost:8080/users/1
//
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
	id := request.PathParameter("user-id")
	usr := u.users[id]
	if len(usr.Id) == 0 {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "User could not be found.")
	}
	response.WriteEntity(usr)
}

// POST http://localhost:8080/users
// <User><Name>Melissa</Name></User>
//
func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {
	usr := new(User)
	err := request.ReadEntity(usr)
	if err != nil {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusInternalServerError, err.Error())
		return
	}
	usr.Id = strconv.Itoa(len(u.users) + 1) // simple id generation
	u.users[usr.Id] = *usr
	response.WriteHeader(http.StatusCreated)
	response.WriteEntity(usr)
}

// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {
	usr := new(User)
	err := request.ReadEntity(&usr)
	if err != nil {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusInternalServerError, err.Error())
		return
	}
	u.users[usr.Id] = *usr
	response.WriteEntity(usr)
}

// DELETE http://localhost:8080/users/1
//
func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {
	id := request.PathParameter("user-id")
	delete(u.users, id)
}

func main() {
	wsContainer := restful.NewContainer()
	u := UserResource{map[string]User{}}
	u.Register(wsContainer)

	// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
	// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
	// Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field.
	config := swagger.Config{
		WebServices:    wsContainer.RegisteredWebServices(), // you control what services are visible
		WebServicesUrl: "http://localhost:8080",
		ApiPath:        "/apidocs.json",

		// Optionally, specifiy where the UI is located
		SwaggerPath:     "/apidocs/",
		SwaggerFilePath: "/Users/emicklei/Downloads/swagger-ui-1.1.7"}
	swagger.RegisterSwaggerService(config, wsContainer)

	log.Printf("start listening on localhost:8080")
	server := &http.Server{Addr: ":8080", Handler: wsContainer}
	log.Fatal(server.ListenAndServe())
}
<|endoftext|>


<commit_before>//Package bytearray provides the ability to convert byte arrays to
// Strings, Base-64 and hexadecimal and vice-versa.
//
// Cryptography is mainly about manipulating byte arrays, so this package provides
// the different translations you need:
//
// - Plain-text strings need to be converted to a byte array for encryption
// and, after decryption, need to be converted from a byte array back to a
// String.
//
// - Encrypted byte arrays look like random bytes, which means they can't be
// reliably represented as a String. The simplest way to represent arbitrary bytes
// as a String is using Base-64. This class lets you convert a byte array of
// encrypted data to Base-64 so it can be easily stored and back again so it can
// be decrypted.
//
// - Finally, this class also allows you to transform a byte array to a
// hexadecimal String and back again. This is most useful in development when
// you need to print out values to see what's going on. Conversion from
// hexadecimal to byte array is occasionally useful, but chances are you'll use
// byte[] to hex most of the time.
//
// The naming convention for functions is set up from the point of view of
// a byte array. For example, a byte array can go:
//  ToHexString
// and back:
//  FromHexString
// The same pattern is usef for each pair of methods (to/from hex, base64 and string).
package bytearray

import (
	b64 "encoding/base64"
	hx "encoding/hex"
)

// ToHexString renders the given byte array as a hex String.
//
// This is a convenience method useful for checking values during development.
//
// Internally, this checks for null and then calls hex.EncodeToString.
//
//   The byteArray parameter : The byte array to be encoded.
//   :return: A hex string representation of the byte array.
//
// The byteArray parameter is encoded as a hex string representation.
func ToHexString(byteArray []byte) string {
	return hx.EncodeToString(byteArray)
}

// FromHexString converts the given hex string to a byte array.
//
// The hex parameter is parsed to bytes.
func FromHexString(hex string) ([]byte, error) {
	return hx.DecodeString(hex)
}

// ToBase64String encodes the given byte array as a base-64 String.
//
// Internally, this checks for null and then calls the Apache commons-codec
// method base64.b64encode(bytetarray).
// The bytes parameter is returned encoded using base-64.
func ToBase64String(bytes []byte) string {
	return b64.StdEncoding.EncodeToString(bytes)
}

// FromBase64String decodes the given base-64 string into a byte array.
//
// The base64 parameter is decoded to a byte slice.
func FromBase64String(base64 string) ([]byte, error) {
	return b64.StdEncoding.DecodeString(base64)
}

// ToString converts the given byte array to a String.
//
// The bytes parameter is converted to the String represented by the given bytes.
func ToString(bytes []byte) string {
	return string(bytes)
}

// FromString converts the given String to a byte array.
//
// The string parameter is converted to a byte array.
func FromString(unicode string) []byte {
	return []byte(unicode)
}
<commit_msg>Tidying up godoc and standardising parameter names.<commit_after>//Package bytearray provides the ability to convert byte arrays to
// Strings, Base-64 and hexadecimal and vice-versa.
//
// Cryptography is mainly about manipulating byte arrays, so this package provides
// the different translations you need:
//
// - Plain-text strings need to be converted to a byte array for encryption
// and, after decryption, need to be converted from a byte array back to a
// String.
//
// - Encrypted byte arrays look like random bytes, which means they can't be
// reliably represented as a String. The simplest way to represent arbitrary bytes
// as a String is using Base-64. This class lets you convert a byte array of
// encrypted data to Base-64 so it can be easily stored and back again so it can
// be decrypted.
//
// - Finally, this class also allows you to transform a byte array to a
// hexadecimal String and back again. This is most useful in development when
// you need to print out values to see what's going on. Conversion from
// hexadecimal to byte array is occasionally useful, but chances are you'll use
// byte[] to hex most of the time.
//
// The naming convention for functions is set up from the point of view of
// a byte array. For example, a byte array can go:
//  ToHexString
// and back:
//  FromHexString
// The same pattern is usef for each pair of methods (to/from hex, base64 and string).
package bytearray

import (
	b64 "encoding/base64"
	hx "encoding/hex"
)

// ToHexString renders the given byte array as a hex String.
//
// This is a convenience method useful for testing values during development.
//
// The byteArray parameter is encoded as a hex string representation.
func ToHexString(byteArray []byte) string {
	return hx.EncodeToString(byteArray)
}

// FromHexString converts the given hex string to a byte array.
//
// This is a convenience method useful for testing values during development.
//
// The hex parameter is parsed to a byte array.
func FromHexString(hex string) ([]byte, error) {
	return hx.DecodeString(hex)
}

// ToBase64String encodes the given byte array as a base-64 String.
//
// Internally, this checks for null and then calls the Apache commons-codec
// method base64.b64encode(bytetarray).
// The byteArray parameter is returned encoded using base-64.
func ToBase64String(byteArray []byte) string {
	return b64.StdEncoding.EncodeToString(byteArray)
}

// FromBase64String decodes the given base-64 string into a byte array.
//
// The base64 parameter is decoded to a byte slice.
func FromBase64String(base64 string) ([]byte, error) {
	return b64.StdEncoding.DecodeString(base64)
}

// ToString converts the given byte array to a String.
//
// The byteArray parameter is converted to the String represented by the given bytes.
func ToString(byteArray []byte) string {
	return string(byteArray)
}

// FromString converts the given String to a byte array.
//
// The string parameter is converted to a byte array.
func FromString(unicode string) []byte {
	return []byte(unicode)
}
<|endoftext|>


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

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

                 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cid

import (
	"crypto/x509"
	"encoding/asn1"
	"encoding/hex"
	"encoding/pem"
	"fmt"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/common/attrmgr"
	"github.com/hyperledger/fabric/protos/msp"
	"github.com/pkg/errors"
)

// GetID returns the ID associated with the invoking identity.  This ID
// is guaranteed to be unique within the MSP.
func GetID(stub ChaincodeStubInterface) (string, error) {
	c, err := New(stub)
	if err != nil {
		return "", err
	}
	return c.GetID()
}

// GetMSPID returns the ID of the MSP associated with the identity that
// submitted the transaction
func GetMSPID(stub ChaincodeStubInterface) (string, error) {
	c, err := New(stub)
	if err != nil {
		return "", err
	}
	return c.GetMSPID()
}

// GetAttributeValue returns value of the specified attribute
func GetAttributeValue(stub ChaincodeStubInterface, attrName string) (value string, found bool, err error) {
	c, err := New(stub)
	if err != nil {
		return "", false, err
	}
	return c.GetAttributeValue(attrName)
}

// AssertAttributeValue checks to see if an attribute value equals the specified value
func AssertAttributeValue(stub ChaincodeStubInterface, attrName, attrValue string) error {
	c, err := New(stub)
	if err != nil {
		return err
	}
	return c.AssertAttributeValue(attrName, attrValue)
}

// GetX509Certificate returns the X509 certificate associated with the client,
// or nil if it was not identified by an X509 certificate.
func GetX509Certificate(stub ChaincodeStubInterface) (*x509.Certificate, error) {
	c, err := New(stub)
	if err != nil {
		return nil, err
	}
	return c.GetX509Certificate()
}

// ClientIdentityImpl implements the ClientIdentity interface
type clientIdentityImpl struct {
	stub  ChaincodeStubInterface
	mspID string
	cert  *x509.Certificate
	attrs *attrmgr.Attributes
}

// New returns an instance of ClientIdentity
func New(stub ChaincodeStubInterface) (ClientIdentity, error) {
	c := &clientIdentityImpl{stub: stub}
	err := c.init()
	if err != nil {
		return nil, err
	}
	return c, nil
}

// GetID returns the ID associated with the invoking identity.  This ID
// is guaranteed to be unique within the MSP.
func (c *clientIdentityImpl) GetID() (string, error) {
	return getDN(c.cert), nil
}

// GetMSPID returns the ID of the MSP associated with the identity that
// submitted the transaction
func (c *clientIdentityImpl) GetMSPID() (string, error) {
	return c.mspID, nil
}

// GetAttributeValue returns value of the specified attribute
func (c *clientIdentityImpl) GetAttributeValue(attrName string) (value string, found bool, err error) {
	if c.attrs == nil {
		return "", false, nil
	}
	return c.attrs.Value(attrName)
}

// AssertAttributeValue checks to see if an attribute value equals the specified value
func (c *clientIdentityImpl) AssertAttributeValue(attrName, attrValue string) error {
	val, ok, err := c.GetAttributeValue(attrName)
	if err != nil {
		return err
	}
	if !ok {
		return errors.Errorf("Attribute '%s' was not found", attrName)
	}
	if val != attrValue {
		return errors.Errorf("Attribute '%s' equals '%s', not '%s'", attrName, val, attrValue)
	}
	return nil
}

// GetX509Certificate returns the X509 certificate associated with the client,
// or nil if it was not identified by an X509 certificate.
func (c *clientIdentityImpl) GetX509Certificate() (*x509.Certificate, error) {
	return c.cert, nil
}

// Initialize the client
func (c *clientIdentityImpl) init() error {
	signingID, err := c.getIdentity()
	if err != nil {
		return err
	}
	c.mspID = signingID.GetMspid()
	idbytes := signingID.GetIdBytes()
	block, _ := pem.Decode(idbytes)
	if block == nil || block.Type != "CERTIFICATE" {
		bt := "none"
		if block != nil {
			bt = block.Type
		}
		return errors.Errorf("unexpected PEM block found. Expected a certificate but found a block of type: %s", bt)
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return errors.Wrap(err, "failed to parse certificate")
	}
	c.cert = cert
	attrs, err := attrmgr.New().GetAttributesFromCert(cert)
	if err != nil {
		return errors.WithMessage(err, "failed to get attributes from the transaction invoker's certificate")
	}
	c.attrs = attrs
	return nil
}

// Unmarshals the bytes returned by ChaincodeStubInterface.GetCreator method and
// returns the resulting msp.SerializedIdentity object
func (c *clientIdentityImpl) getIdentity() (*msp.SerializedIdentity, error) {
	sid := &msp.SerializedIdentity{}
	creator, err := c.stub.GetCreator()
	if err != nil || creator == nil {
		return nil, errors.WithMessage(err, "failed to get transaction invoker's identity from the chaincode stub")
	}
	err = proto.Unmarshal(creator, sid)
	if err != nil {
		return nil, errors.Wrap(err, "failed to unmarshal transaction invoker's identity")
	}
	return sid, nil
}

// Get the DN (distinquished name) associated with the subject of the certificate.
// NOTE: This code is almost a direct copy of the String() function in
// https://go-review.googlesource.com/c/go/+/67270/1/src/crypto/x509/pkix/pkix.go#26
// which returns a DN as defined by RFC 2253.
func getDN(cert *x509.Certificate) string {
	r := cert.Subject.ToRDNSequence()
	s := ""
	for i := 0; i < len(r); i++ {
		rdn := r[len(r)-1-i]
		if i > 0 {
			s += ","
		}
		for j, tv := range rdn {
			if j > 0 {
				s += "+"
			}
			typeString := tv.Type.String()
			typeName, ok := attributeTypeNames[typeString]
			if !ok {
				derBytes, err := asn1.Marshal(tv.Value)
				if err == nil {
					s += typeString + "=#" + hex.EncodeToString(derBytes)
					continue // No value escaping necessary.
				}
				typeName = typeString
			}
			valueString := fmt.Sprint(tv.Value)
			escaped := ""
			begin := 0
			for idx, c := range valueString {
				if (idx == 0 && (c == ' ' || c == '#')) ||
					(idx == len(valueString)-1 && c == ' ') {
					escaped += valueString[begin:idx]
					escaped += "\\" + string(c)
					begin = idx + 1
					continue
				}
				switch c {
				case ',', '+', '"', '\\', '<', '>', ';':
					escaped += valueString[begin:idx]
					escaped += "\\" + string(c)
					begin = idx + 1
				}
			}
			escaped += valueString[begin:]
			s += typeName + "=" + escaped
		}
	}
	return s
}

var attributeTypeNames = map[string]string{
	"2.5.4.6":  "C",
	"2.5.4.10": "O",
	"2.5.4.11": "OU",
	"2.5.4.3":  "CN",
	"2.5.4.5":  "SERIALNUMBER",
	"2.5.4.7":  "L",
	"2.5.4.8":  "ST",
	"2.5.4.9":  "STREET",
	"2.5.4.17": "POSTALCODE",
}
<commit_msg>[FAB-6466] Improve GetID to return unique ID<commit_after>/*
Copyright IBM Corp. 2017 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

                 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cid

import (
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"fmt"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/common/attrmgr"
	"github.com/hyperledger/fabric/protos/msp"
	"github.com/pkg/errors"
)

// GetID returns the ID associated with the invoking identity.  This ID
// is guaranteed to be unique within the MSP.
func GetID(stub ChaincodeStubInterface) (string, error) {
	c, err := New(stub)
	if err != nil {
		return "", err
	}
	return c.GetID()
}

// GetMSPID returns the ID of the MSP associated with the identity that
// submitted the transaction
func GetMSPID(stub ChaincodeStubInterface) (string, error) {
	c, err := New(stub)
	if err != nil {
		return "", err
	}
	return c.GetMSPID()
}

// GetAttributeValue returns value of the specified attribute
func GetAttributeValue(stub ChaincodeStubInterface, attrName string) (value string, found bool, err error) {
	c, err := New(stub)
	if err != nil {
		return "", false, err
	}
	return c.GetAttributeValue(attrName)
}

// AssertAttributeValue checks to see if an attribute value equals the specified value
func AssertAttributeValue(stub ChaincodeStubInterface, attrName, attrValue string) error {
	c, err := New(stub)
	if err != nil {
		return err
	}
	return c.AssertAttributeValue(attrName, attrValue)
}

// GetX509Certificate returns the X509 certificate associated with the client,
// or nil if it was not identified by an X509 certificate.
func GetX509Certificate(stub ChaincodeStubInterface) (*x509.Certificate, error) {
	c, err := New(stub)
	if err != nil {
		return nil, err
	}
	return c.GetX509Certificate()
}

// ClientIdentityImpl implements the ClientIdentity interface
type clientIdentityImpl struct {
	stub  ChaincodeStubInterface
	mspID string
	cert  *x509.Certificate
	attrs *attrmgr.Attributes
}

// New returns an instance of ClientIdentity
func New(stub ChaincodeStubInterface) (ClientIdentity, error) {
	c := &clientIdentityImpl{stub: stub}
	err := c.init()
	if err != nil {
		return nil, err
	}
	return c, nil
}

// GetID returns a unique ID associated with the invoking identity.
func (c *clientIdentityImpl) GetID() (string, error) {
	// The leading "x509::" distinquishes this as an X509 certificate, and
	// the subject and issuer DNs uniquely identify the X509 certificate.
	// The resulting ID will remain the same if the certificate is renewed.
	id := fmt.Sprintf("x509::%s::%s", getDN(&c.cert.Subject), getDN(&c.cert.Issuer))
	return base64.StdEncoding.EncodeToString([]byte(id)), nil
}

// GetMSPID returns the ID of the MSP associated with the identity that
// submitted the transaction
func (c *clientIdentityImpl) GetMSPID() (string, error) {
	return c.mspID, nil
}

// GetAttributeValue returns value of the specified attribute
func (c *clientIdentityImpl) GetAttributeValue(attrName string) (value string, found bool, err error) {
	if c.attrs == nil {
		return "", false, nil
	}
	return c.attrs.Value(attrName)
}

// AssertAttributeValue checks to see if an attribute value equals the specified value
func (c *clientIdentityImpl) AssertAttributeValue(attrName, attrValue string) error {
	val, ok, err := c.GetAttributeValue(attrName)
	if err != nil {
		return err
	}
	if !ok {
		return errors.Errorf("Attribute '%s' was not found", attrName)
	}
	if val != attrValue {
		return errors.Errorf("Attribute '%s' equals '%s', not '%s'", attrName, val, attrValue)
	}
	return nil
}

// GetX509Certificate returns the X509 certificate associated with the client,
// or nil if it was not identified by an X509 certificate.
func (c *clientIdentityImpl) GetX509Certificate() (*x509.Certificate, error) {
	return c.cert, nil
}

// Initialize the client
func (c *clientIdentityImpl) init() error {
	signingID, err := c.getIdentity()
	if err != nil {
		return err
	}
	c.mspID = signingID.GetMspid()
	idbytes := signingID.GetIdBytes()
	block, _ := pem.Decode(idbytes)
	if block == nil || block.Type != "CERTIFICATE" {
		bt := "none"
		if block != nil {
			bt = block.Type
		}
		return errors.Errorf("unexpected PEM block found. Expected a certificate but found a block of type: %s", bt)
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return errors.Wrap(err, "failed to parse certificate")
	}
	c.cert = cert
	attrs, err := attrmgr.New().GetAttributesFromCert(cert)
	if err != nil {
		return errors.WithMessage(err, "failed to get attributes from the transaction invoker's certificate")
	}
	c.attrs = attrs
	return nil
}

// Unmarshals the bytes returned by ChaincodeStubInterface.GetCreator method and
// returns the resulting msp.SerializedIdentity object
func (c *clientIdentityImpl) getIdentity() (*msp.SerializedIdentity, error) {
	sid := &msp.SerializedIdentity{}
	creator, err := c.stub.GetCreator()
	if err != nil || creator == nil {
		return nil, errors.WithMessage(err, "failed to get transaction invoker's identity from the chaincode stub")
	}
	err = proto.Unmarshal(creator, sid)
	if err != nil {
		return nil, errors.Wrap(err, "failed to unmarshal transaction invoker's identity")
	}
	return sid, nil
}

// Get the DN (distinquished name) associated with a pkix.Name.
// NOTE: This code is almost a direct copy of the String() function in
// https://go-review.googlesource.com/c/go/+/67270/1/src/crypto/x509/pkix/pkix.go#26
// which returns a DN as defined by RFC 2253.
func getDN(name *pkix.Name) string {
	r := name.ToRDNSequence()
	s := ""
	for i := 0; i < len(r); i++ {
		rdn := r[len(r)-1-i]
		if i > 0 {
			s += ","
		}
		for j, tv := range rdn {
			if j > 0 {
				s += "+"
			}
			typeString := tv.Type.String()
			typeName, ok := attributeTypeNames[typeString]
			if !ok {
				derBytes, err := asn1.Marshal(tv.Value)
				if err == nil {
					s += typeString + "=#" + hex.EncodeToString(derBytes)
					continue // No value escaping necessary.
				}
				typeName = typeString
			}
			valueString := fmt.Sprint(tv.Value)
			escaped := ""
			begin := 0
			for idx, c := range valueString {
				if (idx == 0 && (c == ' ' || c == '#')) ||
					(idx == len(valueString)-1 && c == ' ') {
					escaped += valueString[begin:idx]
					escaped += "\\" + string(c)
					begin = idx + 1
					continue
				}
				switch c {
				case ',', '+', '"', '\\', '<', '>', ';':
					escaped += valueString[begin:idx]
					escaped += "\\" + string(c)
					begin = idx + 1
				}
			}
			escaped += valueString[begin:]
			s += typeName + "=" + escaped
		}
	}
	return s
}

var attributeTypeNames = map[string]string{
	"2.5.4.6":  "C",
	"2.5.4.10": "O",
	"2.5.4.11": "OU",
	"2.5.4.3":  "CN",
	"2.5.4.5":  "SERIALNUMBER",
	"2.5.4.7":  "L",
	"2.5.4.8":  "ST",
	"2.5.4.9":  "STREET",
	"2.5.4.17": "POSTALCODE",
}
<|endoftext|>


<commit_before>package modules

import (
	"encoding/json"
	"fmt"
	"github.com/nmeum/marvin/irc"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type Module interface {
	Name() string
	Help() string
	Load(*irc.Client) error
	Defaults()
}

type ModuleSet struct {
	client  *irc.Client
	modules []Module
	configs string
}

func NewModuleSet(client *irc.Client, configs string) *ModuleSet {
	return &ModuleSet{client: client, configs: configs}
}

func (m *ModuleSet) Register(module Module) {
	m.modules = append(m.modules, module)
}

func (m *ModuleSet) LoadAll() error {
	if err := os.MkdirAll(m.configs, 0755); err != nil {
		return err
	}

	for _, module := range m.modules {
		fn := fmt.Sprintf("%s.json", module.Name())
		fp := filepath.Join(m.configs, fn)

		data, err := ioutil.ReadFile(fp)
		if err == nil {
			if err := json.Unmarshal(data, &module); err != nil {
				return err
			}
		} else {
			module.Defaults()
		}

		if err := module.Load(m.client); err != nil {
			return err
		}
	}

	m.client.CmdHook("privmsg", m.helpCmd)
	m.client.CmdHook("privmsg", m.moduleCmd)

	return nil
}

func (m *ModuleSet) findModule(name string) Module {
	for _, module := range m.modules {
		if module.Name() == name {
			return module
		}
	}

	return nil
}

func (m *ModuleSet) helpCmd(client *irc.Client, msg irc.Message) error {
	if msg.Data != "!help" && len(m.modules) >= 1 {
		return nil
	}

	var names []string
	for _, module := range m.modules {
		names = append(names, module.Name())
	}

	help := fmt.Sprintf("The following modules are available: %s",
		strings.Join(names, " "))

	return client.Write("NOTICE %s :%s", msg.Receiver, help)
}

func (m *ModuleSet) moduleCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Split(msg.Data, " ")
	if len(splited) < 2 || splited[0] != "!help" {
		return nil
	}

	name := strings.ToLower(splited[1])
	module := m.findModule(name)
	if module == nil {
		return client.Write("NOTICE %s :Module %q isn't installed",
			msg.Receiver, name)
	}

	return client.Write("NOTICE %s :%s module: %s",
		msg.Receiver, module.Name(), module.Help())
}
<commit_msg>Separate modules with a ','<commit_after>package modules

import (
	"encoding/json"
	"fmt"
	"github.com/nmeum/marvin/irc"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type Module interface {
	Name() string
	Help() string
	Load(*irc.Client) error
	Defaults()
}

type ModuleSet struct {
	client  *irc.Client
	modules []Module
	configs string
}

func NewModuleSet(client *irc.Client, configs string) *ModuleSet {
	return &ModuleSet{client: client, configs: configs}
}

func (m *ModuleSet) Register(module Module) {
	m.modules = append(m.modules, module)
}

func (m *ModuleSet) LoadAll() error {
	if err := os.MkdirAll(m.configs, 0755); err != nil {
		return err
	}

	for _, module := range m.modules {
		fn := fmt.Sprintf("%s.json", module.Name())
		fp := filepath.Join(m.configs, fn)

		data, err := ioutil.ReadFile(fp)
		if err == nil {
			if err := json.Unmarshal(data, &module); err != nil {
				return err
			}
		} else {
			module.Defaults()
		}

		if err := module.Load(m.client); err != nil {
			return err
		}
	}

	m.client.CmdHook("privmsg", m.helpCmd)
	m.client.CmdHook("privmsg", m.moduleCmd)

	return nil
}

func (m *ModuleSet) findModule(name string) Module {
	for _, module := range m.modules {
		if module.Name() == name {
			return module
		}
	}

	return nil
}

func (m *ModuleSet) helpCmd(client *irc.Client, msg irc.Message) error {
	if msg.Data != "!help" && len(m.modules) >= 1 {
		return nil
	}

	var names []string
	for _, module := range m.modules {
		names = append(names, module.Name())
	}

	help := fmt.Sprintf("The following modules are available: %s",
		strings.Join(names, ", "))

	return client.Write("NOTICE %s :%s", msg.Receiver, help)
}

func (m *ModuleSet) moduleCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Split(msg.Data, " ")
	if len(splited) < 2 || splited[0] != "!help" {
		return nil
	}

	name := strings.ToLower(splited[1])
	module := m.findModule(name)
	if module == nil {
		return client.Write("NOTICE %s :Module %q isn't installed",
			msg.Receiver, name)
	}

	return client.Write("NOTICE %s :%s module: %s",
		msg.Receiver, module.Name(), module.Help())
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/ghodss/yaml"
)

// To add new e2e test support, you need to:
//   1) Transform e2e performance test result into *PerfData* in k8s/kubernetes/test/e2e/perftype,
//   and print the PerfData in e2e test log.
//   2) Add corresponding bucket, job and test into *TestConfig*.

// TestDescription contains test name, output file prefix and parser function.
type TestDescription struct {
	Name             string
	OutputFilePrefix string
	Parser           func(data []byte, buildNumber int, testResult *BuildData)
}

// TestDescriptions is a map job->component->description.
type TestDescriptions map[string]map[string][]TestDescription

// Tests is a map from test label to test description.
type Tests struct {
	Prefix       string
	Descriptions TestDescriptions
	BuildsCount  int
}

// Jobs is a map from job name to all supported tests in the job.
type Jobs map[string]Tests

// Buckets is a map from bucket url to all supported jobs in the bucket.
type Buckets map[string]Jobs

var (
	// performanceDescriptions contains metrics exported by a --ginko.focus=[Feature:Performance]
	// e2e test
	performanceDescriptions = TestDescriptions{
		"E2E": {
			"DensityResources": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			}},
			"DensityPodStartup": []TestDescription{
				{
					Name:             "density",
					OutputFilePrefix: "PodStartupLatency",
					Parser:           parsePerfData,
				},
				{
					Name:             "density",
					OutputFilePrefix: "PodStartupLatency_PodStartupLatency",
					Parser:           parsePerfData,
				},
			},
			"DensitySaturationPodStartup": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "PodStartupLatency_SaturationPodStartupLatency",
				Parser:           parsePerfData,
			}},
			"LoadResources": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			}},
		},
		"APIServer": {
			"DensityResponsiveness": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parsePerfData,
			}},
			"DensityRequestCount": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			}},
			"DensityRequestCountByClient": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			}},
			"DensityInitEventsCount": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverInitEventsCount,
			}},
			"LoadResponsiveness": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parsePerfData,
			}},
			"LoadRequestCount": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			}},
			"LoadRequestCountByClient": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			}},
			"LoadInitEventsCount": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverInitEventsCount,
			}},
		},
		"Scheduler": {
			"SchedulingLatency": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "SchedulingMetrics",
				Parser:           parseSchedulingLatency,
			}},
			"SchedulingThroughput": []TestDescription{
				{
					Name:             "density",
					OutputFilePrefix: "SchedulingThroughput",
					Parser:           parseSchedulingThroughputCL,
				},
				{
					Name:             "density",
					OutputFilePrefix: "SchedulingMetrics",
					Parser:           parseSchedulingThroughput,
				},
			},
		},
		"Etcd": {
			"BackendCommitDuration": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("backendCommitDuration"),
			}},
			"SnapshotSaveTotalDuration": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("snapshotSaveTotalDuration"),
			}},
			"PeerRoundTripTime": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("peerRoundTripTime"),
			}},
			"WalFsyncDuration": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("walFsyncDuration"),
			}},
		},
		"KubeProxy": {
			"NetworkProgrammingLatency": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "NetworkProgrammingLatency",
				Parser:           parsePerfData,
			}},
		},
	}

	// benchmarkDescriptions contains metrics exported by test/integration/scheduler_perf
	benchmarkDescriptions = TestDescriptions{
		"Scheduler": {
			"BenchmarkResults": []TestDescription{{
				Name:             "benchmark",
				OutputFilePrefix: "BenchmarkResults",
				Parser:           parsePerfData,
			}},
		},
	}

	dnsBenchmarkDescriptions = TestDescriptions{
		"dns": {
			"Latency": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "Latency",
				Parser:           parsePerfData,
			}},
			"LatencyPerc": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "LatencyPerc",
				Parser:           parsePerfData,
			}},
			"Queries": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "Queries",
				Parser:           parsePerfData,
			}},
			"Qps": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "Qps",
				Parser:           parsePerfData,
			}},
		},
	}

	jobTypeToDescriptions = map[string]TestDescriptions{
		"performance":  performanceDescriptions,
		"benchmark":    benchmarkDescriptions,
		"dnsBenchmark": dnsBenchmarkDescriptions,
	}
)

func getProwConfigOrDie(configPaths []string) Jobs {
	jobs, err := getProwConfig(configPaths)
	if err != nil {
		panic(err)
	}
	return jobs
}

// Minimal subset of the prow config definition at k8s.io/test-infra/prow/config
type config struct {
	Periodics []periodic `json:"periodics"`
}
type periodic struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

func urlConfigRead(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, fmt.Errorf("error fetching prow config from %s: %v", url, err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading prow config from %s: %v", url, err)
	}
	return b, nil
}

func fileConfigRead(path string) ([]byte, error) {
	return ioutil.ReadFile(path)
}

func getProwConfig(configPaths []string) (Jobs, error) {
	jobs := Jobs{}

	for _, configPath := range configPaths {
		fmt.Fprintf(os.Stderr, "Fetching config %s\n", configPath)
		// Perfdash supports only yamls.
		if !strings.HasSuffix(configPath, ".yaml") {
			fmt.Fprintf(os.Stderr, "%s is not an yaml file!\n", configPath)
			continue
		}
		var content []byte
		var err error
		switch {
		case strings.HasPrefix(configPath, "http://"), strings.HasPrefix(configPath, "https://"):
			content, err = urlConfigRead(configPath)
		default:
			content, err = fileConfigRead(configPath)
		}
		if err != nil {
			return nil, err
		}
		conf := &config{}
		if err := yaml.Unmarshal(content, conf); err != nil {
			return nil, fmt.Errorf("error unmarshaling prow config from %s: %v", configPath, err)
		}
		for _, periodic := range conf.Periodics {
			var thisPeriodicConfig Tests
			for _, tag := range periodic.Tags {
				if strings.HasPrefix(tag, "perfDashPrefix:") {
					split := strings.SplitN(tag, ":", 2)
					thisPeriodicConfig.Prefix = strings.TrimSpace(split[1])
					continue
				}
				if strings.HasPrefix(tag, "perfDashJobType:") {
					split := strings.SplitN(tag, ":", 2)
					jobType := strings.TrimSpace(split[1])
					var exists bool
					if thisPeriodicConfig.Descriptions, exists = jobTypeToDescriptions[jobType]; !exists {
						fmt.Fprintf(os.Stderr, "warning: unknown job type - %s\n", jobType)
					}
					continue
				}
				if strings.HasPrefix(tag, "perfDashBuildsCount:") {
					split := strings.SplitN(tag, ":", 2)
					i, err := strconv.Atoi(strings.TrimSpace(split[1]))
					if err != nil {
						fmt.Fprintf(os.Stderr, "warning: unparsable builds count - %v\n", split[1])
					}
					if i < 1 {
						fmt.Fprintf(os.Stderr, "warning: non-positive builds count - %v\n", i)
						continue
					}
					thisPeriodicConfig.BuildsCount = i
					continue
				}
				if strings.HasPrefix(tag, "perfDash") {
					fmt.Fprintf(os.Stderr, "warning: unknown perfdash tag name: %q\n", tag)
				}
			}
			if thisPeriodicConfig.Prefix == "" && thisPeriodicConfig.Descriptions == nil {
				continue
			}
			if thisPeriodicConfig.Prefix == "" || thisPeriodicConfig.Descriptions == nil {
				return nil, fmt.Errorf("invalid perfdash config of periodic %q: none or both of prefix and job type must be specified", periodic.Name)
			}
			jobs[periodic.Name] = thisPeriodicConfig
		}
	}
	fmt.Printf("Read configs with %d jobs\n", len(jobs))
	return jobs, nil
}
<commit_msg>Display prometheus-based API responsiveness SLI.<commit_after>/*
Copyright 2016 The Kubernetes 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 main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/ghodss/yaml"
)

// To add new e2e test support, you need to:
//   1) Transform e2e performance test result into *PerfData* in k8s/kubernetes/test/e2e/perftype,
//   and print the PerfData in e2e test log.
//   2) Add corresponding bucket, job and test into *TestConfig*.

// TestDescription contains test name, output file prefix and parser function.
type TestDescription struct {
	Name             string
	OutputFilePrefix string
	Parser           func(data []byte, buildNumber int, testResult *BuildData)
}

// TestDescriptions is a map job->component->description.
type TestDescriptions map[string]map[string][]TestDescription

// Tests is a map from test label to test description.
type Tests struct {
	Prefix       string
	Descriptions TestDescriptions
	BuildsCount  int
}

// Jobs is a map from job name to all supported tests in the job.
type Jobs map[string]Tests

// Buckets is a map from bucket url to all supported jobs in the bucket.
type Buckets map[string]Jobs

var (
	// performanceDescriptions contains metrics exported by a --ginko.focus=[Feature:Performance]
	// e2e test
	performanceDescriptions = TestDescriptions{
		"E2E": {
			"DensityResources": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			}},
			"DensityPodStartup": []TestDescription{
				{
					Name:             "density",
					OutputFilePrefix: "PodStartupLatency",
					Parser:           parsePerfData,
				},
				{
					Name:             "density",
					OutputFilePrefix: "PodStartupLatency_PodStartupLatency",
					Parser:           parsePerfData,
				},
			},
			"DensitySaturationPodStartup": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "PodStartupLatency_SaturationPodStartupLatency",
				Parser:           parsePerfData,
			}},
			"LoadResources": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			}},
		},
		"APIServer": {
			"DensityResponsiveness": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parsePerfData,
			}},
			"DensityRequestCount": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			}},
			"DensityResponsiveness_Prometheus": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "APIResponsivenessPrometheus",
				Parser:           parsePerfData,
			}},
			"DensityRequestCount_Prometheus": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "APIResponsivenessPrometheus",
				Parser:           parseRequestCountData,
			}},
			"DensityRequestCountByClient": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			}},
			"DensityInitEventsCount": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverInitEventsCount,
			}},
			"LoadResponsiveness": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parsePerfData,
			}},
			"LoadRequestCount": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			}},
			"LoadResponsiveness_Prometheus": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "APIResponsivenessPrometheus",
				Parser:           parsePerfData,
			}},
			"LoadRequestCount_Prometheus": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "APIResponsivenessPrometheus",
				Parser:           parseRequestCountData,
			}},
			"LoadRequestCountByClient": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			}},
			"LoadInitEventsCount": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverInitEventsCount,
			}},
		},
		"Scheduler": {
			"SchedulingLatency": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "SchedulingMetrics",
				Parser:           parseSchedulingLatency,
			}},
			"SchedulingThroughput": []TestDescription{
				{
					Name:             "density",
					OutputFilePrefix: "SchedulingThroughput",
					Parser:           parseSchedulingThroughputCL,
				},
				{
					Name:             "density",
					OutputFilePrefix: "SchedulingMetrics",
					Parser:           parseSchedulingThroughput,
				},
			},
		},
		"Etcd": {
			"BackendCommitDuration": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("backendCommitDuration"),
			}},
			"SnapshotSaveTotalDuration": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("snapshotSaveTotalDuration"),
			}},
			"PeerRoundTripTime": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("peerRoundTripTime"),
			}},
			"WalFsyncDuration": []TestDescription{{
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("walFsyncDuration"),
			}},
		},
		"KubeProxy": {
			"NetworkProgrammingLatency": []TestDescription{{
				Name:             "load",
				OutputFilePrefix: "NetworkProgrammingLatency",
				Parser:           parsePerfData,
			}},
		},
	}

	// benchmarkDescriptions contains metrics exported by test/integration/scheduler_perf
	benchmarkDescriptions = TestDescriptions{
		"Scheduler": {
			"BenchmarkResults": []TestDescription{{
				Name:             "benchmark",
				OutputFilePrefix: "BenchmarkResults",
				Parser:           parsePerfData,
			}},
		},
	}

	dnsBenchmarkDescriptions = TestDescriptions{
		"dns": {
			"Latency": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "Latency",
				Parser:           parsePerfData,
			}},
			"LatencyPerc": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "LatencyPerc",
				Parser:           parsePerfData,
			}},
			"Queries": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "Queries",
				Parser:           parsePerfData,
			}},
			"Qps": []TestDescription{{
				Name:             "dns",
				OutputFilePrefix: "Qps",
				Parser:           parsePerfData,
			}},
		},
	}

	jobTypeToDescriptions = map[string]TestDescriptions{
		"performance":  performanceDescriptions,
		"benchmark":    benchmarkDescriptions,
		"dnsBenchmark": dnsBenchmarkDescriptions,
	}
)

func getProwConfigOrDie(configPaths []string) Jobs {
	jobs, err := getProwConfig(configPaths)
	if err != nil {
		panic(err)
	}
	return jobs
}

// Minimal subset of the prow config definition at k8s.io/test-infra/prow/config
type config struct {
	Periodics []periodic `json:"periodics"`
}
type periodic struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

func urlConfigRead(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, fmt.Errorf("error fetching prow config from %s: %v", url, err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading prow config from %s: %v", url, err)
	}
	return b, nil
}

func fileConfigRead(path string) ([]byte, error) {
	return ioutil.ReadFile(path)
}

func getProwConfig(configPaths []string) (Jobs, error) {
	jobs := Jobs{}

	for _, configPath := range configPaths {
		fmt.Fprintf(os.Stderr, "Fetching config %s\n", configPath)
		// Perfdash supports only yamls.
		if !strings.HasSuffix(configPath, ".yaml") {
			fmt.Fprintf(os.Stderr, "%s is not an yaml file!\n", configPath)
			continue
		}
		var content []byte
		var err error
		switch {
		case strings.HasPrefix(configPath, "http://"), strings.HasPrefix(configPath, "https://"):
			content, err = urlConfigRead(configPath)
		default:
			content, err = fileConfigRead(configPath)
		}
		if err != nil {
			return nil, err
		}
		conf := &config{}
		if err := yaml.Unmarshal(content, conf); err != nil {
			return nil, fmt.Errorf("error unmarshaling prow config from %s: %v", configPath, err)
		}
		for _, periodic := range conf.Periodics {
			var thisPeriodicConfig Tests
			for _, tag := range periodic.Tags {
				if strings.HasPrefix(tag, "perfDashPrefix:") {
					split := strings.SplitN(tag, ":", 2)
					thisPeriodicConfig.Prefix = strings.TrimSpace(split[1])
					continue
				}
				if strings.HasPrefix(tag, "perfDashJobType:") {
					split := strings.SplitN(tag, ":", 2)
					jobType := strings.TrimSpace(split[1])
					var exists bool
					if thisPeriodicConfig.Descriptions, exists = jobTypeToDescriptions[jobType]; !exists {
						fmt.Fprintf(os.Stderr, "warning: unknown job type - %s\n", jobType)
					}
					continue
				}
				if strings.HasPrefix(tag, "perfDashBuildsCount:") {
					split := strings.SplitN(tag, ":", 2)
					i, err := strconv.Atoi(strings.TrimSpace(split[1]))
					if err != nil {
						fmt.Fprintf(os.Stderr, "warning: unparsable builds count - %v\n", split[1])
					}
					if i < 1 {
						fmt.Fprintf(os.Stderr, "warning: non-positive builds count - %v\n", i)
						continue
					}
					thisPeriodicConfig.BuildsCount = i
					continue
				}
				if strings.HasPrefix(tag, "perfDash") {
					fmt.Fprintf(os.Stderr, "warning: unknown perfdash tag name: %q\n", tag)
				}
			}
			if thisPeriodicConfig.Prefix == "" && thisPeriodicConfig.Descriptions == nil {
				continue
			}
			if thisPeriodicConfig.Prefix == "" || thisPeriodicConfig.Descriptions == nil {
				return nil, fmt.Errorf("invalid perfdash config of periodic %q: none or both of prefix and job type must be specified", periodic.Name)
			}
			jobs[periodic.Name] = thisPeriodicConfig
		}
	}
	fmt.Printf("Read configs with %d jobs\n", len(jobs))
	return jobs, nil
}
<|endoftext|>


<commit_before>// Copyright 2020 The LUCI 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 configcron

import (
	"context"
	"time"

	"google.golang.org/protobuf/proto"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/data/stringset"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/sync/parallel"
	"go.chromium.org/luci/server/tq"

	"go.chromium.org/luci/cv/internal/common"
	"go.chromium.org/luci/cv/internal/config"
	"go.chromium.org/luci/cv/internal/prjmanager"
)

// SubmitRefreshTasks submits tasks that update config for LUCI projects
// or disable projects that do not have CV config in LUCI Config.
//
// It's expected to be called by a cron.
func SubmitRefreshTasks(ctx context.Context) error {
	projects, err := config.ProjectsWithConfig(ctx)
	if err != nil {
		return err
	}
	if common.IsDev(ctx) {
		// Consider only some projects, regardless of which projects are registered.
		// TODO(crbug/1158505): switch to -dev configs.
		projects = []string{"infra", "cq-test"}
	}
	tasks := make([]*tq.Task, len(projects))
	for i, p := range projects {
		tasks[i] = &tq.Task{
			Title: "update/" + p,
			Payload: &RefreshProjectConfigTask{
				Project: p,
			},
		}
	}

	curEnabledProjects, err := config.GetAllProjectIDs(ctx, true)
	if err != nil {
		return err
	}
	projectsInLUCIConfig := stringset.NewFromSlice(projects...)
	for _, p := range curEnabledProjects {
		if !projectsInLUCIConfig.Has(p) {
			tasks = append(tasks, &tq.Task{
				Title: "disable/" + p,
				Payload: &RefreshProjectConfigTask{
					Project: p,
					Disable: true,
				},
			})
		}
	}

	err = parallel.WorkPool(32, func(workCh chan<- func() error) {
		for _, task := range tasks {
			task := task
			workCh <- func() (err error) {
				if err = tq.AddTask(ctx, task); err != nil {
					logging.Errorf(ctx, "Failed to submit task for %q: %s", task.Title, err)
				}
				return
			}
		}
	})

	if err != nil {
		return err.(errors.MultiError).First()
	}
	return nil
}

func init() {
	tq.RegisterTaskClass(tq.TaskClass{
		ID:        "refresh-project-config",
		Prototype: &RefreshProjectConfigTask{},
		Queue:     "refresh-project-config",
		Quiet:     true,
		Handler: func(ctx context.Context, payload proto.Message) error {
			task := payload.(*RefreshProjectConfigTask)
			if err := refreshProject(ctx, task.GetProject(), task.GetDisable()); err != nil {
				// Never retry tasks because the refresh task is submitted every minute
				// by the AppEngine Cron.
				err = tq.Fatal.Apply(err)
				return common.TQIfy{}.Error(ctx, err)
			}
			return nil
		},
	})
}

func refreshProject(ctx context.Context, project string, disable bool) error {
	action, actionFn := "update", config.UpdateProject
	if disable {
		action, actionFn = "disable", config.DisableProject
	}
	err := actionFn(ctx, project, func(ctx context.Context) error {
		return prjmanager.UpdateConfig(ctx, project)
	})
	if err != nil {
		return errors.Annotate(err, "failed to %s project %q", action, project).Err()
	}
	if !disable {
		return maybePokePM(ctx, project)
	}
	return nil
}

const pokePMInterval = 10 * time.Minute

func maybePokePM(ctx context.Context, project string) error {
	now := clock.Now(ctx).UTC()
	offset := common.ProjectOffset("cron-poke", pokePMInterval, project)
	nextPokeETA := now.Truncate(pokePMInterval).Add(offset)
	if nextPokeETA.Before(now) {
		nextPokeETA = nextPokeETA.Add(pokePMInterval)
	}

	// Cron runs every minute on average and triggers RefreshProjectConfigTask,
	// which may be delayed, so send iff it's less than 1.5 minutes before next
	// poke. This will sometimes result in 2 pokes sent instead of 1, but pokes
	// are less likely to not be sent at all.
	if nextPokeETA.Sub(now) < 90*time.Second {
		return prjmanager.Poke(ctx, project)
	}
	return nil
}
<commit_msg>Revert "[cv][dev] reduce potential blast radius before the CL purging is deployed."<commit_after>// Copyright 2020 The LUCI 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 configcron

import (
	"context"
	"time"

	"google.golang.org/protobuf/proto"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/data/stringset"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/sync/parallel"
	"go.chromium.org/luci/server/tq"

	"go.chromium.org/luci/cv/internal/common"
	"go.chromium.org/luci/cv/internal/config"
	"go.chromium.org/luci/cv/internal/prjmanager"
)

// SubmitRefreshTasks submits tasks that update config for LUCI projects
// or disable projects that do not have CV config in LUCI Config.
//
// It's expected to be called by a cron.
func SubmitRefreshTasks(ctx context.Context) error {
	projects, err := config.ProjectsWithConfig(ctx)
	if err != nil {
		return err
	}
	if common.IsDev(ctx) {
		// Consider only some projects, regardless of which projects are registered.
		// TODO(crbug/1158505): switch to -dev configs.
		projects = []string{"infra", "chromium", "chromium-m86", "v8"}
	}
	tasks := make([]*tq.Task, len(projects))
	for i, p := range projects {
		tasks[i] = &tq.Task{
			Title: "update/" + p,
			Payload: &RefreshProjectConfigTask{
				Project: p,
			},
		}
	}

	curEnabledProjects, err := config.GetAllProjectIDs(ctx, true)
	if err != nil {
		return err
	}
	projectsInLUCIConfig := stringset.NewFromSlice(projects...)
	for _, p := range curEnabledProjects {
		if !projectsInLUCIConfig.Has(p) {
			tasks = append(tasks, &tq.Task{
				Title: "disable/" + p,
				Payload: &RefreshProjectConfigTask{
					Project: p,
					Disable: true,
				},
			})
		}
	}

	err = parallel.WorkPool(32, func(workCh chan<- func() error) {
		for _, task := range tasks {
			task := task
			workCh <- func() (err error) {
				if err = tq.AddTask(ctx, task); err != nil {
					logging.Errorf(ctx, "Failed to submit task for %q: %s", task.Title, err)
				}
				return
			}
		}
	})

	if err != nil {
		return err.(errors.MultiError).First()
	}
	return nil
}

func init() {
	tq.RegisterTaskClass(tq.TaskClass{
		ID:        "refresh-project-config",
		Prototype: &RefreshProjectConfigTask{},
		Queue:     "refresh-project-config",
		Quiet:     true,
		Handler: func(ctx context.Context, payload proto.Message) error {
			task := payload.(*RefreshProjectConfigTask)
			if err := refreshProject(ctx, task.GetProject(), task.GetDisable()); err != nil {
				// Never retry tasks because the refresh task is submitted every minute
				// by the AppEngine Cron.
				err = tq.Fatal.Apply(err)
				return common.TQIfy{}.Error(ctx, err)
			}
			return nil
		},
	})
}

func refreshProject(ctx context.Context, project string, disable bool) error {
	action, actionFn := "update", config.UpdateProject
	if disable {
		action, actionFn = "disable", config.DisableProject
	}
	err := actionFn(ctx, project, func(ctx context.Context) error {
		return prjmanager.UpdateConfig(ctx, project)
	})
	if err != nil {
		return errors.Annotate(err, "failed to %s project %q", action, project).Err()
	}
	if !disable {
		return maybePokePM(ctx, project)
	}
	return nil
}

const pokePMInterval = 10 * time.Minute

func maybePokePM(ctx context.Context, project string) error {
	now := clock.Now(ctx).UTC()
	offset := common.ProjectOffset("cron-poke", pokePMInterval, project)
	nextPokeETA := now.Truncate(pokePMInterval).Add(offset)
	if nextPokeETA.Before(now) {
		nextPokeETA = nextPokeETA.Add(pokePMInterval)
	}

	// Cron runs every minute on average and triggers RefreshProjectConfigTask,
	// which may be delayed, so send iff it's less than 1.5 minutes before next
	// poke. This will sometimes result in 2 pokes sent instead of 1, but pokes
	// are less likely to not be sent at all.
	if nextPokeETA.Sub(now) < 90*time.Second {
		return prjmanager.Poke(ctx, project)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Kubicorn 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 kubeconfig

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/kris-nova/kubicorn/apis/cluster"
)

func TestMain(m *testing.M) {
	m.Run()
}

func TestSdkHappy(t *testing.T) {
	tmpdir, err := ioutil.TempDir("", "test")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	localDir := fmt.Sprintf("%s/.kube", tmpdir)
	localPath, err := getKubeConfigPath(localDir)
	if err != nil {
		t.Fatal(err)
	}

	if _, err := os.Stat(localDir); os.IsNotExist(err) {
		t.Fatal("error creating kubectl directory")
	}

	expectedLocalPath := fmt.Sprintf("%s/.kube/config", tmpdir)
	if localPath != expectedLocalPath {
		t.Fatalf("kubectl config path incorrect, got: %s, expected: %s", localPath, expectedLocalPath)
	}
}

func TestGetConfigHappy(t *testing.T) {
	dir, err := os.Getwd()
	dir, err = filepath.Abs(dir + "/../../test")
	testCluster := &cluster.Cluster{
		SSH: &cluster.SSH{
			User:          "root",
			PublicKeyPath: dir + "/credentials/id_rsa.pub",
			Port:          "6666",
		},
		KubernetesAPI: &cluster.KubernetesAPI{
			Endpoint: "localhost",
		},
	}
	os.Setenv("KUBICORN_TEST_HOME_DIRECTORY", dir+"/tmp")

	err = GetConfig(testCluster)

	result, err := ioutil.ReadFile(dir + "/tmp/.kube/config")
	if err != nil {
		t.Fatal(err)
	}

	if strings.TrimSpace(string(result)) != "kubicorn test data" {
		os.RemoveAll(dir + "/tmp/.kube")
		t.Fatalf("File content is incorrect \"%v\"", strings.TrimSpace(string(result)))
	}

	os.RemoveAll(dir + "/tmp/.kube")
}
<commit_msg>kubeconfig: fix tests<commit_after>// Copyright © 2017 The Kubicorn 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 kubeconfig

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/kris-nova/kubicorn/apis/cluster"
	"github.com/kris-nova/kubicorn/cutil/agent"
)

func TestMain(m *testing.M) {
	m.Run()
}

func TestSdkHappy(t *testing.T) {
	tmpdir, err := ioutil.TempDir("", "test")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	localDir := fmt.Sprintf("%s/.kube", tmpdir)
	localPath, err := getKubeConfigPath(localDir)
	if err != nil {
		t.Fatal(err)
	}

	if _, err := os.Stat(localDir); os.IsNotExist(err) {
		t.Fatal("error creating kubectl directory")
	}

	expectedLocalPath := fmt.Sprintf("%s/.kube/config", tmpdir)
	if localPath != expectedLocalPath {
		t.Fatalf("kubectl config path incorrect, got: %s, expected: %s", localPath, expectedLocalPath)
	}
}

func TestGetConfigHappy(t *testing.T) {
	dir, err := os.Getwd()
	dir, err = filepath.Abs(dir + "/../../test")
	testCluster := &cluster.Cluster{
		SSH: &cluster.SSH{
			User:          "root",
			PublicKeyPath: dir + "/credentials/id_rsa.pub",
			Port:          "6666",
		},
		KubernetesAPI: &cluster.KubernetesAPI{
			Endpoint: "localhost",
		},
	}
	os.Setenv("KUBICORN_TEST_HOME_DIRECTORY", dir+"/tmp")

	err = GetConfig(testCluster, agent.NewAgent())

	result, err := ioutil.ReadFile(dir + "/tmp/.kube/config")
	if err != nil {
		t.Fatal(err)
	}

	if strings.TrimSpace(string(result)) != "kubicorn test data" {
		os.RemoveAll(dir + "/tmp/.kube")
		t.Fatalf("File content is incorrect \"%v\"", strings.TrimSpace(string(result)))
	}

	os.RemoveAll(dir + "/tmp/.kube")
}
<|endoftext|>


<commit_before>package gdrj

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

type LedgerSummary struct {
	orm.ModelBase                          `bson:"-" json:"-"`
	ID                                     string `bson:"_id"`
	PC                                     *ProfitCenter
	CC                                     *CostCenter
	PLModel                                *PLModel
	CompanyCode                            string
	LedgerAccount                          string
	Customer                               *Customer
	Product                                *Product
	Date                                   *Date
	PLGroup1, PLGroup2, PLGroup3, PLGroup4 string
	Value1, Value2, Value3                 float64
	//EasyForSelect
	PCID, CCID, OutletID, SKUID, PLCode, PLOrder string
	Month                                        time.Month
	Year                                         int
}

// month,year
func (s *LedgerSummary) RecordID() interface{} {
	return s.ID
	//return toolkit.Sprintf("%d_%d_%s_%s", s.Date.Year, s.Date.Month, s.CompanyCode, s.LedgerAccount)
}

func (s *LedgerSummary) PrepareID() interface{} {
	return toolkit.Sprintf("%d_%d_%s_%s_%s_%s_%s_%s_%s",
		s.Date.Year, s.Date.Month,
		s.CompanyCode, s.LedgerAccount,
		s.PLCode, s.OutletID, s.SKUID, s.PCID, s.CCID)
}

func (s *LedgerSummary) TableName() string {
	return "ledgersummaries"
}

func (s *LedgerSummary) PreSave() error {
	s.ID = s.PrepareID().(string)
	return nil
}

func GetLedgerSummaryByDetail(LedgerAccount, PCID, CCID, OutletID, SKUID string, Year int, Month time.Month) (ls *LedgerSummary) {
	ls = new(LedgerSummary)

	filter := dbox.And(dbox.Eq("month", Month),
		dbox.Eq("year", Year),
		dbox.Eq("ledgeraccount", LedgerAccount),
		dbox.Eq("pcid", PCID),
		dbox.Eq("ccid", CCID),
		dbox.Eq("outletid", OutletID),
		dbox.Eq("skuid", SKUID))

	cr, err := Find(new(LedgerSummary), filter, nil)
	if err != nil {
		return
	}

	_ = cr.Fetch(&ls, 1, false)
	cr.Close()

	return
}

func CalculateLedgerSummaryAnalysisIdea(payload *PivotParam) ([]*toolkit.M, error) {
	var filter *dbox.Filter = payload.ParseFilter()

	sum := new(LedgerSummary)
	conn := DB().Connection
	q := conn.NewQuery().From(sum.TableName())
	q = q.Where(filter)
	q = q.Group("plmodel._id", "plmodel.plheader1", "plmodel.plheader2", "plmodel.plheader3")
	q = q.Aggr(dbox.AggrSum, "$value1", "value1")

	c, e := q.Cursor(nil)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Preparing cursor error " + e.Error())
	}
	defer c.Close()

	ms := []*toolkit.M{}
	e = c.Fetch(&ms, 0, false)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
	}

	res := []*toolkit.M{}
	for _, each := range ms {
		o := toolkit.M{}
		o.Set("_id", each.Get("_id").(toolkit.M).Get("plmodel._id"))
		o.Set("plheader1", each.Get("_id").(toolkit.M).Get("plmodel.plheader1"))
		o.Set("plheader2", each.Get("_id").(toolkit.M).Get("plmodel.plheader2"))
		o.Set("plheader3", each.Get("_id").(toolkit.M).Get("plmodel.plheader3"))
		o.Set("value", each.Get("value1"))
		res = append(res, &o)
	}

	return res, nil
}

func CalculateLedgerSummary(payload *PivotParam) ([]*toolkit.M, error) {
	var filter *dbox.Filter = payload.ParseFilter()
	var columns []string = payload.ParseDimensions()
	var datapoints []string = payload.ParseDataPoints()
	var fnTransform (func(m *toolkit.M) error) = nil

	fmt.Printf("--- %#v\n", filter)
	fmt.Printf("--- %#v\n", columns)
	fmt.Printf("--- %#v\n", datapoints)

	fmt.Printf("+++++ %#v\n", *(filter.Value.([]*dbox.Filter)[0]))
	fmt.Printf("+++++ %#v\n", *(filter.Value.([]*dbox.Filter)[1]))

	var plFilter1, plFilter2, plFilter3 *dbox.Filter

	if payload.Which == "gross_sales_discount_and_net_sales" {
		plFilter1 = dbox.Eq("plcode", "PL1")
		plFilter2 = dbox.Eq("plcode", "PL2")
		plFilter3 = dbox.Eq("plcode", "PL3")
	}

	bunchData1, err := SummarizeLedgerSum(
		dbox.And(plFilter1, filter), columns, datapoints, fnTransform)
	if err != nil {
		return nil, err
	}

	bunchData2, err := SummarizeLedgerSum(
		dbox.And(plFilter2, filter), columns, datapoints, fnTransform)
	if err != nil {
		return nil, err
	}

	bunchData3, err := SummarizeLedgerSum(
		dbox.And(plFilter3, filter), columns, datapoints, fnTransform)
	if err != nil {
		return nil, err
	}

	allKeys := map[string]*toolkit.M{}
	rows := []*toolkit.M{}

	for _, each := range bunchData1 {
		keyword := ""
		for _, s := range columns {
			keyword = fmt.Sprintf("%s%s", keyword, each.Get("_id").(toolkit.M).GetString(s))
		}

		if _, ok := allKeys[keyword]; !ok {
			allKeys[keyword] = each
			rows = append(rows, each)

			for key, val := range *each {
				if key == "_id" {
					for skey, sval := range val.(toolkit.M) {
						each.Set(strings.Replace(skey, ".", "_", -1), sval)
					}
				}
			}
			each.Unset("_id")
		} else {
			current := allKeys[keyword]
			current.Set("value1", current.GetFloat64("value1")+each.GetFloat64("value1"))
		}
	}

	for _, each := range bunchData2 {
		keyword := ""
		for _, s := range columns {
			keyword = fmt.Sprintf("%s%s", keyword, each.Get("_id").(toolkit.M).GetString(s))
		}

		if _, ok := allKeys[keyword]; !ok {
			allKeys[keyword] = each
			rows = append(rows, each)
			each.Set("value2", each.GetFloat64("value1"))
			each.Set("value1", 0)

			for key, val := range *each {
				if key == "_id" {
					for skey, sval := range val.(toolkit.M) {
						each.Set(strings.Replace(skey, ".", "_", -1), sval)
					}
				}
			}
			each.Unset("_id")
		} else {
			current := allKeys[keyword]
			current.Set("value2", current.GetFloat64("value2")+each.GetFloat64("value1"))
		}
	}

	for _, each := range bunchData3 {
		keyword := ""
		for _, s := range columns {
			keyword = fmt.Sprintf("%s%s", keyword, each.Get("_id").(toolkit.M).GetString(s))
		}

		if _, ok := allKeys[keyword]; !ok {
			allKeys[keyword] = each
			rows = append(rows, each)
			each.Set("value3", each.GetFloat64("value1"))
			each.Set("value1", 0)

			for key, val := range *each {
				if key == "_id" {
					for skey, sval := range val.(toolkit.M) {
						each.Set(strings.Replace(skey, ".", "_", -1), sval)
					}
				}
			}
			each.Unset("_id")
		} else {
			current := allKeys[keyword]
			current.Set("value3", current.GetFloat64("value3")+each.GetFloat64("value1"))
		}
	}

	return rows, nil
}

func SummarizeLedgerSum(
	filter *dbox.Filter,
	columns []string,
	datapoints []string,
	fnTransform func(m *toolkit.M) error) ([]*toolkit.M, error) {
	sum := new(LedgerSummary)
	conn := DB().Connection
	q := conn.NewQuery().From(sum.TableName())
	if filter != nil {
		q = q.Where(filter)
	}

	if len(columns) > 0 {
		cs := []string{}
		for i := range columns {
			cs = append(cs, strings.ToLower(columns[i]))
		}
		q = q.Group(cs...)
	}
	if len(datapoints) == 0 {
		return nil, errors.New("SummarizedLedgerSum: Datapoints should be defined at least 1")
	}
	for _, dp := range datapoints {
		dps := strings.Split(strings.ToLower(dp), ":")
		if len(dps) < 2 {
			return nil, errors.New("SummarizeLedgerSum: Parameters should follow this pattern aggrOp:fieldName:[alias - optional]")
		}

		fieldid := dps[1]
		alias := fieldid
		op := ""
		if !strings.HasPrefix(dps[0], "$") {
			dps[0] = "$" + strings.ToLower(dps[0])
		}

		if toolkit.HasMember([]string{dbox.AggrSum, dbox.AggrAvr, dbox.AggrMax,
			dbox.AggrMin, dbox.AggrMean, dbox.AggrMed}, dps[0]) {
			op = dps[0]
		}
		if op == "" {
			return nil, errors.New("SummarizeLedgerSum: Invalid Operation")
		}
		if len(dps) > 2 {
			alias = dps[2]
		}

		if strings.HasPrefix(alias, "$") {
			alias = alias[1:]
		}

		if fnumber, enumber := toolkit.IsStringNumber(fieldid, "."); enumber == nil {
			q = q.Aggr(op, fnumber, alias)
		} else {
			q = q.Aggr(op, fieldid, alias)
		}
	}

	c, e := q.Cursor(nil)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Preparing cursor error " + e.Error())
	}
	defer c.Close()

	ms := []*toolkit.M{}
	e = c.Fetch(&ms, 0, false)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
	}

	if fnTransform != nil {
		for idx, m := range ms {
			e = fnTransform(m)
			if e != nil {
				return nil, errors.New(toolkit.Sprintf("SummarizedLedgerSum: Transform error on index %d, %s",
					idx, e.Error()))
			}
		}
	}

	return ms, nil
}

func (s *LedgerSummary) Save() error {
	e := Save(s)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", s.TableName(), "save", e.Error()))
	}
	return e
}

type PivotParam struct {
	Dimensions []*PivotParamDimensions `json:"dimensions"`
	DataPoints []*PivotParamDataPoint  `json:"datapoints"`
	Which      string                  `json:"which"`
	Filters    []toolkit.M             `json:"filters"`
	Type       string                  `json:"type"`
}

type PivotParamDimensions struct {
	Field string `json:"field"`
}

type PivotParamDataPoint struct {
	Aggr  string `json:"aggr"`
	Field string `json:"field"`
}

func (p *PivotParam) ParseDimensions() (res []string) {
	res = []string{}
	for _, each := range p.Dimensions {
		res = append(res, each.Field)
	}
	return
}

func (p *PivotParam) ParseDataPoints() (res []string) {
	for _, each := range p.DataPoints {
		parts := []string{each.Aggr, each.Field, each.Field}

		if !strings.HasPrefix(parts[1], "$") {
			parts[1] = fmt.Sprintf("$%s", parts[1])
		}

		res = append(res, strings.Join(parts, ":"))
	}
	return
}

func (p *PivotParam) ParseFilter() *dbox.Filter {
	filters := []*dbox.Filter{}

	for _, each := range p.Filters {
		field := each.GetString("Field")

		switch each.GetString("Op") {
		case dbox.FilterOpIn:
			values := []string{}
			for _, v := range each.Get("Value").([]interface{}) {
				values = append(values, v.(string))
			}

			if len(values) > 0 {
				filters = append(filters, dbox.In(field, values))
			}
		case dbox.FilterOpGte:
			var value interface{} = each.GetString("Value")

			if value.(string) != "" {
				if field == "year" {
					t, err := time.Parse(time.RFC3339Nano, value.(string))
					if err != nil {
						fmt.Println(err.Error())
					} else {
						value = t.Year()
					}
				}

				filters = append(filters, dbox.Gte(field, value))
			}
		case dbox.FilterOpLte:
			var value interface{} = each.GetString("Value")

			if value.(string) != "" {
				if field == "year" {
					t, err := time.Parse(time.RFC3339Nano, value.(string))
					if err != nil {
						fmt.Println(err.Error())
					} else {
						value = t.Year()
					}
				}

				filters = append(filters, dbox.Lte(field, value))
			}
		case dbox.FilterOpEqual:
			value := each.GetString("Value")

			filters = append(filters, dbox.Gte(field, value))
		}
	}

	for _, each := range filters {
		fmt.Printf(">>>> %#v\n", *each)
	}

	return dbox.And(filters...)
}
<commit_msg>no message<commit_after>package gdrj

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

var (
	summaryTableName = "ledgersummariestemp"
)

type LedgerSummary struct {
	orm.ModelBase                          `bson:"-" json:"-"`
	ID                                     string `bson:"_id"`
	PC                                     *ProfitCenter
	CC                                     *CostCenter
	PLModel                                *PLModel
	CompanyCode                            string
	LedgerAccount                          string
	Customer                               *Customer
	Product                                *Product
	Date                                   *Date
	PLGroup1, PLGroup2, PLGroup3, PLGroup4 string
	Value1, Value2, Value3                 float64
	//EasyForSelect
	PCID, CCID, OutletID, SKUID, PLCode, PLOrder string
	Month                                        time.Month
	Year                                         int
}

// month,year
func (s *LedgerSummary) RecordID() interface{} {
	return s.ID
	//return toolkit.Sprintf("%d_%d_%s_%s", s.Date.Year, s.Date.Month, s.CompanyCode, s.LedgerAccount)
}

func (s *LedgerSummary) PrepareID() interface{} {
	return toolkit.Sprintf("%d_%d_%s_%s_%s_%s_%s_%s_%s",
		s.Date.Year, s.Date.Month,
		s.CompanyCode, s.LedgerAccount,
		s.PLCode, s.OutletID, s.SKUID, s.PCID, s.CCID)
}

func (s *LedgerSummary) TableName() string {
	return "ledgersummaries"
}

func (s *LedgerSummary) PreSave() error {
	s.ID = s.PrepareID().(string)
	return nil
}

func GetLedgerSummaryByDetail(LedgerAccount, PCID, CCID, OutletID, SKUID string, Year int, Month time.Month) (ls *LedgerSummary) {
	ls = new(LedgerSummary)

	filter := dbox.And(dbox.Eq("month", Month),
		dbox.Eq("year", Year),
		dbox.Eq("ledgeraccount", LedgerAccount),
		dbox.Eq("pcid", PCID),
		dbox.Eq("ccid", CCID),
		dbox.Eq("outletid", OutletID),
		dbox.Eq("skuid", SKUID))

	cr, err := Find(new(LedgerSummary), filter, nil)
	if err != nil {
		return
	}

	_ = cr.Fetch(&ls, 1, false)
	cr.Close()

	return
}

func CalculateLedgerSummaryAnalysisIdea(payload *PivotParam) ([]*toolkit.M, error) {
	var filter *dbox.Filter = payload.ParseFilter()

	conn := DB().Connection
	q := conn.NewQuery().From(summaryTableName)
	q = q.Where(filter)
	q = q.Group("plmodel._id", "plmodel.plheader1", "plmodel.plheader2", "plmodel.plheader3")
	q = q.Aggr(dbox.AggrSum, "$value1", "value1")

	c, e := q.Cursor(nil)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Preparing cursor error " + e.Error())
	}
	defer c.Close()

	ms := []*toolkit.M{}
	e = c.Fetch(&ms, 0, false)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
	}

	res := []*toolkit.M{}
	for _, each := range ms {
		o := toolkit.M{}
		o.Set("_id", each.Get("_id").(toolkit.M).Get("plmodel._id"))
		o.Set("plheader1", each.Get("_id").(toolkit.M).Get("plmodel.plheader1"))
		o.Set("plheader2", each.Get("_id").(toolkit.M).Get("plmodel.plheader2"))
		o.Set("plheader3", each.Get("_id").(toolkit.M).Get("plmodel.plheader3"))
		o.Set("value", each.Get("value1"))
		res = append(res, &o)
	}

	return res, nil
}

func CalculateLedgerSummary(payload *PivotParam) ([]*toolkit.M, error) {
	var filter *dbox.Filter = payload.ParseFilter()
	var columns []string = payload.ParseDimensions()
	var datapoints []string = payload.ParseDataPoints()
	var fnTransform (func(m *toolkit.M) error) = nil

	fmt.Printf("--- %#v\n", filter)
	fmt.Printf("--- %#v\n", columns)
	fmt.Printf("--- %#v\n", datapoints)

	fmt.Printf("+++++ %#v\n", *(filter.Value.([]*dbox.Filter)[0]))
	fmt.Printf("+++++ %#v\n", *(filter.Value.([]*dbox.Filter)[1]))

	var plFilter1, plFilter2, plFilter3 *dbox.Filter

	if payload.Which == "gross_sales_discount_and_net_sales" {
		plFilter1 = dbox.Eq("plcode", "PL1")
		plFilter2 = dbox.Eq("plcode", "PL2")
		plFilter3 = dbox.Eq("plcode", "PL3")
	}

	bunchData1, err := SummarizeLedgerSum(
		dbox.And(plFilter1, filter), columns, datapoints, fnTransform)
	if err != nil {
		return nil, err
	}

	bunchData2, err := SummarizeLedgerSum(
		dbox.And(plFilter2, filter), columns, datapoints, fnTransform)
	if err != nil {
		return nil, err
	}

	bunchData3, err := SummarizeLedgerSum(
		dbox.And(plFilter3, filter), columns, datapoints, fnTransform)
	if err != nil {
		return nil, err
	}

	allKeys := map[string]*toolkit.M{}
	rows := []*toolkit.M{}

	for _, each := range bunchData1 {
		keyword := ""
		for _, s := range columns {
			keyword = fmt.Sprintf("%s%s", keyword, each.Get("_id").(toolkit.M).GetString(s))
		}

		if _, ok := allKeys[keyword]; !ok {
			allKeys[keyword] = each
			rows = append(rows, each)

			for key, val := range *each {
				if key == "_id" {
					for skey, sval := range val.(toolkit.M) {
						each.Set(strings.Replace(skey, ".", "_", -1), sval)
					}
				}
			}
			each.Unset("_id")
		} else {
			current := allKeys[keyword]
			current.Set("value1", current.GetFloat64("value1")+each.GetFloat64("value1"))
		}
	}

	for _, each := range bunchData2 {
		keyword := ""
		for _, s := range columns {
			keyword = fmt.Sprintf("%s%s", keyword, each.Get("_id").(toolkit.M).GetString(s))
		}

		if _, ok := allKeys[keyword]; !ok {
			allKeys[keyword] = each
			rows = append(rows, each)
			each.Set("value2", each.GetFloat64("value1"))
			each.Set("value1", 0)

			for key, val := range *each {
				if key == "_id" {
					for skey, sval := range val.(toolkit.M) {
						each.Set(strings.Replace(skey, ".", "_", -1), sval)
					}
				}
			}
			each.Unset("_id")
		} else {
			current := allKeys[keyword]
			current.Set("value2", current.GetFloat64("value2")+each.GetFloat64("value1"))
		}
	}

	for _, each := range bunchData3 {
		keyword := ""
		for _, s := range columns {
			keyword = fmt.Sprintf("%s%s", keyword, each.Get("_id").(toolkit.M).GetString(s))
		}

		if _, ok := allKeys[keyword]; !ok {
			allKeys[keyword] = each
			rows = append(rows, each)
			each.Set("value3", each.GetFloat64("value1"))
			each.Set("value1", 0)

			for key, val := range *each {
				if key == "_id" {
					for skey, sval := range val.(toolkit.M) {
						each.Set(strings.Replace(skey, ".", "_", -1), sval)
					}
				}
			}
			each.Unset("_id")
		} else {
			current := allKeys[keyword]
			current.Set("value3", current.GetFloat64("value3")+each.GetFloat64("value1"))
		}
	}

	return rows, nil
}

func SummarizeLedgerSum(
	filter *dbox.Filter,
	columns []string,
	datapoints []string,
	fnTransform func(m *toolkit.M) error) ([]*toolkit.M, error) {
	conn := DB().Connection
	q := conn.NewQuery().From(summaryTableName)
	if filter != nil {
		q = q.Where(filter)
	}

	if len(columns) > 0 {
		cs := []string{}
		for i := range columns {
			cs = append(cs, strings.ToLower(columns[i]))
		}
		q = q.Group(cs...)
	}
	if len(datapoints) == 0 {
		return nil, errors.New("SummarizedLedgerSum: Datapoints should be defined at least 1")
	}
	for _, dp := range datapoints {
		dps := strings.Split(strings.ToLower(dp), ":")
		if len(dps) < 2 {
			return nil, errors.New("SummarizeLedgerSum: Parameters should follow this pattern aggrOp:fieldName:[alias - optional]")
		}

		fieldid := dps[1]
		alias := fieldid
		op := ""
		if !strings.HasPrefix(dps[0], "$") {
			dps[0] = "$" + strings.ToLower(dps[0])
		}

		if toolkit.HasMember([]string{dbox.AggrSum, dbox.AggrAvr, dbox.AggrMax,
			dbox.AggrMin, dbox.AggrMean, dbox.AggrMed}, dps[0]) {
			op = dps[0]
		}
		if op == "" {
			return nil, errors.New("SummarizeLedgerSum: Invalid Operation")
		}
		if len(dps) > 2 {
			alias = dps[2]
		}

		if strings.HasPrefix(alias, "$") {
			alias = alias[1:]
		}

		if fnumber, enumber := toolkit.IsStringNumber(fieldid, "."); enumber == nil {
			q = q.Aggr(op, fnumber, alias)
		} else {
			q = q.Aggr(op, fieldid, alias)
		}
	}

	c, e := q.Cursor(nil)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Preparing cursor error " + e.Error())
	}
	defer c.Close()

	ms := []*toolkit.M{}
	e = c.Fetch(&ms, 0, false)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
	}

	if fnTransform != nil {
		for idx, m := range ms {
			e = fnTransform(m)
			if e != nil {
				return nil, errors.New(toolkit.Sprintf("SummarizedLedgerSum: Transform error on index %d, %s",
					idx, e.Error()))
			}
		}
	}

	return ms, nil
}

func (s *LedgerSummary) Save() error {
	e := Save(s)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", s.TableName(), "save", e.Error()))
	}
	return e
}

type PivotParam struct {
	Dimensions []*PivotParamDimensions `json:"dimensions"`
	DataPoints []*PivotParamDataPoint  `json:"datapoints"`
	Which      string                  `json:"which"`
	Filters    []toolkit.M             `json:"filters"`
	Type       string                  `json:"type"`
}

type PivotParamDimensions struct {
	Field string `json:"field"`
}

type PivotParamDataPoint struct {
	Aggr  string `json:"aggr"`
	Field string `json:"field"`
}

func (p *PivotParam) ParseDimensions() (res []string) {
	res = []string{}
	for _, each := range p.Dimensions {
		res = append(res, each.Field)
	}
	return
}

func (p *PivotParam) ParseDataPoints() (res []string) {
	for _, each := range p.DataPoints {
		parts := []string{each.Aggr, each.Field, each.Field}

		if !strings.HasPrefix(parts[1], "$") {
			parts[1] = fmt.Sprintf("$%s", parts[1])
		}

		res = append(res, strings.Join(parts, ":"))
	}
	return
}

func (p *PivotParam) ParseFilter() *dbox.Filter {
	filters := []*dbox.Filter{}

	for _, each := range p.Filters {
		field := each.GetString("Field")

		switch each.GetString("Op") {
		case dbox.FilterOpIn:
			values := []string{}
			for _, v := range each.Get("Value").([]interface{}) {
				values = append(values, v.(string))
			}

			if len(values) > 0 {
				filters = append(filters, dbox.In(field, values))
			}
		case dbox.FilterOpGte:
			var value interface{} = each.GetString("Value")

			if value.(string) != "" {
				if field == "year" {
					t, err := time.Parse(time.RFC3339Nano, value.(string))
					if err != nil {
						fmt.Println(err.Error())
					} else {
						value = t.Year()
					}
				}

				filters = append(filters, dbox.Gte(field, value))
			}
		case dbox.FilterOpLte:
			var value interface{} = each.GetString("Value")

			if value.(string) != "" {
				if field == "year" {
					t, err := time.Parse(time.RFC3339Nano, value.(string))
					if err != nil {
						fmt.Println(err.Error())
					} else {
						value = t.Year()
					}
				}

				filters = append(filters, dbox.Lte(field, value))
			}
		case dbox.FilterOpEqual:
			value := each.GetString("Value")

			filters = append(filters, dbox.Gte(field, value))
		}
	}

	for _, each := range filters {
		fmt.Printf(">>>> %#v\n", *each)
	}

	return dbox.And(filters...)
}
<|endoftext|>