<commit_before>// Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved.
// Use of this source code is governed by a LGPLv2.1
// license that can be found in the LICENSE file.

// +build linux,cgo

package lxc

// #cgo pkg-config: lxc
// #include <lxc/lxccontainer.h>
// #include "lxc.h"
import "C"

import (
	"runtime"
	"unsafe"
)

// NewContainer returns a new container struct.
func NewContainer(name string, lxcpath ...string) (*Container, error) {
	var container *C.struct_lxc_container

	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		container = C.lxc_container_new(cname, clxcpath)
	} else {
		container = C.lxc_container_new(cname, nil)
	}

	if container == nil {
		return nil, ErrNewFailed
	}
	c := &Container{container: container, verbosity: Quiet}

	// http://golang.org/pkg/runtime/#SetFinalizer
	runtime.SetFinalizer(c, Release)
	return c, nil
}

// Acquire increments the reference counter of the container object.
func Acquire(c *Container) bool {
	return C.lxc_container_get(c.container) == 1
}

// Release decrements the reference counter of the container object.
func Release(c *Container) bool {
	// http://golang.org/pkg/runtime/#SetFinalizer
	runtime.SetFinalizer(c, nil)

	return C.lxc_container_put(c.container) == 1
}

// Version returns the LXC version.
func Version() string {
	return C.GoString(C.lxc_get_version())
}

// GlobalConfigItem returns the value of the given global config key.
func GlobalConfigItem(name string) string {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	return C.GoString(C.lxc_get_global_config_item(cname))
}

// DefaultConfigPath returns default config path.
func DefaultConfigPath() string {
	return GlobalConfigItem("lxc.lxcpath")
}

// DefaultLvmVg returns the name of the default LVM volume group.
func DefaultLvmVg() string {
	return GlobalConfigItem("lxc.bdev.lvm.vg")
}

// DefaultZfsRoot returns the name of the default ZFS root.
func DefaultZfsRoot() string {
	return GlobalConfigItem("lxc.bdec.zfs.root")
}

// ContainerNames returns the names of defined and active containers on the system.
func ContainerNames(lxcpath ...string) []string {
	var size int
	var cnames **C.char

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		size = int(C.list_all_containers(clxcpath, &cnames, nil))
	} else {

		size = int(C.list_all_containers(nil, &cnames, nil))
	}

	if size < 1 {
		return nil
	}
	return convertNArgs(cnames, size)
}

// Containers returns the defined and active containers on the system. Only
// containers that could retrieved successfully are returned.
func Containers(lxcpath ...string) []Container {
	var containers []Container

	for _, v := range ContainerNames(lxcpath...) {
		if container, err := NewContainer(v, lxcpath...); err == nil {
			containers = append(containers, *container)
		}
	}

	return containers
}

// DefinedContainerNames returns the names of the defined containers on the system.
func DefinedContainerNames(lxcpath ...string) []string {
	var size int
	var cnames **C.char

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		size = int(C.list_defined_containers(clxcpath, &cnames, nil))
	} else {

		size = int(C.list_defined_containers(nil, &cnames, nil))
	}

	if size < 1 {
		return nil
	}
	return convertNArgs(cnames, size)
}

// DefinedContainers returns the defined containers on the system.  Only
// containers that could retrieved successfully are returned.
func DefinedContainers(lxcpath ...string) []Container {
	var containers []Container

	for _, v := range DefinedContainerNames(lxcpath...) {
		if container, err := NewContainer(v, lxcpath...); err == nil {
			containers = append(containers, *container)
		}
	}

	return containers
}

// ActiveContainerNames returns the names of the active containers on the system.
func ActiveContainerNames(lxcpath ...string) []string {
	var size int
	var cnames **C.char

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		size = int(C.list_active_containers(clxcpath, &cnames, nil))
	} else {

		size = int(C.list_active_containers(nil, &cnames, nil))
	}

	if size < 1 {
		return nil
	}
	return convertNArgs(cnames, size)
}

// ActiveContainers returns the active containers on the system. Only
// containers that could retrieved successfully are returned.
func ActiveContainers(lxcpath ...string) []Container {
	var containers []Container

	for _, v := range ActiveContainerNames(lxcpath...) {
		if container, err := NewContainer(v, lxcpath...); err == nil {
			containers = append(containers, *container)
		}
	}

	return containers
}
<commit_msg>fixes #30<commit_after>// Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved.
// Use of this source code is governed by a LGPLv2.1
// license that can be found in the LICENSE file.

// +build linux,cgo

package lxc

// #cgo pkg-config: lxc
// #include <lxc/lxccontainer.h>
// #include "lxc.h"
import "C"

import (
	"runtime"
	"unsafe"
)

// NewContainer returns a new container struct.
func NewContainer(name string, lxcpath ...string) (*Container, error) {
	var container *C.struct_lxc_container

	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		container = C.lxc_container_new(cname, clxcpath)
	} else {
		container = C.lxc_container_new(cname, nil)
	}

	if container == nil {
		return nil, ErrNewFailed
	}
	c := &Container{container: container, verbosity: Quiet}

	// http://golang.org/pkg/runtime/#SetFinalizer
	runtime.SetFinalizer(c, Release)
	return c, nil
}

// Acquire increments the reference counter of the container object.
func Acquire(c *Container) bool {
	return C.lxc_container_get(c.container) == 1
}

// Release decrements the reference counter of the container object.
func Release(c *Container) bool {
	// http://golang.org/pkg/runtime/#SetFinalizer
	runtime.SetFinalizer(c, nil)

	return C.lxc_container_put(c.container) == 1
}

// Version returns the LXC version.
func Version() string {
	return C.GoString(C.lxc_get_version())
}

// GlobalConfigItem returns the value of the given global config key.
func GlobalConfigItem(name string) string {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	return C.GoString(C.lxc_get_global_config_item(cname))
}

// DefaultConfigPath returns default config path.
func DefaultConfigPath() string {
	return GlobalConfigItem("lxc.lxcpath")
}

// DefaultLvmVg returns the name of the default LVM volume group.
func DefaultLvmVg() string {
	return GlobalConfigItem("lxc.bdev.lvm.vg")
}

// DefaultZfsRoot returns the name of the default ZFS root.
func DefaultZfsRoot() string {
	return GlobalConfigItem("lxc.bdev.zfs.root")
}

// ContainerNames returns the names of defined and active containers on the system.
func ContainerNames(lxcpath ...string) []string {
	var size int
	var cnames **C.char

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		size = int(C.list_all_containers(clxcpath, &cnames, nil))
	} else {

		size = int(C.list_all_containers(nil, &cnames, nil))
	}

	if size < 1 {
		return nil
	}
	return convertNArgs(cnames, size)
}

// Containers returns the defined and active containers on the system. Only
// containers that could retrieved successfully are returned.
func Containers(lxcpath ...string) []Container {
	var containers []Container

	for _, v := range ContainerNames(lxcpath...) {
		if container, err := NewContainer(v, lxcpath...); err == nil {
			containers = append(containers, *container)
		}
	}

	return containers
}

// DefinedContainerNames returns the names of the defined containers on the system.
func DefinedContainerNames(lxcpath ...string) []string {
	var size int
	var cnames **C.char

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		size = int(C.list_defined_containers(clxcpath, &cnames, nil))
	} else {

		size = int(C.list_defined_containers(nil, &cnames, nil))
	}

	if size < 1 {
		return nil
	}
	return convertNArgs(cnames, size)
}

// DefinedContainers returns the defined containers on the system.  Only
// containers that could retrieved successfully are returned.
func DefinedContainers(lxcpath ...string) []Container {
	var containers []Container

	for _, v := range DefinedContainerNames(lxcpath...) {
		if container, err := NewContainer(v, lxcpath...); err == nil {
			containers = append(containers, *container)
		}
	}

	return containers
}

// ActiveContainerNames returns the names of the active containers on the system.
func ActiveContainerNames(lxcpath ...string) []string {
	var size int
	var cnames **C.char

	if lxcpath != nil && len(lxcpath) == 1 {
		clxcpath := C.CString(lxcpath[0])
		defer C.free(unsafe.Pointer(clxcpath))

		size = int(C.list_active_containers(clxcpath, &cnames, nil))
	} else {

		size = int(C.list_active_containers(nil, &cnames, nil))
	}

	if size < 1 {
		return nil
	}
	return convertNArgs(cnames, size)
}

// ActiveContainers returns the active containers on the system. Only
// containers that could retrieved successfully are returned.
func ActiveContainers(lxcpath ...string) []Container {
	var containers []Container

	for _, v := range ActiveContainerNames(lxcpath...) {
		if container, err := NewContainer(v, lxcpath...); err == nil {
			containers = append(containers, *container)
		}
	}

	return containers
}
<|endoftext|>


<commit_before>package lz4

// #cgo CFLAGS: -O3
// #include "src/lz4.h"
// #include "src/lz4.c"
import "C"

import (
	"fmt"
	"unsafe"
)

// p gets a char pointer to the first byte of a []byte slice
func p(in []byte) *C.char {
	if len(in) == 0 {
		return (*C.char)(unsafe.Pointer(nil))
	}
	return (*C.char)(unsafe.Pointer(&in[0]))
}

// clen gets the length of a []byte slice as a char *
func clen(s []byte) C.int {
	return C.int(len(s))
}

// Uncompress with a known output size. len(out) should be equal to
// the length of the uncompressed out.
func Uncompress(in, out []byte) (err error) {
	read := int(C.LZ4_uncompress(p(in), p(out), clen(out)))

	if read != len(in) {
		err = fmt.Errorf("uncompress read %d bytes should have read %d",
			read, len(in))
	}
	return
}

// CompressBound calculates the size of the output buffer needed by
// Compress. This is based on the following macro:
//
// #define LZ4_COMPRESSBOUND(isize)
//      ((unsigned int)(isize) > (unsigned int)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
func CompressBound(in []byte) int {
	return len(in) + ((len(in) / 255) + 16)
}

// Compress compresses in and puts the content in out. len(out)
// should have enough space for the compressed data (use CompressBound
// to calculate). Returns the number of bytes in the out slice.
func Compress(in, out []byte) (outSize int, err error) {
	outSize = int(C.LZ4_compress_limitedOutput(p(in), p(out), clen(in), clen(out)))
	if outSize == 0 {
		err = fmt.Errorf("insufficient space for compression")
	}
	return
}
<commit_msg>Use LZ4_decompress_safe instead of LZ4_uncompress<commit_after>package lz4

// #cgo CFLAGS: -O3
// #include "src/lz4.h"
// #include "src/lz4.c"
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

// p gets a char pointer to the first byte of a []byte slice
func p(in []byte) *C.char {
	if len(in) == 0 {
		return (*C.char)(unsafe.Pointer(nil))
	}
	return (*C.char)(unsafe.Pointer(&in[0]))
}

// clen gets the length of a []byte slice as a char *
func clen(s []byte) C.int {
	return C.int(len(s))
}

// Uncompress with a known output size. len(out) should be equal to
// the length of the uncompressed out.
func Uncompress(in, out []byte) (error) {
	if int(C.LZ4_decompress_safe(p(in), p(out), clen(in), clen(out))) < 0 {
		return errors.New("Malformed compression stream")
	}

	return nil
}

// CompressBound calculates the size of the output buffer needed by
// Compress. This is based on the following macro:
//
// #define LZ4_COMPRESSBOUND(isize)
//      ((unsigned int)(isize) > (unsigned int)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
func CompressBound(in []byte) int {
	return len(in) + ((len(in) / 255) + 16)
}

// Compress compresses in and puts the content in out. len(out)
// should have enough space for the compressed data (use CompressBound
// to calculate). Returns the number of bytes in the out slice.
func Compress(in, out []byte) (outSize int, err error) {
	outSize = int(C.LZ4_compress_limitedOutput(p(in), p(out), clen(in), clen(out)))
	if outSize == 0 {
		err = fmt.Errorf("insufficient space for compression")
	}
	return
}
<|endoftext|>


<commit_before>package main

////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2016, Gianluca Fiore
//
////////////////////////////////////////////////////////////////////////////////

import (
	"flag"
	"fmt"
	"os"
	"path"
	"regexp"
	"strconv"
)

var mgnt_type string
var watchdir string

// Parse flags
func init() {

	const (
		def_watchdir = "/media/private/torrent/watch"
		def_type     = "others"
	)

	flag.StringVar(&mgnt_type, "type", def_type, "")
	flag.StringVar(&mgnt_type, "t", def_type, "")
	flag.StringVar(&watchdir, "watchdir", def_watchdir, "")
	flag.StringVar(&watchdir, "w", def_watchdir, "")

}

func main() {
	var torrentFilename, fileName string

	// initialize cli arguments
	flag.Parse()

	// at least the magnet link is needed, bail out if it was not given
	if flag.NArg() == 0 {
		fmt.Println("No magnet link given, exiting...")
		os.Exit(1)
	}

	// Compose the watch dir's path for the torrent file
	outputDir := path.Join(watchdir, mgnt_type)
	// Reassign first flag to a variable
	magnetLink := flag.Arg(0)

	// Compile a couple of regexp we need
	var validMagnet = regexp.MustCompile(`xt=urn:btih:([^&/]+)`)
	var displayName = regexp.MustCompile(`dn=([^&/]+)`)

	if validMagnet.MatchString(magnetLink) {
		if displayName.MatchString(magnetLink) {
			torrentFilename = displayName.FindString(magnetLink)
			// split at '='
			fileName = regexp.MustCompile(`=`).Split(torrentFilename, -1)[1]
			if len(fileName) == 0 {
				xt := validMagnet.FindString(magnetLink)
				fileName = regexp.MustCompile(`:`).Split(xt, -1)[2]
			}
			// Add torrent extension
			fileName = fileName + ".torrent"
		}
	} else {
		// not a valid magnet URI given
		fmt.Println("The magnet URI is not correct or unparseable")
		os.Exit(1)
	}

	// Create torrent file and output magnet link string to it
	f, err := os.Create(path.Join(outputDir, fileName))
	if err != nil {
		panic(err)
	}
	_, w_err := f.WriteString("d10:magnet-uri" + strconv.Itoa(len(magnetLink)) + ":" + magnetLink + "e")
	if w_err != nil {
		panic(err)
	}

	defer f.Close()

	os.Exit(0)
}
<commit_msg>linting format edits<commit_after>package main

////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2016, Gianluca Fiore
//
////////////////////////////////////////////////////////////////////////////////

import (
	"flag"
	"fmt"
	"os"
	"path"
	"regexp"
	"strconv"
)

var mgntType string
var watchdir string

// Parse flags
func init() {

	const (
		defWatchdir = "/media/private/torrent/watch"
		defType     = "others"
	)

	flag.StringVar(&mgntType, "type", defType, "")
	flag.StringVar(&mgntType, "t", defType, "")
	flag.StringVar(&watchdir, "watchdir", defWatchdir, "")
	flag.StringVar(&watchdir, "w", defWatchdir, "")

}

func main() {
	var torrentFilename, fileName string

	// initialize cli arguments
	flag.Parse()

	// at least the magnet link is needed, bail out if it was not given
	if flag.NArg() == 0 {
		fmt.Println("No magnet link given, exiting...")
		os.Exit(1)
	}

	// Compose the watch dir's path for the torrent file
	outputDir := path.Join(watchdir, mgntType)
	// Reassign first flag to a variable
	magnetLink := flag.Arg(0)

	// Compile a couple of regexp we need
	var validMagnet = regexp.MustCompile(`xt=urn:btih:([^&/]+)`)
	var displayName = regexp.MustCompile(`dn=([^&/]+)`)

	if validMagnet.MatchString(magnetLink) {
		if displayName.MatchString(magnetLink) {
			torrentFilename = displayName.FindString(magnetLink)
			// split at '='
			fileName = regexp.MustCompile(`=`).Split(torrentFilename, -1)[1]
			if len(fileName) == 0 {
				xt := validMagnet.FindString(magnetLink)
				fileName = regexp.MustCompile(`:`).Split(xt, -1)[2]
			}
			// Add torrent extension
			fileName = fileName + ".torrent"
		}
	} else {
		// not a valid magnet URI given
		fmt.Println("The magnet URI is not correct or unparseable")
		os.Exit(1)
	}

	// Create torrent file and output magnet link string to it
	f, err := os.Create(path.Join(outputDir, fileName))
	if err != nil {
		panic(err)
	}
	_, wErr := f.WriteString("d10:magnet-uri" + strconv.Itoa(len(magnetLink)) + ":" + magnetLink + "e")
	if wErr != nil {
		panic(err)
	}

	defer f.Close()

	os.Exit(0)
}
<|endoftext|>


<commit_before>package gostats

import (
  "math"
)

func Min(slice []float64) (float64, error) {
  if len(slice) == 0 {
    return math.NaN(), EmptySlice
  }
  min := slice[0]
  for i := 1; i < len(slice); i++ {
    if slice[i] < min {
      min = slice[i]
    }
  }
  return min, nil
}
<commit_msg>add comment for godoc<commit_after>package gostats

import (
  "math"
)

//return the smallest value in a slice
func Min(slice []float64) (float64, error) {
  if len(slice) == 0 {
    return math.NaN(), EmptySlice
  }
  min := slice[0]
  for i := 1; i < len(slice); i++ {
    if slice[i] < min {
      min = slice[i]
    }
  }
  return min, nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	// "fmt"
	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
	"time"
)

type NotificationContent struct {
	// unique identifier of NotificationContent
	Id int64 `json:"id"`

	// target of the activity (replied messageId, followed accountId etc.)
	TargetId int64 `json:"targetId"          sql:"NOT NULL"`

	// Type of the NotificationContent
	TypeConstant string `json:"typeConstant" sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the NotificationContent
	CreatedAt time.Time `json:"createdAt"`
}

const (
	// NotificationContent Types
	NotificationContent_TYPE_LIKE    = "like"
	NotificationContent_TYPE_COMMENT = "comment"
	NotificationContent_TYPE_FOLLOW  = "follow"
	NotificationContent_TYPE_JOIN    = "join"
	NotificationContent_TYPE_LEAVE   = "leave"
	NotificationContent_TYPE_MENTION = "mention"
)

func NewNotificationContent() *NotificationContent {
	return &NotificationContent{}
}

func (n *NotificationContent) GetId() int64 {
	return n.Id
}

func (n NotificationContent) TableName() string {
	return "notification.notification_content"
}

// Create checks for NotificationContent using type_constant and target_id
// and creates new one if it does not exist.
func (n *NotificationContent) Create() error {
	if err := n.FindByTarget(); err != nil {
		if err != gorm.RecordNotFound {
			return err
		}
		return bongo.B.Create(n)
	}

	return nil
}

func (n *NotificationContent) One(q *bongo.Query) error {
	return bongo.B.One(n, n, q)
}

func (n *NotificationContent) ById(id int64) error {
	return bongo.B.ById(n, id)
}

func (n *NotificationContent) FindByTarget() error {
	s := map[string]interface{}{
		"type_constant": n.TypeConstant,
		"target_id":     n.TargetId,
	}
	q := bongo.NewQS(s)

	return n.One(q)
}

// CreateNotification validates notifiable instance and creates a new notification
// with actor activity.
func CreateNotificationContent(i Notifiable) (*NotificationContent, error) {
	// first check for type constant and target id
	if i.GetType() == "" {
		return nil, errors.New("Type must be set")
	}

	if i.GetTargetId() == 0 {
		return nil, errors.New("TargetId must be set")
	}

	if i.GetActorId() == 0 {
		return nil, errors.New("ActorId must be set")
	}

	// check for previous NotificationContent create if it does not exist (type:comment targetId:messageId)
	nc := NewNotificationContent()
	nc.TypeConstant = i.GetType()
	nc.TargetId = i.GetTargetId()

	if err := nc.Create(); err != nil {
		return nil, err
	}
	a := NewNotificationActivity()
	a.NotificationContentId = nc.Id
	a.ActorId = i.GetActorId()
	if err := a.Create(); err != nil {
		return nil, err
	}

	return nc, nil
}

// FetchByIds fetches notification contents with given ids
func (n *NotificationContent) FetchByIds(ids []int64) ([]NotificationContent, error) {
	notificationContents := make([]NotificationContent, 0)
	if err := bongo.B.FetchByIds(n, &notificationContents, ids); err != nil {
		return nil, err
	}
	return notificationContents, nil
}

// FetchMapByIds returns NotificationContent map with given ids
func (n *NotificationContent) FetchMapByIds(ids []int64) (map[int64]NotificationContent, error) {
	ncList, err := n.FetchByIds(ids)
	if err != nil {
		return nil, err
	}

	ncMap := make(map[int64]NotificationContent, 0)
	for _, nc := range ncList {
		ncMap[nc.Id] = nc
	}

	return ncMap, nil
}

// GetEventType retrieves related event name for the NotificationContent instance
func (n *NotificationContent) GetEventType() string {
	// TODO it could be stored in a map
	switch n.TypeConstant {
	case NotificationContent_TYPE_LIKE:
		return "LikeIsAdded"
	case NotificationContent_TYPE_COMMENT:
		return "ReplyIsAdded"
	case NotificationContent_TYPE_FOLLOW:
		return "FollowHappened"
	case NotificationContent_TYPE_JOIN:
		return "GroupJoined"
	case NotificationContent_TYPE_LEAVE:
		return "GroupLeft"
	default:
		return "undefined"
	}
}

// CreateNotificationType creates an instance of notifiable subclasses
func CreateNotificationContentType(notificationType string) (Notifiable, error) {
	switch notificationType {
	case NotificationContent_TYPE_LIKE:
		return NewInteractionNotification(notificationType), nil
	case NotificationContent_TYPE_COMMENT:
		return NewReplyNotification(), nil
	case NotificationContent_TYPE_FOLLOW:
		return NewFollowNotification(), nil
	case NotificationContent_TYPE_JOIN:
		return NewGroupNotification(notificationType), nil
	case NotificationContent_TYPE_LEAVE:
		return NewGroupNotification(notificationType), nil
	case NotificationContent_TYPE_MENTION:
		return NewMentionNotification(), nil
	default:
		return nil, errors.New("undefined notification type")
	}

}

func (nc *NotificationContent) AfterCreate() {
	bongo.B.AfterCreate(nc)
}

func (nc *NotificationContent) AfterUpdate() {
	bongo.B.AfterUpdate(nc)
}

func (nc *NotificationContent) AfterDelete() {
	bongo.B.AfterDelete(nc)
}
<commit_msg>Social: GetContentType method added to NotificationContent<commit_after>package models

import (
	"errors"
	// "fmt"
	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
	"time"
)

type NotificationContent struct {
	// unique identifier of NotificationContent
	Id int64 `json:"id"`

	// target of the activity (replied messageId, followed accountId etc.)
	TargetId int64 `json:"targetId"          sql:"NOT NULL"`

	// Type of the NotificationContent
	TypeConstant string `json:"typeConstant" sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the NotificationContent
	CreatedAt time.Time `json:"createdAt"`
}

const (
	// NotificationContent Types
	NotificationContent_TYPE_LIKE    = "like"
	NotificationContent_TYPE_COMMENT = "comment"
	NotificationContent_TYPE_FOLLOW  = "follow"
	NotificationContent_TYPE_JOIN    = "join"
	NotificationContent_TYPE_LEAVE   = "leave"
	NotificationContent_TYPE_MENTION = "mention"
)

func NewNotificationContent() *NotificationContent {
	return &NotificationContent{}
}

func (n *NotificationContent) GetId() int64 {
	return n.Id
}

func (n NotificationContent) TableName() string {
	return "notification.notification_content"
}

// Create checks for NotificationContent using type_constant and target_id
// and creates new one if it does not exist.
func (n *NotificationContent) Create() error {
	if err := n.FindByTarget(); err != nil {
		if err != gorm.RecordNotFound {
			return err
		}
		return bongo.B.Create(n)
	}

	return nil
}

func (n *NotificationContent) One(q *bongo.Query) error {
	return bongo.B.One(n, n, q)
}

func (n *NotificationContent) ById(id int64) error {
	return bongo.B.ById(n, id)
}

func (n *NotificationContent) FindByTarget() error {
	s := map[string]interface{}{
		"type_constant": n.TypeConstant,
		"target_id":     n.TargetId,
	}
	q := bongo.NewQS(s)

	return n.One(q)
}

// CreateNotification validates notifiable instance and creates a new notification
// with actor activity.
func CreateNotificationContent(i Notifiable) (*NotificationContent, error) {
	// first check for type constant and target id
	if i.GetType() == "" {
		return nil, errors.New("Type must be set")
	}

	if i.GetTargetId() == 0 {
		return nil, errors.New("TargetId must be set")
	}

	if i.GetActorId() == 0 {
		return nil, errors.New("ActorId must be set")
	}

	// check for previous NotificationContent create if it does not exist (type:comment targetId:messageId)
	nc := NewNotificationContent()
	nc.TypeConstant = i.GetType()
	nc.TargetId = i.GetTargetId()

	if err := nc.Create(); err != nil {
		return nil, err
	}
	a := NewNotificationActivity()
	a.NotificationContentId = nc.Id
	a.ActorId = i.GetActorId()
	if err := a.Create(); err != nil {
		return nil, err
	}

	return nc, nil
}

// FetchByIds fetches notification contents with given ids
func (n *NotificationContent) FetchByIds(ids []int64) ([]NotificationContent, error) {
	notificationContents := make([]NotificationContent, 0)
	if err := bongo.B.FetchByIds(n, &notificationContents, ids); err != nil {
		return nil, err
	}
	return notificationContents, nil
}

// FetchMapByIds returns NotificationContent map with given ids
func (n *NotificationContent) FetchMapByIds(ids []int64) (map[int64]NotificationContent, error) {
	ncList, err := n.FetchByIds(ids)
	if err != nil {
		return nil, err
	}

	ncMap := make(map[int64]NotificationContent, 0)
	for _, nc := range ncList {
		ncMap[nc.Id] = nc
	}

	return ncMap, nil
}

// GetEventType retrieves related event name for the NotificationContent instance
func (n *NotificationContent) GetEventType() string {
	// TODO it could be stored in a map
	switch n.TypeConstant {
	case NotificationContent_TYPE_LIKE:
		return "LikeIsAdded"
	case NotificationContent_TYPE_COMMENT:
		return "ReplyIsAdded"
	case NotificationContent_TYPE_FOLLOW:
		return "FollowHappened"
	case NotificationContent_TYPE_JOIN:
		return "GroupJoined"
	case NotificationContent_TYPE_LEAVE:
		return "GroupLeft"
	default:
		return "undefined"
	}
}

// CreateNotificationType creates an instance of notifiable subclasses
func CreateNotificationContentType(notificationType string) (Notifiable, error) {
	switch notificationType {
	case NotificationContent_TYPE_LIKE:
		return NewInteractionNotification(notificationType), nil
	case NotificationContent_TYPE_COMMENT:
		return NewReplyNotification(), nil
	case NotificationContent_TYPE_FOLLOW:
		return NewFollowNotification(), nil
	case NotificationContent_TYPE_JOIN:
		return NewGroupNotification(notificationType), nil
	case NotificationContent_TYPE_LEAVE:
		return NewGroupNotification(notificationType), nil
	case NotificationContent_TYPE_MENTION:
		return NewMentionNotification(), nil
	default:
		return nil, errors.New("undefined notification type")
	}

}

func (n *NotificationContent) GetContentType() (Notifiable, error) {
	return CreateNotificationContentType(n.TypeConstant)
}

func (nc *NotificationContent) AfterCreate() {
	bongo.B.AfterCreate(nc)
}

func (nc *NotificationContent) AfterUpdate() {
	bongo.B.AfterUpdate(nc)
}

func (nc *NotificationContent) AfterDelete() {
	bongo.B.AfterDelete(nc)
}
<|endoftext|>


<commit_before>package somatree

import (
	"sync"

	"github.com/satori/go.uuid"
)

//
// Interface: SomaTreePropertier
func (tec *SomaTreeElemCluster) SetProperty(
	p SomaTreeProperty) {
	switch p.GetType() {
	case "custom":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertyCustom).Id, uuid.Nil) {
			p.(*PropertyCustom).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertyCustom).InheritedFrom = tec.Id
		p.(*PropertyCustom).Inherited = false
		p.(*PropertyCustom).SourceId = p.(*PropertyCustom).Id
		// send a scrubbed copy downward
		f := new(PropertyCustom)
		*f = *p.(*PropertyCustom)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertyCustom).Instances = nil
		tec.setCustomProperty(p)
	case "service":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertyService).Id, uuid.Nil) {
			p.(*PropertyService).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertyService).InheritedFrom = tec.Id
		p.(*PropertyService).Inherited = false
		p.(*PropertyService).SourceId = p.(*PropertyService).Id
		// send a scrubbed copy downward
		f := new(PropertyService)
		*f = *p.(*PropertyService)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertyService).Instances = nil
		tec.setServiceProperty(p)
	case "system":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertySystem).Id, uuid.Nil) {
			p.(*PropertySystem).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertySystem).InheritedFrom = tec.Id
		p.(*PropertySystem).Inherited = false
		p.(*PropertySystem).SourceId = p.(*PropertySystem).Id
		// send a scrubbed copy downward
		f := new(PropertySystem)
		*f = *p.(*PropertySystem)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertySystem).Instances = nil
		tec.setSystemProperty(p)
	case "oncall":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertyOncall).Id, uuid.Nil) {
			p.(*PropertyOncall).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertyOncall).InheritedFrom = tec.Id
		p.(*PropertyOncall).Inherited = false
		p.(*PropertyOncall).SourceId = p.(*PropertyOncall).Id
		// send a scrubbed copy downward
		f := new(PropertyOncall)
		*f = *p.(*PropertyOncall)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertyOncall).Instances = nil
		tec.setOncallProperty(p)
	}
	tec.actionPropertyNew(tec.setupPropertyAction(p))
}

func (tec *SomaTreeElemCluster) inheritProperty(
	p SomaTreeProperty) {
	// make a copy of the scrubbed version we inherited
	f := new(PropertyOncall)
	*f = *p.(*PropertyOncall)

	switch p.GetType() {
	case "custom":
		p.(*PropertyCustom).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertyCustom).Id, uuid.Nil) {
			p.(*PropertyCustom).Id = uuid.NewV4()
		}
		p.(*PropertyCustom).Instances = nil
		tec.setCustomProperty(p)
	case "service":
		p.(*PropertyService).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertyService).Id, uuid.Nil) {
			p.(*PropertyService).Id = uuid.NewV4()
		}
		p.(*PropertyService).Instances = nil
		tec.setServiceProperty(p)
	case "system":
		p.(*PropertySystem).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertySystem).Id, uuid.Nil) {
			p.(*PropertySystem).Id = uuid.NewV4()
		}
		p.(*PropertySystem).Instances = nil
		tec.setSystemProperty(p)
	case "oncall":
		p.(*PropertyOncall).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertyOncall).Id, uuid.Nil) {
			p.(*PropertyOncall).Id = uuid.NewV4()
		}
		p.(*PropertyOncall).Instances = nil
		tec.setOncallProperty(p)
	}
	tec.actionPropertyNew(tec.setupPropertyAction(p))
	// inherit scrubbed version
	tec.inheritPropertyDeep(f)
}

func (tec *SomaTreeElemCluster) inheritPropertyDeep(
	p SomaTreeProperty) {
	var wg sync.WaitGroup
	for child, _ := range tec.Children {
		wg.Add(1)
		c := child
		go func(stp SomaTreeProperty) {
			defer wg.Done()
			tec.Children[c].inheritProperty(stp)
		}(p)
	}
	wg.Wait()
}

func (tec *SomaTreeElemCluster) setCustomProperty(
	p SomaTreeProperty) {
	tec.PropertyCustom[p.GetID()] = p
}

func (tec *SomaTreeElemCluster) setServiceProperty(
	p SomaTreeProperty) {
	tec.PropertyService[p.GetID()] = p
}

func (tec *SomaTreeElemCluster) setSystemProperty(
	p SomaTreeProperty) {
	tec.PropertySystem[p.GetID()] = p
}

func (tec *SomaTreeElemCluster) setOncallProperty(
	p SomaTreeProperty) {
	tec.PropertyOncall[p.GetID()] = p
}

// when a child attaches, it calls self.Parent.syncProperty(self.Id)
// to get get all properties of that part of the tree
func (tec *SomaTreeElemCluster) syncProperty(
	childId string) {
customloop:
	for prop, _ := range tec.PropertyCustom {
		if !tec.PropertyCustom[prop].hasInheritance() {
			continue customloop
		}
		f := new(PropertyCustom)
		*f = *tec.PropertyCustom[prop].(*PropertyCustom)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
oncallloop:
	for prop, _ := range tec.PropertyOncall {
		if !tec.PropertyOncall[prop].hasInheritance() {
			continue oncallloop
		}
		f := new(PropertyOncall)
		*f = *tec.PropertyOncall[prop].(*PropertyOncall)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
serviceloop:
	for prop, _ := range tec.PropertyService {
		if !tec.PropertyService[prop].hasInheritance() {
			continue serviceloop
		}
		f := new(PropertyService)
		*f = *tec.PropertyService[prop].(*PropertyService)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
systemloop:
	for prop, _ := range tec.PropertySystem {
		if !tec.PropertySystem[prop].hasInheritance() {
			continue systemloop
		}
		f := new(PropertySystem)
		*f = *tec.PropertySystem[prop].(*PropertySystem)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
}

// function to be used by a child to check if the parent has a
// specific Property
func (tec *SomaTreeElemCluster) checkProperty(
	propType string, propId string) bool {
	switch propType {
	case "custom":
		if _, ok := tec.PropertyCustom[propId]; ok {
			return true
		}
	case "service":
		if _, ok := tec.PropertyService[propId]; ok {
			return true
		}
	case "system":
		if _, ok := tec.PropertySystem[propId]; ok {
			return true
		}
	case "oncall":
		if _, ok := tec.PropertyOncall[propId]; ok {
			return true
		}
	}
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Bugfix object type and scope issue<commit_after>package somatree

import (
	"sync"

	"github.com/satori/go.uuid"
)

//
// Interface: SomaTreePropertier
func (tec *SomaTreeElemCluster) SetProperty(
	p SomaTreeProperty) {
	switch p.GetType() {
	case "custom":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertyCustom).Id, uuid.Nil) {
			p.(*PropertyCustom).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertyCustom).InheritedFrom = tec.Id
		p.(*PropertyCustom).Inherited = false
		p.(*PropertyCustom).SourceId = p.(*PropertyCustom).Id
		// send a scrubbed copy downward
		f := new(PropertyCustom)
		*f = *p.(*PropertyCustom)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertyCustom).Instances = nil
		tec.setCustomProperty(p)
	case "service":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertyService).Id, uuid.Nil) {
			p.(*PropertyService).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertyService).InheritedFrom = tec.Id
		p.(*PropertyService).Inherited = false
		p.(*PropertyService).SourceId = p.(*PropertyService).Id
		// send a scrubbed copy downward
		f := new(PropertyService)
		*f = *p.(*PropertyService)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertyService).Instances = nil
		tec.setServiceProperty(p)
	case "system":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertySystem).Id, uuid.Nil) {
			p.(*PropertySystem).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertySystem).InheritedFrom = tec.Id
		p.(*PropertySystem).Inherited = false
		p.(*PropertySystem).SourceId = p.(*PropertySystem).Id
		// send a scrubbed copy downward
		f := new(PropertySystem)
		*f = *p.(*PropertySystem)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertySystem).Instances = nil
		tec.setSystemProperty(p)
	case "oncall":
		// generate uuid if none is set
		if uuid.Equal(p.(*PropertyOncall).Id, uuid.Nil) {
			p.(*PropertyOncall).Id = uuid.NewV4()
		}
		// this property is the source instance
		p.(*PropertyOncall).InheritedFrom = tec.Id
		p.(*PropertyOncall).Inherited = false
		p.(*PropertyOncall).SourceId = p.(*PropertyOncall).Id
		// send a scrubbed copy downward
		f := new(PropertyOncall)
		*f = *p.(*PropertyOncall)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.inheritPropertyDeep(f)
		// scrub instance startup information prior to setting
		p.(*PropertyOncall).Instances = nil
		tec.setOncallProperty(p)
	}
	tec.actionPropertyNew(tec.setupPropertyAction(p))
}

func (tec *SomaTreeElemCluster) inheritProperty(
	p SomaTreeProperty) {

	switch p.GetType() {
	case "custom":
		f := new(PropertyCustom)
		*f = *p.(*PropertyCustom)
		p.(*PropertyCustom).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertyCustom).Id, uuid.Nil) {
			p.(*PropertyCustom).Id = uuid.NewV4()
		}
		p.(*PropertyCustom).Instances = nil
		tec.setCustomProperty(p)
		tec.inheritPropertyDeep(f)
	case "service":
		f := new(PropertyService)
		*f = *p.(*PropertyService)
		p.(*PropertyService).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertyService).Id, uuid.Nil) {
			p.(*PropertyService).Id = uuid.NewV4()
		}
		p.(*PropertyService).Instances = nil
		tec.setServiceProperty(p)
		tec.inheritPropertyDeep(f)
	case "system":
		f := new(PropertySystem)
		*f = *p.(*PropertySystem)
		p.(*PropertySystem).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertySystem).Id, uuid.Nil) {
			p.(*PropertySystem).Id = uuid.NewV4()
		}
		p.(*PropertySystem).Instances = nil
		tec.setSystemProperty(p)
		tec.inheritPropertyDeep(f)
	case "oncall":
		f := new(PropertyOncall)
		*f = *p.(*PropertyOncall)
		p.(*PropertyOncall).Id = p.GetInstanceId(tec.Type, tec.Id)
		if uuid.Equal(p.(*PropertyOncall).Id, uuid.Nil) {
			p.(*PropertyOncall).Id = uuid.NewV4()
		}
		p.(*PropertyOncall).Instances = nil
		tec.setOncallProperty(p)
		tec.inheritPropertyDeep(f)
	}
	tec.actionPropertyNew(tec.setupPropertyAction(p))
}

func (tec *SomaTreeElemCluster) inheritPropertyDeep(
	p SomaTreeProperty) {
	var wg sync.WaitGroup
	for child, _ := range tec.Children {
		wg.Add(1)
		c := child
		go func(stp SomaTreeProperty) {
			defer wg.Done()
			tec.Children[c].inheritProperty(stp)
		}(p)
	}
	wg.Wait()
}

func (tec *SomaTreeElemCluster) setCustomProperty(
	p SomaTreeProperty) {
	tec.PropertyCustom[p.GetID()] = p
}

func (tec *SomaTreeElemCluster) setServiceProperty(
	p SomaTreeProperty) {
	tec.PropertyService[p.GetID()] = p
}

func (tec *SomaTreeElemCluster) setSystemProperty(
	p SomaTreeProperty) {
	tec.PropertySystem[p.GetID()] = p
}

func (tec *SomaTreeElemCluster) setOncallProperty(
	p SomaTreeProperty) {
	tec.PropertyOncall[p.GetID()] = p
}

// when a child attaches, it calls self.Parent.syncProperty(self.Id)
// to get get all properties of that part of the tree
func (tec *SomaTreeElemCluster) syncProperty(
	childId string) {
customloop:
	for prop, _ := range tec.PropertyCustom {
		if !tec.PropertyCustom[prop].hasInheritance() {
			continue customloop
		}
		f := new(PropertyCustom)
		*f = *tec.PropertyCustom[prop].(*PropertyCustom)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
oncallloop:
	for prop, _ := range tec.PropertyOncall {
		if !tec.PropertyOncall[prop].hasInheritance() {
			continue oncallloop
		}
		f := new(PropertyOncall)
		*f = *tec.PropertyOncall[prop].(*PropertyOncall)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
serviceloop:
	for prop, _ := range tec.PropertyService {
		if !tec.PropertyService[prop].hasInheritance() {
			continue serviceloop
		}
		f := new(PropertyService)
		*f = *tec.PropertyService[prop].(*PropertyService)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
systemloop:
	for prop, _ := range tec.PropertySystem {
		if !tec.PropertySystem[prop].hasInheritance() {
			continue systemloop
		}
		f := new(PropertySystem)
		*f = *tec.PropertySystem[prop].(*PropertySystem)
		f.Inherited = true
		f.Id = uuid.Nil
		tec.Children[childId].inheritProperty(f)
	}
}

// function to be used by a child to check if the parent has a
// specific Property
func (tec *SomaTreeElemCluster) checkProperty(
	propType string, propId string) bool {
	switch propType {
	case "custom":
		if _, ok := tec.PropertyCustom[propId]; ok {
			return true
		}
	case "service":
		if _, ok := tec.PropertyService[propId]; ok {
			return true
		}
	case "system":
		if _, ok := tec.PropertySystem[propId]; ok {
			return true
		}
	case "oncall":
		if _, ok := tec.PropertyOncall[propId]; ok {
			return true
		}
	}
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


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

import (
	"context"
	"sync"
	"time"

	"github.com/golang/protobuf/ptypes"
	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/gcloud/gs"
	"go.chromium.org/luci/common/logging"
	log "go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/tsmon/distribution"
	"go.chromium.org/luci/common/tsmon/field"
	"go.chromium.org/luci/common/tsmon/metric"
	montypes "go.chromium.org/luci/common/tsmon/types"
	"go.chromium.org/luci/logdog/api/config/svcconfig"
	logdog "go.chromium.org/luci/logdog/api/endpoints/coordinator/services/v1"
	"go.chromium.org/luci/logdog/appengine/coordinator"
	"go.chromium.org/luci/logdog/server/archivist"
	"go.chromium.org/luci/logdog/server/bundleServicesClient"
	"go.chromium.org/luci/logdog/server/service"

	"go.chromium.org/luci/hardcoded/chromeinfra"
)

// loopParams control the outer archivist loop.
type loopParams struct {
	batchSize int64
	leaseTime time.Duration
}

var (
	errInvalidConfig = errors.New("invalid configuration")
	errNoWorkToDo    = errors.New("no work to do")

	// Note: these initial values are mostly bogus, they are replaced by
	// fetchLoopParams before the loop starts.
	loop = loopParams{
		// batchSize is the number of jobs to lease from taskqueue per cycle.
		//
		// TaskQueue has a limit of 10qps for leasing tasks, so the batch size must
		// be set to:
		// batchSize * 10 > (max expected stream creation QPS)
		//
		// In 2020, max stream QPS is approximately 1000 QPS
		batchSize: 500,

		// leaseTime is the amount of time to to lease the batch of tasks for.
		//
		// We need:
		// (time to process avg stream) * batchSize < leaseTime
		//
		// As of 2020, 90th percentile process time per stream is ~5s, 95th
		// percentile of the loop time is 25m.
		leaseTime: 40 * time.Minute,
	}

	// loopM protects 'loop'.
	loopM = sync.Mutex{}

	// maxSleepTime is the max amount of time to sleep in-between errors, in seconds.
	maxSleepTime = 32

	// tsTaskProcessingTime measures the amount of time spent processing a single
	// task.
	//
	// The "consumed" field is true if the underlying task was consumed and
	// false if it was not.
	tsTaskProcessingTime = metric.NewCumulativeDistribution("logdog/archivist/task_processing_time_ms_ng",
		"The amount of time (in milliseconds) that a single task takes to process in the new pipeline.",
		&montypes.MetricMetadata{Units: montypes.Milliseconds},
		distribution.DefaultBucketer,
		field.Bool("consumed"))

	tsLoopCycleTime = metric.NewCumulativeDistribution("logdog/archivist/loop_cycle_time_ms",
		"The amount of time a single batch of leases takes to process.",
		&montypes.MetricMetadata{Units: montypes.Milliseconds},
		distribution.DefaultBucketer)

	tsLeaseCount = metric.NewCounter("logdog/archivist/tasks_leased",
		"Number of tasks leased.",
		nil)

	tsNackCount = metric.NewCounter("logdog/archivist/tasks_not_acked",
		"Number of tasks leased but failed.",
		nil)

	tsAckCount = metric.NewCounter("logdog/archivist/tasks_acked",
		"Number of tasks successfully completed and acked.",
		nil)
)

// application is the Archivist application state.
type application struct {
	service.Service
}

// runForever runs the archivist loop forever.
func runForever(c context.Context, ar archivist.Archivist) error {
	sleepTime := 1
	client := ar.Service
	// Forever loop.
	for {
		// Only exit out if the context is cancelled (I.E. Ctrl + c).
		if c.Err() != nil {
			return c.Err()
		}
		if err := func() error {
			loop := grabLoopParams()

			// Sizes of archival tasks are pretty uniform. It means the total time it
			// takes to process equally sized batches of tasks is approximately the
			// same across all archivist instances. Once a bunch of them start at the
			// same time, they end up hitting LeaseArchiveTasks in waves at
			// approximately the same time. Randomize the batch size +-25% to remove
			// this synchronization.
			factor := int64(mathrand.Intn(c, 500) + 750) // [750, 1250)
			loop.batchSize = loop.batchSize * factor / 1000
			loop.leaseTime = loop.leaseTime * time.Duration(factor) / 1000

			cycleStartTime := clock.Now(c)
			leaseTimeProto := ptypes.DurationProto(loop.leaseTime)
			nc, cancel := context.WithTimeout(c, loop.leaseTime)
			defer cancel()

			logging.Infof(c, "Leasing max %d tasks for %s", loop.batchSize, loop.leaseTime)
			tasks, err := client.LeaseArchiveTasks(c, &logdog.LeaseRequest{
				MaxTasks:  loop.batchSize,
				LeaseTime: leaseTimeProto,
			})
			if err != nil {
				return err
			}
			logging.Fields{
				"cycleStart": true,
				"numLeased":  len(tasks.Tasks),
				"noWork":     len(tasks.Tasks) == 0,
			}.Infof(c, "Start of cycled, leased %d tasks", len(tasks.Tasks))
			tsLeaseCount.Add(c, int64(len(tasks.Tasks)))
			if len(tasks.Tasks) == 0 {
				return errNoWorkToDo
			}
			merr := errors.NewLazyMultiError(len(tasks.Tasks))
			ackTasks := make([]*logdog.ArchiveTask, 0, len(tasks.Tasks))
			for i, task := range tasks.Tasks {
				deleteTask := false
				startTime := clock.Now(c)
				if err := ar.ArchiveTask(nc, task); err != nil {
					merr.Assign(i, err)
				} else { // err == nil
					ackTasks = append(ackTasks, task)
					deleteTask = true
				}
				duration := clock.Now(c).Sub(startTime)
				tsTaskProcessingTime.Add(c, float64(duration.Nanoseconds())/1000000, deleteTask)
			}
			tsNackCount.Add(c, int64(len(tasks.Tasks)-len(ackTasks)))
			// Ack the successful tasks.  We log the error here since there's nothing we can do.
			if len(ackTasks) > 0 {
				tsAckCount.Add(c, int64(len(ackTasks)))
				c := log.SetFields(c, log.Fields{"Tasks": ackTasks})
				if _, err := client.DeleteArchiveTasks(c, &logdog.DeleteRequest{Tasks: ackTasks}); err != nil {
					log.WithError(err).Errorf(c, "error while acking tasks (%s)", ackTasks)
				}
			}

			duration := clock.Now(c).Sub(cycleStartTime)
			logging.Fields{
				"cycleEnd":     true,
				"numLeased":    len(tasks.Tasks),
				"numCompleted": len(ackTasks),
				"timeSpentSec": duration.Seconds(),
			}.Infof(c, "Done archiving %d items (%d successful) took %.2fs", len(tasks.Tasks), len(ackTasks), duration.Seconds())
			tsLoopCycleTime.Add(c, float64(duration.Nanoseconds()/1000000))
			return merr.Get()
		}(); err != nil {
			// Back off on errors.
			sleepTime *= 2
			if sleepTime > maxSleepTime {
				sleepTime = maxSleepTime
			}
			switch err {
			case errNoWorkToDo:
				logging.Infof(c, "no work to do, sleeping for %d seconds", sleepTime)
			default:
				logging.WithError(err).Errorf(c, "got error in loop, sleeping for %d seconds", sleepTime)
			}
			clock.Sleep(c, time.Duration(sleepTime)*time.Second)
		} else {
			sleepTime = 1
		}
	}
}

// grabLoopParams returns a copy of most recent value of `loop`.
func grabLoopParams() loopParams {
	loopM.Lock()
	defer loopM.Unlock()
	return loop
}

// fetchLoopParams updates `loop` based on settings in datastore (or panics).
func fetchLoopParams(c context.Context) {
	// Note: these are eventually controlled through /admin/portal/archivist.
	set := coordinator.GetSettings(c)
	loopM.Lock()
	defer loopM.Unlock()
	if set.ArchivistBatchSize != 0 {
		loop.batchSize = set.ArchivistBatchSize
	}
	if set.ArchivistLeaseTime != 0 {
		loop.leaseTime = set.ArchivistLeaseTime
	}
	logging.Debugf(c, "loop settings: batchSize:%d, leastTime:%s", loop.batchSize, loop.leaseTime)
}

// loopParamsUpdater updates loopParams based on setting in datastore every once
// in a while.
func loopParamsUpdater(c context.Context) {
	for {
		clock.Sleep(c, 5*time.Minute)
		fetchLoopParams(c)
	}
}

// run is the main execution function.
func (a *application) runArchivist(c context.Context) error {
	cfg := a.ServiceConfig()

	coordCfg, acfg := cfg.GetCoordinator(), cfg.GetArchivist()
	switch {
	case coordCfg == nil:
		fallthrough

	case acfg == nil:
		return errors.New("missing required config: archivist")
	case acfg.GsStagingBucket == "":
		return errors.New("missing required config: archivist.gs_staging_bucket")
	}

	// Initialize our Storage.
	//
	// NOTE: We're requesting read/write access even though we only need read-only
	// access because GKE doesn't understand the read-only scope:
	// https://www.googleapis.com/auth/bigtable.readonly
	st, err := a.IntermediateStorage(c, true)
	if err != nil {
		log.WithError(err).Errorf(c, "Failed to get storage instance.")
		return err
	}
	defer st.Close()

	// Defines our Google Storage client project scoped factory.
	gsClientFactory := func(ctx context.Context, project string) (gs.Client, error) {
		gsClient, err := a.GSClient(ctx, project)
		if err != nil {
			log.WithError(err).Errorf(c, "Failed to get Google Storage client.")
			return nil, err
		}
		return gsClient, nil
	}

	// Initialize a Coordinator client that bundles requests together.
	coordClient := &bundleServicesClient.Client{
		ServicesClient:       a.Coordinator(),
		DelayThreshold:       time.Second,
		BundleCountThreshold: 100,
	}
	defer coordClient.Flush()

	ar := archivist.Archivist{
		Service:         coordClient,
		SettingsLoader:  a.GetSettingsLoader(acfg),
		Storage:         st,
		GSClientFactory: gsClientFactory,
	}

	// Application shutdown will now operate by stopping the Iterator.
	c, cancelFunc := context.WithCancel(c)
	defer cancelFunc()

	// Application shutdown will now operate by cancelling the Archivist's
	// shutdown Context.
	a.SetShutdownFunc(cancelFunc)

	// Load our settings and update them periodically.
	fetchLoopParams(c)
	go loopParamsUpdater(c)

	return runForever(c, ar)
}

// GetSettingsLoader is an archivist.SettingsLoader implementation that merges
// global and project-specific settings.
//
// The resulting settings object will be verified by the Archivist.
func (a *application) GetSettingsLoader(acfg *svcconfig.Archivist) archivist.SettingsLoader {
	serviceID := a.ServiceID()

	return func(c context.Context, project string) (*archivist.Settings, error) {
		// Fold in our project-specific configuration, if valid.
		pcfg, err := a.ProjectConfig(c, project)
		if err != nil {
			log.Fields{
				log.ErrorKey: err,
				"project":    project,
			}.Errorf(c, "Failed to fetch project configuration.")
			return nil, err
		}

		indexParam := func(get func(ic *svcconfig.ArchiveIndexConfig) int32) int {
			if ic := pcfg.ArchiveIndexConfig; ic != nil {
				if v := get(ic); v > 0 {
					return int(v)
				}
			}

			if ic := acfg.ArchiveIndexConfig; ic != nil {
				if v := get(ic); v > 0 {
					return int(v)
				}
			}

			return 0
		}

		// Load our base settings.
		//
		// Archival bases are:
		// Staging: gs://<services:gs_staging_bucket>/<project-id>/...
		// Archive: gs://<project:archive_gs_bucket>/<project-id>/...
		st := archivist.Settings{
			GSBase:        gs.MakePath(pcfg.ArchiveGsBucket, "").Concat(serviceID),
			GSStagingBase: gs.MakePath(acfg.GsStagingBucket, "").Concat(serviceID),

			IndexStreamRange: indexParam(func(ic *svcconfig.ArchiveIndexConfig) int32 { return ic.StreamRange }),
			IndexPrefixRange: indexParam(func(ic *svcconfig.ArchiveIndexConfig) int32 { return ic.PrefixRange }),
			IndexByteRange:   indexParam(func(ic *svcconfig.ArchiveIndexConfig) int32 { return ic.ByteRange }),
		}

		// Fold project settings into loaded ones.
		return &st, nil
	}
}

// Entry point.
func main() {
	mathrand.SeedRandomly()
	a := application{
		Service: service.Service{
			Name:               "archivist",
			DefaultAuthOptions: chromeinfra.DefaultAuthOptions(),
		},
	}
	a.Run(context.Background(), a.runArchivist)
}
<commit_msg>[archivist] Don't busy loop in loopParamsUpdater when shutting down.<commit_after>// Copyright 2016 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 main

import (
	"context"
	"sync"
	"time"

	"github.com/golang/protobuf/ptypes"
	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/gcloud/gs"
	"go.chromium.org/luci/common/logging"
	log "go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/tsmon/distribution"
	"go.chromium.org/luci/common/tsmon/field"
	"go.chromium.org/luci/common/tsmon/metric"
	montypes "go.chromium.org/luci/common/tsmon/types"
	"go.chromium.org/luci/logdog/api/config/svcconfig"
	logdog "go.chromium.org/luci/logdog/api/endpoints/coordinator/services/v1"
	"go.chromium.org/luci/logdog/appengine/coordinator"
	"go.chromium.org/luci/logdog/server/archivist"
	"go.chromium.org/luci/logdog/server/bundleServicesClient"
	"go.chromium.org/luci/logdog/server/service"

	"go.chromium.org/luci/hardcoded/chromeinfra"
)

// loopParams control the outer archivist loop.
type loopParams struct {
	batchSize int64
	leaseTime time.Duration
}

var (
	errInvalidConfig = errors.New("invalid configuration")
	errNoWorkToDo    = errors.New("no work to do")

	// Note: these initial values are mostly bogus, they are replaced by
	// fetchLoopParams before the loop starts.
	loop = loopParams{
		// batchSize is the number of jobs to lease from taskqueue per cycle.
		//
		// TaskQueue has a limit of 10qps for leasing tasks, so the batch size must
		// be set to:
		// batchSize * 10 > (max expected stream creation QPS)
		//
		// In 2020, max stream QPS is approximately 1000 QPS
		batchSize: 500,

		// leaseTime is the amount of time to to lease the batch of tasks for.
		//
		// We need:
		// (time to process avg stream) * batchSize < leaseTime
		//
		// As of 2020, 90th percentile process time per stream is ~5s, 95th
		// percentile of the loop time is 25m.
		leaseTime: 40 * time.Minute,
	}

	// loopM protects 'loop'.
	loopM = sync.Mutex{}

	// maxSleepTime is the max amount of time to sleep in-between errors, in seconds.
	maxSleepTime = 32

	// tsTaskProcessingTime measures the amount of time spent processing a single
	// task.
	//
	// The "consumed" field is true if the underlying task was consumed and
	// false if it was not.
	tsTaskProcessingTime = metric.NewCumulativeDistribution("logdog/archivist/task_processing_time_ms_ng",
		"The amount of time (in milliseconds) that a single task takes to process in the new pipeline.",
		&montypes.MetricMetadata{Units: montypes.Milliseconds},
		distribution.DefaultBucketer,
		field.Bool("consumed"))

	tsLoopCycleTime = metric.NewCumulativeDistribution("logdog/archivist/loop_cycle_time_ms",
		"The amount of time a single batch of leases takes to process.",
		&montypes.MetricMetadata{Units: montypes.Milliseconds},
		distribution.DefaultBucketer)

	tsLeaseCount = metric.NewCounter("logdog/archivist/tasks_leased",
		"Number of tasks leased.",
		nil)

	tsNackCount = metric.NewCounter("logdog/archivist/tasks_not_acked",
		"Number of tasks leased but failed.",
		nil)

	tsAckCount = metric.NewCounter("logdog/archivist/tasks_acked",
		"Number of tasks successfully completed and acked.",
		nil)
)

// application is the Archivist application state.
type application struct {
	service.Service
}

// runForever runs the archivist loop forever.
func runForever(c context.Context, ar archivist.Archivist) error {
	sleepTime := 1
	client := ar.Service
	// Forever loop.
	for {
		// Only exit out if the context is cancelled (I.E. Ctrl + c).
		if c.Err() != nil {
			return c.Err()
		}
		if err := func() error {
			loop := grabLoopParams()

			// Sizes of archival tasks are pretty uniform. It means the total time it
			// takes to process equally sized batches of tasks is approximately the
			// same across all archivist instances. Once a bunch of them start at the
			// same time, they end up hitting LeaseArchiveTasks in waves at
			// approximately the same time. Randomize the batch size +-25% to remove
			// this synchronization.
			factor := int64(mathrand.Intn(c, 500) + 750) // [750, 1250)
			loop.batchSize = loop.batchSize * factor / 1000
			loop.leaseTime = loop.leaseTime * time.Duration(factor) / 1000

			cycleStartTime := clock.Now(c)
			leaseTimeProto := ptypes.DurationProto(loop.leaseTime)
			nc, cancel := context.WithTimeout(c, loop.leaseTime)
			defer cancel()

			logging.Infof(c, "Leasing max %d tasks for %s", loop.batchSize, loop.leaseTime)
			tasks, err := client.LeaseArchiveTasks(c, &logdog.LeaseRequest{
				MaxTasks:  loop.batchSize,
				LeaseTime: leaseTimeProto,
			})
			if err != nil {
				return err
			}
			logging.Fields{
				"cycleStart": true,
				"numLeased":  len(tasks.Tasks),
				"noWork":     len(tasks.Tasks) == 0,
			}.Infof(c, "Start of cycled, leased %d tasks", len(tasks.Tasks))
			tsLeaseCount.Add(c, int64(len(tasks.Tasks)))
			if len(tasks.Tasks) == 0 {
				return errNoWorkToDo
			}
			merr := errors.NewLazyMultiError(len(tasks.Tasks))
			ackTasks := make([]*logdog.ArchiveTask, 0, len(tasks.Tasks))
			for i, task := range tasks.Tasks {
				deleteTask := false
				startTime := clock.Now(c)
				if err := ar.ArchiveTask(nc, task); err != nil {
					merr.Assign(i, err)
				} else { // err == nil
					ackTasks = append(ackTasks, task)
					deleteTask = true
				}
				duration := clock.Now(c).Sub(startTime)
				tsTaskProcessingTime.Add(c, float64(duration.Nanoseconds())/1000000, deleteTask)
			}
			tsNackCount.Add(c, int64(len(tasks.Tasks)-len(ackTasks)))
			// Ack the successful tasks.  We log the error here since there's nothing we can do.
			if len(ackTasks) > 0 {
				tsAckCount.Add(c, int64(len(ackTasks)))
				c := log.SetFields(c, log.Fields{"Tasks": ackTasks})
				if _, err := client.DeleteArchiveTasks(c, &logdog.DeleteRequest{Tasks: ackTasks}); err != nil {
					log.WithError(err).Errorf(c, "error while acking tasks (%s)", ackTasks)
				}
			}

			duration := clock.Now(c).Sub(cycleStartTime)
			logging.Fields{
				"cycleEnd":     true,
				"numLeased":    len(tasks.Tasks),
				"numCompleted": len(ackTasks),
				"timeSpentSec": duration.Seconds(),
			}.Infof(c, "Done archiving %d items (%d successful) took %.2fs", len(tasks.Tasks), len(ackTasks), duration.Seconds())
			tsLoopCycleTime.Add(c, float64(duration.Nanoseconds()/1000000))
			return merr.Get()
		}(); err != nil {
			// Back off on errors.
			sleepTime *= 2
			if sleepTime > maxSleepTime {
				sleepTime = maxSleepTime
			}
			switch err {
			case errNoWorkToDo:
				logging.Infof(c, "no work to do, sleeping for %d seconds", sleepTime)
			default:
				logging.WithError(err).Errorf(c, "got error in loop, sleeping for %d seconds", sleepTime)
			}
			clock.Sleep(c, time.Duration(sleepTime)*time.Second)
		} else {
			sleepTime = 1
		}
	}
}

// grabLoopParams returns a copy of most recent value of `loop`.
func grabLoopParams() loopParams {
	loopM.Lock()
	defer loopM.Unlock()
	return loop
}

// fetchLoopParams updates `loop` based on settings in datastore (or panics).
func fetchLoopParams(c context.Context) {
	// Note: these are eventually controlled through /admin/portal/archivist.
	set := coordinator.GetSettings(c)
	loopM.Lock()
	defer loopM.Unlock()
	if set.ArchivistBatchSize != 0 {
		loop.batchSize = set.ArchivistBatchSize
	}
	if set.ArchivistLeaseTime != 0 {
		loop.leaseTime = set.ArchivistLeaseTime
	}
	logging.Debugf(c, "loop settings: batchSize:%d, leastTime:%s", loop.batchSize, loop.leaseTime)
}

// loopParamsUpdater updates loopParams based on setting in datastore every once
// in a while.
func loopParamsUpdater(c context.Context) {
	for {
		if clock.Sleep(c, 5*time.Minute).Err != nil {
			break //  the context is canceled, the process is exiting
		}
		fetchLoopParams(c)
	}
}

// run is the main execution function.
func (a *application) runArchivist(c context.Context) error {
	cfg := a.ServiceConfig()

	coordCfg, acfg := cfg.GetCoordinator(), cfg.GetArchivist()
	switch {
	case coordCfg == nil:
		fallthrough

	case acfg == nil:
		return errors.New("missing required config: archivist")
	case acfg.GsStagingBucket == "":
		return errors.New("missing required config: archivist.gs_staging_bucket")
	}

	// Initialize our Storage.
	//
	// NOTE: We're requesting read/write access even though we only need read-only
	// access because GKE doesn't understand the read-only scope:
	// https://www.googleapis.com/auth/bigtable.readonly
	st, err := a.IntermediateStorage(c, true)
	if err != nil {
		log.WithError(err).Errorf(c, "Failed to get storage instance.")
		return err
	}
	defer st.Close()

	// Defines our Google Storage client project scoped factory.
	gsClientFactory := func(ctx context.Context, project string) (gs.Client, error) {
		gsClient, err := a.GSClient(ctx, project)
		if err != nil {
			log.WithError(err).Errorf(c, "Failed to get Google Storage client.")
			return nil, err
		}
		return gsClient, nil
	}

	// Initialize a Coordinator client that bundles requests together.
	coordClient := &bundleServicesClient.Client{
		ServicesClient:       a.Coordinator(),
		DelayThreshold:       time.Second,
		BundleCountThreshold: 100,
	}
	defer coordClient.Flush()

	ar := archivist.Archivist{
		Service:         coordClient,
		SettingsLoader:  a.GetSettingsLoader(acfg),
		Storage:         st,
		GSClientFactory: gsClientFactory,
	}

	// Application shutdown will now operate by stopping the Iterator.
	c, cancelFunc := context.WithCancel(c)
	defer cancelFunc()

	// Application shutdown will now operate by cancelling the Archivist's
	// shutdown Context.
	a.SetShutdownFunc(cancelFunc)

	// Load our settings and update them periodically.
	fetchLoopParams(c)
	go loopParamsUpdater(c)

	return runForever(c, ar)
}

// GetSettingsLoader is an archivist.SettingsLoader implementation that merges
// global and project-specific settings.
//
// The resulting settings object will be verified by the Archivist.
func (a *application) GetSettingsLoader(acfg *svcconfig.Archivist) archivist.SettingsLoader {
	serviceID := a.ServiceID()

	return func(c context.Context, project string) (*archivist.Settings, error) {
		// Fold in our project-specific configuration, if valid.
		pcfg, err := a.ProjectConfig(c, project)
		if err != nil {
			log.Fields{
				log.ErrorKey: err,
				"project":    project,
			}.Errorf(c, "Failed to fetch project configuration.")
			return nil, err
		}

		indexParam := func(get func(ic *svcconfig.ArchiveIndexConfig) int32) int {
			if ic := pcfg.ArchiveIndexConfig; ic != nil {
				if v := get(ic); v > 0 {
					return int(v)
				}
			}

			if ic := acfg.ArchiveIndexConfig; ic != nil {
				if v := get(ic); v > 0 {
					return int(v)
				}
			}

			return 0
		}

		// Load our base settings.
		//
		// Archival bases are:
		// Staging: gs://<services:gs_staging_bucket>/<project-id>/...
		// Archive: gs://<project:archive_gs_bucket>/<project-id>/...
		st := archivist.Settings{
			GSBase:        gs.MakePath(pcfg.ArchiveGsBucket, "").Concat(serviceID),
			GSStagingBase: gs.MakePath(acfg.GsStagingBucket, "").Concat(serviceID),

			IndexStreamRange: indexParam(func(ic *svcconfig.ArchiveIndexConfig) int32 { return ic.StreamRange }),
			IndexPrefixRange: indexParam(func(ic *svcconfig.ArchiveIndexConfig) int32 { return ic.PrefixRange }),
			IndexByteRange:   indexParam(func(ic *svcconfig.ArchiveIndexConfig) int32 { return ic.ByteRange }),
		}

		// Fold project settings into loaded ones.
		return &st, nil
	}
}

// Entry point.
func main() {
	mathrand.SeedRandomly()
	a := application{
		Service: service.Service{
			Name:               "archivist",
			DefaultAuthOptions: chromeinfra.DefaultAuthOptions(),
		},
	}
	a.Run(context.Background(), a.runArchivist)
}
<|endoftext|>


<commit_before>// +build windows

package mpmssql

//go:generate wmi2struct -n -p mpmssql -o wmi.go Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSBufferManager Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSSQLStatistics Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSGeneralStatistics Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSAccessMethods

import (
	"flag"
	"os"
	"strings"

	"github.com/StackExchange/wmi"
	mp "github.com/mackerelio/go-mackerel-plugin"
	"github.com/mackerelio/golib/logging"
)

var logger = logging.GetLogger("metrics.plugin.mssql")

// MSSQLPlugin store the name of servers
type MSSQLPlugin struct {
	prefix   string
	instance string
}

// MetricKeyPrefix retruns the metrics key prefix
func (m MSSQLPlugin) MetricKeyPrefix() string {
	if m.prefix == "" {
		m.prefix = "mssql"
	}
	return m.prefix
}

func (m MSSQLPlugin) name(n string) string {
	if m.instance == "SQLSERVER" {
		return "Win32_PerfRawData_MSSQLSERVER_SQLServer" + n
	} else {
		return "Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESS" + n
	}
}

// FetchMetrics interface for mackerelplugin
func (m MSSQLPlugin) FetchMetrics() (map[string]float64, error) {
	var bufferManager []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSBufferManager
	var sqlStatistics []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSSQLStatistics
	var generalStatistics []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSGeneralStatistics
	var accessMethods []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSAccessMethods

	var err error
	err = wmi.Query("select * from "+m.name("BufferManager"), &bufferManager)
	if err != nil {
		return nil, err
	}
	err = wmi.Query("select * from "+m.name("SQLStatistics"), &sqlStatistics)
	if err != nil {
		return nil, err
	}
	err = wmi.Query("select * from "+m.name("GeneralStatistics"), &generalStatistics)
	if err != nil {
		return nil, err
	}
	err = wmi.Query("select * from "+m.name("AccessMethods"), &accessMethods)
	if err != nil {
		return nil, err
	}

	stat := make(map[string]float64)
	stat["buffer_cache_hit_ratio"] = float64(bufferManager[0].Buffercachehitratio)
	stat["buffer_page_life_expectancy"] = float64(bufferManager[0].Pagelifeexpectancy)
	stat["buffer_checkpoint_pages"] = float64(bufferManager[0].CheckpointpagesPersec)
	stat["stats_batch_requests"] = float64(sqlStatistics[0].BatchRequestsPersec)
	stat["stats_sql_compilations"] = float64(sqlStatistics[0].SQLCompilationsPersec)
	stat["stats_sql_recompilations"] = float64(sqlStatistics[0].SQLReCompilationsPersec)
	stat["stats_connections"] = float64(generalStatistics[0].UserConnections)
	stat["stats_lock_waits"] = float64(generalStatistics[0].SQLTraceIOProviderLockWaits)
	stat["stats_procs_blocked"] = float64(generalStatistics[0].Processesblocked)
	stat["access_page_splits"] = float64(accessMethods[0].PageSplitsPersec)
	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m MSSQLPlugin) GraphDefinition() map[string](mp.Graphs) {
	labelPrefix := strings.Title(strings.Replace(m.MetricKeyPrefix(), "mssql", "MSSQL", -1))
	return map[string](mp.Graphs){
		"buffer": mp.Graphs{
			Label: labelPrefix + " Buffer",
			Unit:  mp.UnitInteger,
			Metrics: []mp.Metrics{
				{
					Name:    "buffer_cache_hit_ratio",
					Label:   "Cache Hit Ratio",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "buffer_page_life_expectancy",
					Label:   "Page Life Expectancy",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "buffer_checkpoint_pages",
					Label:   "Checkpoint Pages",
					Diff:    false,
					Stacked: true,
				},
			},
		},
		"stats": mp.Graphs{
			Label: labelPrefix + " Stats",
			Unit:  mp.UnitInteger,
			Metrics: []mp.Metrics{
				{
					Name:    "stats_batch_requests",
					Label:   "Batch Requests",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "stats_sql_compilations",
					Label:   "SQL Compilations",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "stats_sql_recompilations",
					Label:   "SQL Re-Compilations",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "stats_connections",
					Label:   "Connections",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "stats_lock_waits",
					Label:   "Lock Waits",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "stats_procs_blocked",
					Label:   "Procs Blocked",
					Diff:    false,
					Stacked: true,
				},
			},
		},
		"access": mp.Graphs{
			Label: labelPrefix + " Access",
			Unit:  mp.UnitInteger,
			Metrics: []mp.Metrics{
				{
					Name:    "access_page_splits",
					Label:   "Page Splits",
					Diff:    false,
					Stacked: true,
				},
			},
		},
	}
}

// Do the plugin
func Do() {
	optPrefix := flag.String("metric-key-prefix", "mssql", "Metric key prefix")
	optInstance := flag.String("instance", "SQLSERVER", "Instance name of MSSQL(SQLSERVER or SQLEXPRESS)")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	instance := strings.ToUpper(*optInstance)
	if instance != "SQLSERVER" && instance != "SQLEXPRESS" {
		flag.Usage()
		os.Exit(2)
	}
	plugin := MSSQLPlugin{
		prefix:   *optPrefix,
		instance: instance,
	}
	helper := mp.NewMackerelPlugin(plugin)
	helper.Tempfile = *optTempfile
	helper.Run()
}
<commit_msg>Enable Diff<commit_after>// +build windows

package mpmssql

//go:generate wmi2struct -n -p mpmssql -o wmi.go Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSBufferManager Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSSQLStatistics Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSGeneralStatistics Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSAccessMethods

import (
	"flag"
	"os"
	"strings"

	"github.com/StackExchange/wmi"
	mp "github.com/mackerelio/go-mackerel-plugin"
	"github.com/mackerelio/golib/logging"
)

var logger = logging.GetLogger("metrics.plugin.mssql")

// MSSQLPlugin store the name of servers
type MSSQLPlugin struct {
	prefix   string
	instance string
}

// MetricKeyPrefix retruns the metrics key prefix
func (m MSSQLPlugin) MetricKeyPrefix() string {
	if m.prefix == "" {
		m.prefix = "mssql"
	}
	return m.prefix
}

func (m MSSQLPlugin) name(n string) string {
	if m.instance == "SQLSERVER" {
		return "Win32_PerfRawData_MSSQLSERVER_SQLServer" + n
	} else {
		return "Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESS" + n
	}
}

// FetchMetrics interface for mackerelplugin
func (m MSSQLPlugin) FetchMetrics() (map[string]float64, error) {
	var bufferManager []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSBufferManager
	var sqlStatistics []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSSQLStatistics
	var generalStatistics []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSGeneralStatistics
	var accessMethods []Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSAccessMethods

	var err error
	err = wmi.Query("select * from "+m.name("BufferManager"), &bufferManager)
	if err != nil {
		return nil, err
	}
	err = wmi.Query("select * from "+m.name("SQLStatistics"), &sqlStatistics)
	if err != nil {
		return nil, err
	}
	err = wmi.Query("select * from "+m.name("GeneralStatistics"), &generalStatistics)
	if err != nil {
		return nil, err
	}
	err = wmi.Query("select * from "+m.name("AccessMethods"), &accessMethods)
	if err != nil {
		return nil, err
	}

	stat := make(map[string]float64)
	stat["buffer_cache_hit_ratio"] = float64(bufferManager[0].Buffercachehitratio)
	stat["buffer_page_life_expectancy"] = float64(bufferManager[0].Pagelifeexpectancy)
	stat["buffer_checkpoint_pages"] = float64(bufferManager[0].CheckpointpagesPersec)
	stat["stats_batch_requests"] = float64(sqlStatistics[0].BatchRequestsPersec)
	stat["stats_sql_compilations"] = float64(sqlStatistics[0].SQLCompilationsPersec)
	stat["stats_sql_recompilations"] = float64(sqlStatistics[0].SQLReCompilationsPersec)
	stat["stats_connections"] = float64(generalStatistics[0].UserConnections)
	stat["stats_lock_waits"] = float64(generalStatistics[0].SQLTraceIOProviderLockWaits)
	stat["stats_procs_blocked"] = float64(generalStatistics[0].Processesblocked)
	stat["access_page_splits"] = float64(accessMethods[0].PageSplitsPersec)
	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m MSSQLPlugin) GraphDefinition() map[string](mp.Graphs) {
	labelPrefix := strings.Title(strings.Replace(m.MetricKeyPrefix(), "mssql", "MSSQL", -1))
	return map[string](mp.Graphs){
		"buffer": mp.Graphs{
			Label: labelPrefix + " Buffer",
			Unit:  mp.UnitInteger,
			Metrics: []mp.Metrics{
				{
					Name:    "buffer_cache_hit_ratio",
					Label:   "Cache Hit Ratio",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "buffer_page_life_expectancy",
					Label:   "Page Life Expectancy",
					Diff:    true,
					Stacked: true,
				},
				{
					Name:    "buffer_checkpoint_pages",
					Label:   "Checkpoint Pages",
					Diff:    false,
					Stacked: true,
				},
			},
		},
		"stats": mp.Graphs{
			Label: labelPrefix + " Stats",
			Unit:  mp.UnitInteger,
			Metrics: []mp.Metrics{
				{
					Name:    "stats_batch_requests",
					Label:   "Batch Requests",
					Diff:    true,
					Stacked: true,
				},
				{
					Name:    "stats_sql_compilations",
					Label:   "SQL Compilations",
					Diff:    true,
					Stacked: true,
				},
				{
					Name:    "stats_sql_recompilations",
					Label:   "SQL Re-Compilations",
					Diff:    true,
					Stacked: true,
				},
				{
					Name:    "stats_connections",
					Label:   "Connections",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "stats_lock_waits",
					Label:   "Lock Waits",
					Diff:    false,
					Stacked: true,
				},
				{
					Name:    "stats_procs_blocked",
					Label:   "Procs Blocked",
					Diff:    false,
					Stacked: true,
				},
			},
		},
		"access": mp.Graphs{
			Label: labelPrefix + " Access",
			Unit:  mp.UnitInteger,
			Metrics: []mp.Metrics{
				{
					Name:    "access_page_splits",
					Label:   "Page Splits",
					Diff:    true,
					Stacked: true,
				},
			},
		},
	}
}

// Do the plugin
func Do() {
	optPrefix := flag.String("metric-key-prefix", "mssql", "Metric key prefix")
	optInstance := flag.String("instance", "SQLSERVER", "Instance name of MSSQL(SQLSERVER or SQLEXPRESS)")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	instance := strings.ToUpper(*optInstance)
	if instance != "SQLSERVER" && instance != "SQLEXPRESS" {
		flag.Usage()
		os.Exit(2)
	}
	plugin := MSSQLPlugin{
		prefix:   *optPrefix,
		instance: instance,
	}
	helper := mp.NewMackerelPlugin(plugin)
	helper.Tempfile = *optTempfile
	helper.Run()
}
<|endoftext|>


<commit_before>package typescriptify

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"strings"
	"testing"
	"time"
)

type Address struct {
	// Used in html
	Duration float64 `json:"duration"`
	Text1    string  `json:"text,omitempty"`
	// Ignored:
	Text2 string `json:",omitempty"`
	Text3 string `json:"-"`
}

type Dummy struct {
	Something string `json:"something"`
}

type HasName struct {
	Name string `json:"name"`
}

type Person struct {
	HasName
	Nicknames []string  `json:"nicknames"`
	Addresses []Address `json:"addresses"`
	Address   *Address  `json:"address"`
	Metadata  []byte    `json:"metadata" ts_type:"{[key:string]:string}"`
	Friends   []*Person `json:"friends"`
	Dummy     Dummy     `json:"a"`
}

func TestTypescriptifyWithTypes(t *testing.T) {
	converter := New()

	converter.AddType(reflect.TypeOf(Person{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class Dummy {
        something: string;
}
export class Address {
        duration: number;
        text: string;
}
export class Person {
        name: string;
        nicknames: string[];
		addresses: Address[];
		address: Address;
		metadata: {[key:string]:string};
		friends: Person[];
        a: Dummy;
}`
	testConverter(t, converter, desiredResult)
}

func TestTypescriptifyWithInstances(t *testing.T) {
	converter := New()

	converter.Add(Person{})
	converter.Add(Dummy{})
	converter.CreateFromMethod = false
	converter.DontExport = true
	converter.BackupDir = ""

	desiredResult := `class Dummy {
        something: string;
}
class Address {
        duration: number;
        text: string;
}
class Person {
        name: string;
        nicknames: string[];
		addresses: Address[];
		address: Address;
		metadata: {[key:string]:string};
		friends: Person[];
        a: Dummy;
}`
	testConverter(t, converter, desiredResult)
}

func TestTypescriptifyWithDoubleClasses(t *testing.T) {
	converter := New()

	converter.AddType(reflect.TypeOf(Person{}))
	converter.AddType(reflect.TypeOf(Person{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class Dummy {
        something: string;
}
export class Address {
        duration: number;
        text: string;
}
export class Person {
        name: string;
		nicknames: string[];
		addresses: Address[];
		address: Address;
		metadata: {[key:string]:string};
		friends: Person[];
        a: Dummy;
}`
	testConverter(t, converter, desiredResult)
}

func TestWithPrefixes(t *testing.T) {
	converter := New()

	converter.Prefix = "test_"
	converter.Suffix = "_test"

	converter.Add(Person{})
	converter.CreateFromMethod = false
	converter.DontExport = true
	converter.BackupDir = ""

	desiredResult := `class test_Dummy_test {
    something: string;
}
class test_Address_test {
    duration: number;
    text: string;
}
class test_Person_test {
    name: string;
    nicknames: string[];
    addresses: test_Address_test[];
    address: test_Address_test;
    metadata: {[key:string]:string};
    friends: test_Person_test[];
    a: test_Dummy_test;
}`
	testConverter(t, converter, desiredResult)
}

func testConverter(t *testing.T, converter *TypeScriptify, desiredResult string) {
	typeScriptCode, err := converter.Convert(nil)
	if err != nil {
		panic(err.Error())
	}

	typeScriptCode = strings.Trim(typeScriptCode, " \t\n\r")
	if typeScriptCode != desiredResult {
		lines1 := strings.Split(typeScriptCode, "\n")
		lines2 := strings.Split(desiredResult, "\n")

		if len(lines1) != len(lines2) {
			os.Stderr.WriteString(fmt.Sprintf("Lines: %d != %d\n", len(lines1), len(lines2)))
			os.Stderr.WriteString(fmt.Sprintf("Expected:\n%s\n\nGot:\n%s\n", desiredResult, typeScriptCode))
			t.Fail()
		} else {
			for i := 0; i < len(lines1); i++ {
				line1 := strings.Trim(lines1[i], " \t\r\n")
				line2 := strings.Trim(lines2[i], " \t\r\n")
				if line1 != line2 {
					os.Stderr.WriteString(fmt.Sprintf("%d. line don't match: `%s` != `%s`\n", i+1, line1, line2))
					os.Stderr.WriteString(fmt.Sprintf("Expected:\n%s\n\nGot:\n%s\n", desiredResult, typeScriptCode))
					t.Fail()
				}
			}
		}
	}
}

func TestTypescriptifyCustomType(t *testing.T) {
	type TestCustomType struct {
		Map map[string]int `json:"map" ts_type:"{[key: string]: number}"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(TestCustomType{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class TestCustomType {
        map: {[key: string]: number};
}`
	testConverter(t, converter, desiredResult)
}

func TestDate(t *testing.T) {
	type TestCustomType struct {
		Time time.Time `json:"time" ts_type:"Date" ts_transform:"new Date(__VALUE__)"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(TestCustomType{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class TestCustomType {
    time: Date;

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new TestCustomType();
        result.time = new Date(source["time"]);
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

func TestRecursive(t *testing.T) {
	type Test struct {
		Children []Test `json:"children"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Test{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class Test {
    children: Test[];

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Test();
        result.children = source["children"] ? source["children"].map(function(element: any) { return Test.createFrom(element); }) : null;
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

func TestArrayOfArrays(t *testing.T) {
	type Key struct {
		Key string `json:"key"`
	}
	type Keyboard struct {
		Keys [][]Key `json:"keys"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Keyboard{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class Key {
    key: string;

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Key();
        result.key = source["key"];
        return result;
    }

}
export class Keyboard {
    keys: Key[][];

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Keyboard();
        result.keys = source["keys"] ? source["keys"].map(function(element: any) { return Key.createFrom(element); }) : null;
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

func TestAny(t *testing.T) {
	type Test struct {
		Any interface{} `json:"field"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Test{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class Test {
    field: any;

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Test();
        result.field = source["field"];
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

type NumberTime time.Time

func (t NumberTime) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("%d", time.Time(t).Unix())), nil
}

func TestTypeAlias(t *testing.T) {
	type Person struct {
		Birth NumberTime `json:"birth" ts_type:"number"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Person{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class Person {
    birth: number;
}`
	testConverter(t, converter, desiredResult)
}

type MSTime struct {
	time.Time
}

func (MSTime) UnmarshalJSON([]byte) error   { return nil }
func (MSTime) MarshalJSON() ([]byte, error) { return []byte("1111"), nil }

func TestOverrideCustomType(t *testing.T) {

	type SomeStruct struct {
		Time MSTime `json:"time" ts_type:"number"`
	}
	var _ json.Marshaler = new(MSTime)
	var _ json.Unmarshaler = new(MSTime)

	converter := New()

	converter.AddType(reflect.TypeOf(SomeStruct{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class SomeStruct {
    time: number;
}`
	testConverter(t, converter, desiredResult)

	byts, _ := json.Marshal(SomeStruct{Time: MSTime{Time: time.Now()}})
	if string(byts) != `{"time":1111}` {
		t.Error("marhshalling failed")
	}
}
<commit_msg>Better error msg<commit_after>package typescriptify

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"strings"
	"testing"
	"time"
)

type Address struct {
	// Used in html
	Duration float64 `json:"duration"`
	Text1    string  `json:"text,omitempty"`
	// Ignored:
	Text2 string `json:",omitempty"`
	Text3 string `json:"-"`
}

type Dummy struct {
	Something string `json:"something"`
}

type HasName struct {
	Name string `json:"name"`
}

type Person struct {
	HasName
	Nicknames []string  `json:"nicknames"`
	Addresses []Address `json:"addresses"`
	Address   *Address  `json:"address"`
	Metadata  []byte    `json:"metadata" ts_type:"{[key:string]:string}"`
	Friends   []*Person `json:"friends"`
	Dummy     Dummy     `json:"a"`
}

func TestTypescriptifyWithTypes(t *testing.T) {
	converter := New()

	converter.AddType(reflect.TypeOf(Person{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class Dummy {
        something: string;
}
export class Address {
        duration: number;
        text: string;
}
export class Person {
        name: string;
        nicknames: string[];
		addresses: Address[];
		address: Address;
		metadata: {[key:string]:string};
		friends: Person[];
        a: Dummy;
}`
	testConverter(t, converter, desiredResult)
}

func TestTypescriptifyWithInstances(t *testing.T) {
	converter := New()

	converter.Add(Person{})
	converter.Add(Dummy{})
	converter.CreateFromMethod = false
	converter.DontExport = true
	converter.BackupDir = ""

	desiredResult := `class Dummy {
        something: string;
}
class Address {
        duration: number;
        text: string;
}
class Person {
        name: string;
        nicknames: string[];
		addresses: Address[];
		address: Address;
		metadata: {[key:string]:string};
		friends: Person[];
        a: Dummy;
}`
	testConverter(t, converter, desiredResult)
}

func TestTypescriptifyWithDoubleClasses(t *testing.T) {
	converter := New()

	converter.AddType(reflect.TypeOf(Person{}))
	converter.AddType(reflect.TypeOf(Person{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class Dummy {
        something: string;
}
export class Address {
        duration: number;
        text: string;
}
export class Person {
        name: string;
		nicknames: string[];
		addresses: Address[];
		address: Address;
		metadata: {[key:string]:string};
		friends: Person[];
        a: Dummy;
}`
	testConverter(t, converter, desiredResult)
}

func TestWithPrefixes(t *testing.T) {
	converter := New()

	converter.Prefix = "test_"
	converter.Suffix = "_test"

	converter.Add(Person{})
	converter.CreateFromMethod = false
	converter.DontExport = true
	converter.BackupDir = ""

	desiredResult := `class test_Dummy_test {
    something: string;
}
class test_Address_test {
    duration: number;
    text: string;
}
class test_Person_test {
    name: string;
    nicknames: string[];
    addresses: test_Address_test[];
    address: test_Address_test;
    metadata: {[key:string]:string};
    friends: test_Person_test[];
    a: test_Dummy_test;
}`
	testConverter(t, converter, desiredResult)
}

func testConverter(t *testing.T, converter *TypeScriptify, desiredResult string) {
	typeScriptCode, err := converter.Convert(nil)
	if err != nil {
		panic(err.Error())
	}

	desiredResult = strings.TrimSpace(desiredResult)
	typeScriptCode = strings.Trim(typeScriptCode, " \t\n\r")
	if typeScriptCode != desiredResult {
		gotLines1 := strings.Split(typeScriptCode, "\n")
		expectedLines2 := strings.Split(desiredResult, "\n")

		max := len(gotLines1)
		if len(expectedLines2) > max {
			max = len(expectedLines2)
		}

		for i := 0; i < max; i++ {
			var gotLine, expectedLine string
			if i < len(gotLines1) {
				gotLine = gotLines1[i]
			}
			if i < len(expectedLines2) {
				expectedLine = expectedLines2[i]
			}
			if strings.TrimSpace(gotLine) == strings.TrimSpace(expectedLine) {
				fmt.Printf("OK:       %s\n", gotLine)
			} else {
				fmt.Printf("GOT:      %s\n", gotLine)
				fmt.Printf("EXPECTED: %s\n", expectedLine)
				t.Fail()
			}
		}
	}
}

func TestTypescriptifyCustomType(t *testing.T) {
	type TestCustomType struct {
		Map map[string]int `json:"map" ts_type:"{[key: string]: number}"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(TestCustomType{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class TestCustomType {
        map: {[key: string]: number};
}`
	testConverter(t, converter, desiredResult)
}

func TestDate(t *testing.T) {
	type TestCustomType struct {
		Time time.Time `json:"time" ts_type:"Date" ts_transform:"new Date(__VALUE__)"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(TestCustomType{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class TestCustomType {
    time: Date;

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new TestCustomType();
        result.time = new Date(source["time"]);
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

func TestRecursive(t *testing.T) {
	type Test struct {
		Children []Test `json:"children"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Test{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class Test {
    children: Test[];

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Test();
        result.children = source["children"] ? source["children"].map(function(element: any) { return Test.createFrom(element); }) : null;
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

func TestArrayOfArrays(t *testing.T) {
	type Key struct {
		Key string `json:"key"`
	}
	type Keyboard struct {
		Keys [][]Key `json:"keys"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Keyboard{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class Key {
    key: string;

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Key();
        result.key = source["key"];
        return result;
    }

}
export class Keyboard {
    keys: Key[][];

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Keyboard();
        result.keys = source["keys"] ? source["keys"].map(function(element: any) { return Key.createFrom(element); }) : null;
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

func TestAny(t *testing.T) {
	type Test struct {
		Any interface{} `json:"field"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Test{}))
	converter.CreateFromMethod = true
	converter.BackupDir = ""

	desiredResult := `export class Test {
    field: any;

    static createFrom(source: any) {
        if ('string' === typeof source) source = JSON.parse(source);
        const result = new Test();
        result.field = source["field"];
        return result;
    }

}`
	testConverter(t, converter, desiredResult)
}

type NumberTime time.Time

func (t NumberTime) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("%d", time.Time(t).Unix())), nil
}

func TestTypeAlias(t *testing.T) {
	type Person struct {
		Birth NumberTime `json:"birth" ts_type:"number"`
	}

	converter := New()

	converter.AddType(reflect.TypeOf(Person{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class Person {
    birth: number;
}`
	testConverter(t, converter, desiredResult)
}

type MSTime struct {
	time.Time
}

func (MSTime) UnmarshalJSON([]byte) error   { return nil }
func (MSTime) MarshalJSON() ([]byte, error) { return []byte("1111"), nil }

func TestOverrideCustomType(t *testing.T) {

	type SomeStruct struct {
		Time MSTime `json:"time" ts_type:"number"`
	}
	var _ json.Marshaler = new(MSTime)
	var _ json.Unmarshaler = new(MSTime)

	converter := New()

	converter.AddType(reflect.TypeOf(SomeStruct{}))
	converter.CreateFromMethod = false
	converter.BackupDir = ""

	desiredResult := `export class SomeStruct {
    time: number;
}`
	testConverter(t, converter, desiredResult)

	byts, _ := json.Marshal(SomeStruct{Time: MSTime{Time: time.Now()}})
	if string(byts) != `{"time":1111}` {
		t.Error("marhshalling failed")
	}
}
<|endoftext|>