<commit_before>package graphql

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"sync"

	"github.com/samsarahq/thunder/concurrencylimiter"
	"github.com/samsarahq/thunder/reactive"
)

type pathError struct {
	inner error
	path  []string
}

func nestPathErrorMulti(path []string, err error) error {
	// Don't nest SanitzedError's, as they are intended for human consumption.
	if se, ok := err.(SanitizedError); ok {
		return se
	}

	if pe, ok := err.(*pathError); ok {
		return &pathError{
			inner: pe.inner,
			path:  append(pe.path, path...),
		}
	}

	return &pathError{
		inner: err,
		path:  path,
	}
}

func nestPathError(key string, err error) error {
	// Don't nest SanitzedError's, as they are intended for human consumption.
	if se, ok := err.(SanitizedError); ok {
		return se
	}

	if pe, ok := err.(*pathError); ok {
		return &pathError{
			inner: pe.inner,
			path:  append(pe.path, key),
		}
	}

	return &pathError{
		inner: err,
		path:  []string{key},
	}
}

func ErrorCause(err error) error {
	if pe, ok := err.(*pathError); ok {
		return pe.inner
	}
	return err
}

func (pe *pathError) Error() string {
	var buffer bytes.Buffer
	for i := len(pe.path) - 1; i >= 0; i-- {
		if i < len(pe.path)-1 {
			buffer.WriteString(".")
		}
		buffer.WriteString(pe.path[i])
	}
	buffer.WriteString(": ")
	buffer.WriteString(pe.inner.Error())
	return buffer.String()
}

func isNilArgs(args interface{}) bool {
	m, ok := args.(map[string]interface{})
	return args == nil || (ok && len(m) == 0)
}

// unwrap will return the value associated with a pointer type, or nil if the
// pointer is nil
func unwrap(v interface{}) interface{} {
	i := reflect.ValueOf(v)
	for i.Kind() == reflect.Ptr && !i.IsNil() {
		i = i.Elem()
	}
	if i.Kind() == reflect.Invalid {
		return nil
	}
	return i.Interface()
}

// PrepareQuery checks that the given selectionSet matches the schema typ, and
// parses the args in selectionSet
func PrepareQuery(typ Type, selectionSet *SelectionSet) error {
	switch typ := typ.(type) {
	case *Scalar:
		if selectionSet != nil {
			return NewClientError("scalar field must have no selections")
		}
		return nil
	case *Enum:
		if selectionSet != nil {
			return NewClientError("enum field must have no selections")
		}
		return nil
	case *Union:
		if selectionSet == nil {
			return NewClientError("object field must have selections")
		}

		for _, fragment := range selectionSet.Fragments {
			for typString, graphqlTyp := range typ.Types {
				if fragment.On != typString {
					continue
				}
				if err := PrepareQuery(graphqlTyp, fragment.SelectionSet); err != nil {
					return err
				}
			}
		}
		for _, selection := range selectionSet.Selections {
			if selection.Name == "__typename" {
				if !isNilArgs(selection.Args) {
					return NewClientError(`error parsing args for "__typename": no args expected`)
				}
				if selection.SelectionSet != nil {
					return NewClientError(`scalar field "__typename" must have no selection`)
				}
				for _, fragment := range selectionSet.Fragments {
					fragment.SelectionSet.Selections = append(fragment.SelectionSet.Selections, selection)
				}
				continue
			}
			return NewClientError(`unknown field "%s"`, selection.Name)
		}
		return nil
	case *Object:
		if selectionSet == nil {
			return NewClientError("object field must have selections")
		}
		for _, selection := range selectionSet.Selections {
			if selection.Name == "__typename" {
				if !isNilArgs(selection.Args) {
					return NewClientError(`error parsing args for "__typename": no args expected`)
				}
				if selection.SelectionSet != nil {
					return NewClientError(`scalar field "__typename" must have no selection`)
				}
				continue
			}

			field, ok := typ.Fields[selection.Name]
			if !ok {
				return NewClientError(`unknown field "%s"`, selection.Name)
			}

			// Only parse args once for a given selection.
			if !selection.parsed {
				parsed, err := field.ParseArguments(selection.Args)
				if err != nil {
					return NewClientError(`error parsing args for "%s": %s`, selection.Name, err)
				}
				selection.Args = parsed
				selection.parsed = true
			}

			if err := PrepareQuery(field.Type, selection.SelectionSet); err != nil {
				return err
			}
		}
		for _, fragment := range selectionSet.Fragments {
			if err := PrepareQuery(typ, fragment.SelectionSet); err != nil {
				return err
			}
		}
		return nil

	case *List:
		return PrepareQuery(typ.Type, selectionSet)

	case *NonNull:
		return PrepareQuery(typ.Type, selectionSet)

	default:
		panic("unknown type kind")
	}
}

type panicError struct {
	message string
}

func (p panicError) Error() string {
	return p.message
}

func safeResolve(ctx context.Context, field *Field, source, args interface{}, selectionSet *SelectionSet) (result interface{}, err error) {
	defer func() {
		if panicErr := recover(); panicErr != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			result, err = nil, fmt.Errorf("graphql: panic: %v\n%s", panicErr, buf)
		}
	}()
	return field.Resolve(ctx, source, args, selectionSet)
}

type resolveAndExecuteCacheKey struct {
	field     *Field
	source    interface{}
	selection *Selection
}

func (e *Executor) resolveAndExecute(ctx context.Context, field *Field, source interface{}, selection *Selection) (interface{}, error) {
	if field.Expensive {
		// TODO: Skip goroutine for cached value
		ctx, release := concurrencylimiter.Acquire(ctx)
		return fork(func() (interface{}, error) {
			defer release()

			value := reflect.ValueOf(source)
			// cache the body of resolve and excecute so that if the source doesn't change, we
			// don't need to recompute
			key := resolveAndExecuteCacheKey{field: field, source: source, selection: selection}

			// some types can't be put in a map; for those, use a always different value
			// as source
			if value.IsValid() && !value.Type().Comparable() {
				// TODO: Warn, or somehow prevent using type-system?
				key.source = new(byte)
			}

			// TODO: Consider cacheing resolve and execute independently
			resolvedValue, err := reactive.Cache(ctx, key, func(ctx context.Context) (interface{}, error) {
				value, err := safeResolve(ctx, field, source, selection.Args, selection.SelectionSet)
				if err != nil {
					return nil, err
				}

				// Release concurrency token before recursing into execute. It will attempt to
				// grab another concurrency token.
				release()

				e.mu.Lock()
				value, err = e.execute(ctx, field.Type, value, selection.SelectionSet)
				e.mu.Unlock()

				if err != nil {
					return nil, err
				}
				return await(value)
			})

			return resolvedValue, err
		}), nil
	}

	value, err := safeResolve(ctx, field, source, selection.Args, selection.SelectionSet)
	if err != nil {
		return nil, err
	}
	return e.execute(ctx, field.Type, value, selection.SelectionSet)
}

func (e *Executor) executeUnion(ctx context.Context, typ *Union, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	value := reflect.ValueOf(source)
	if value.Kind() == reflect.Ptr && value.IsNil() {
		return nil, nil
	}

	fields := make(map[string]interface{})
	for _, selection := range selectionSet.Selections {
		if selection.Name == "__typename" {
			fields[selection.Alias] = typ.Name
			continue
		}
	}

	// For every inline fragment spread, check if the current concrete type
	// matches and execute that object.
	var possibleTypes []string
	for typString, graphqlTyp := range typ.Types {
		inner := reflect.ValueOf(source)
		if inner.Kind() == reflect.Ptr && inner.Elem().Kind() == reflect.Struct {
			inner = inner.Elem()
		}

		inner = inner.FieldByName(typString)
		if inner.IsNil() {
			continue
		}
		possibleTypes = append(possibleTypes, graphqlTyp.String())

		for _, fragment := range selectionSet.Fragments {
			if fragment.On != typString {
				continue
			}
			resolved, err := e.executeObject(ctx, graphqlTyp, inner.Interface(), fragment.SelectionSet)
			if err != nil {
				return nil, nestPathError(typString, err)
			}

			for k, v := range resolved.(map[string]interface{}) {
				fields[k] = v
			}
		}
	}

	if len(possibleTypes) > 1 {
		return nil, fmt.Errorf("union type field should only return one value, but received: %s", strings.Join(possibleTypes, " "))
	}
	return fields, nil
}

// executeObject executes an object query
func (e *Executor) executeObject(ctx context.Context, typ *Object, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	value := reflect.ValueOf(source)
	if value.Kind() == reflect.Ptr && value.IsNil() {
		return nil, nil
	}

	selections := Flatten(selectionSet)

	fields := make(map[string]interface{})

	// for every selection, resolve the value and store it in the output object
	for _, selection := range selections {
		if selection.Name == "__typename" {
			fields[selection.Alias] = typ.Name
			continue
		}

		field := typ.Fields[selection.Name]
		resolved, err := e.resolveAndExecute(ctx, field, source, selection)
		if err != nil {
			return nil, nestPathError(selection.Alias, err)
		}
		fields[selection.Alias] = resolved
	}

	if typ.KeyField != nil {
		value, err := e.resolveAndExecute(ctx, typ.KeyField, source, &Selection{})
		if err != nil {
			return nil, nestPathError("__key", err)
		}
		fields["__key"] = value
	}

	return fields, nil
}

var emptyList = []interface{}{}

// executeList executes a set query
func (e *Executor) executeList(ctx context.Context, typ *List, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	if reflect.ValueOf(source).IsNil() {
		return emptyList, nil
	}

	// iterate over arbitrary slice types using reflect
	slice := reflect.ValueOf(source)
	items := make([]interface{}, slice.Len())

	// resolve every element in the slice
	for i := 0; i < slice.Len(); i++ {
		value := slice.Index(i)
		resolved, err := e.execute(ctx, typ.Type, value.Interface(), selectionSet)
		if err != nil {
			return nil, nestPathError(fmt.Sprint(i), err)
		}
		items[i] = resolved
	}

	return items, nil
}

// execute executes a query by dispatches according to typ
func (e *Executor) execute(ctx context.Context, typ Type, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	switch typ := typ.(type) {
	case *Scalar:
		if typ.Unwrapper != nil {
			return typ.Unwrapper(source)
		}
		return unwrap(source), nil
	case *Enum:
		val := unwrap(source)
		if mapVal, ok := typ.ReverseMap[val]; ok {
			return mapVal, nil
		}
		return nil, errors.New("enum is not valid")
	case *Union:
		return e.executeUnion(ctx, typ, source, selectionSet)
	case *Object:
		return e.executeObject(ctx, typ, source, selectionSet)
	case *List:
		return e.executeList(ctx, typ, source, selectionSet)
	case *NonNull:
		return e.execute(ctx, typ.Type, source, selectionSet)
	default:
		panic(typ)
	}
}

type ExecutorRunner interface {
	Execute(ctx context.Context, typ Type, source interface{}, query *Query) (interface{}, error)
}

type Executor struct {
	mu sync.Mutex
}

// Execute executes a query by dispatches according to typ
func (e *Executor) Execute(ctx context.Context, typ Type, source interface{}, query *Query) (interface{}, error) {
	e.mu.Lock()
	value, err := e.execute(ctx, typ, source, query.SelectionSet)
	e.mu.Unlock()

	// Await the promise if things look good so far.
	if err == nil {
		value, err = await(value)
	}

	// Maybe error wrap if we have an error and a name to attach.
	if err != nil && query.Name != "" {
		err = nestPathError(query.Name, err)
	}

	return value, err
}
<commit_msg>graphql: add Unwrap support<commit_after>package graphql

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"sync"

	"github.com/samsarahq/thunder/concurrencylimiter"
	"github.com/samsarahq/thunder/reactive"
)

type pathError struct {
	inner error
	path  []string
}

func nestPathErrorMulti(path []string, err error) error {
	// Don't nest SanitzedError's, as they are intended for human consumption.
	if se, ok := err.(SanitizedError); ok {
		return se
	}

	if pe, ok := err.(*pathError); ok {
		return &pathError{
			inner: pe.inner,
			path:  append(pe.path, path...),
		}
	}

	return &pathError{
		inner: err,
		path:  path,
	}
}

func nestPathError(key string, err error) error {
	// Don't nest SanitzedError's, as they are intended for human consumption.
	if se, ok := err.(SanitizedError); ok {
		return se
	}

	if pe, ok := err.(*pathError); ok {
		return &pathError{
			inner: pe.inner,
			path:  append(pe.path, key),
		}
	}

	return &pathError{
		inner: err,
		path:  []string{key},
	}
}

func ErrorCause(err error) error {
	if pe, ok := err.(*pathError); ok {
		return pe.inner
	}
	return err
}

func (pe *pathError) Unwrap() error {
	return pe.inner
}

func (pe *pathError) Error() string {
	var buffer bytes.Buffer
	for i := len(pe.path) - 1; i >= 0; i-- {
		if i < len(pe.path)-1 {
			buffer.WriteString(".")
		}
		buffer.WriteString(pe.path[i])
	}
	buffer.WriteString(": ")
	buffer.WriteString(pe.inner.Error())
	return buffer.String()
}

func isNilArgs(args interface{}) bool {
	m, ok := args.(map[string]interface{})
	return args == nil || (ok && len(m) == 0)
}

// unwrap will return the value associated with a pointer type, or nil if the
// pointer is nil
func unwrap(v interface{}) interface{} {
	i := reflect.ValueOf(v)
	for i.Kind() == reflect.Ptr && !i.IsNil() {
		i = i.Elem()
	}
	if i.Kind() == reflect.Invalid {
		return nil
	}
	return i.Interface()
}

// PrepareQuery checks that the given selectionSet matches the schema typ, and
// parses the args in selectionSet
func PrepareQuery(typ Type, selectionSet *SelectionSet) error {
	switch typ := typ.(type) {
	case *Scalar:
		if selectionSet != nil {
			return NewClientError("scalar field must have no selections")
		}
		return nil
	case *Enum:
		if selectionSet != nil {
			return NewClientError("enum field must have no selections")
		}
		return nil
	case *Union:
		if selectionSet == nil {
			return NewClientError("object field must have selections")
		}

		for _, fragment := range selectionSet.Fragments {
			for typString, graphqlTyp := range typ.Types {
				if fragment.On != typString {
					continue
				}
				if err := PrepareQuery(graphqlTyp, fragment.SelectionSet); err != nil {
					return err
				}
			}
		}
		for _, selection := range selectionSet.Selections {
			if selection.Name == "__typename" {
				if !isNilArgs(selection.Args) {
					return NewClientError(`error parsing args for "__typename": no args expected`)
				}
				if selection.SelectionSet != nil {
					return NewClientError(`scalar field "__typename" must have no selection`)
				}
				for _, fragment := range selectionSet.Fragments {
					fragment.SelectionSet.Selections = append(fragment.SelectionSet.Selections, selection)
				}
				continue
			}
			return NewClientError(`unknown field "%s"`, selection.Name)
		}
		return nil
	case *Object:
		if selectionSet == nil {
			return NewClientError("object field must have selections")
		}
		for _, selection := range selectionSet.Selections {
			if selection.Name == "__typename" {
				if !isNilArgs(selection.Args) {
					return NewClientError(`error parsing args for "__typename": no args expected`)
				}
				if selection.SelectionSet != nil {
					return NewClientError(`scalar field "__typename" must have no selection`)
				}
				continue
			}

			field, ok := typ.Fields[selection.Name]
			if !ok {
				return NewClientError(`unknown field "%s"`, selection.Name)
			}

			// Only parse args once for a given selection.
			if !selection.parsed {
				parsed, err := field.ParseArguments(selection.Args)
				if err != nil {
					return NewClientError(`error parsing args for "%s": %s`, selection.Name, err)
				}
				selection.Args = parsed
				selection.parsed = true
			}

			if err := PrepareQuery(field.Type, selection.SelectionSet); err != nil {
				return err
			}
		}
		for _, fragment := range selectionSet.Fragments {
			if err := PrepareQuery(typ, fragment.SelectionSet); err != nil {
				return err
			}
		}
		return nil

	case *List:
		return PrepareQuery(typ.Type, selectionSet)

	case *NonNull:
		return PrepareQuery(typ.Type, selectionSet)

	default:
		panic("unknown type kind")
	}
}

type panicError struct {
	message string
}

func (p panicError) Error() string {
	return p.message
}

func safeResolve(ctx context.Context, field *Field, source, args interface{}, selectionSet *SelectionSet) (result interface{}, err error) {
	defer func() {
		if panicErr := recover(); panicErr != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			result, err = nil, fmt.Errorf("graphql: panic: %v\n%s", panicErr, buf)
		}
	}()
	return field.Resolve(ctx, source, args, selectionSet)
}

type resolveAndExecuteCacheKey struct {
	field     *Field
	source    interface{}
	selection *Selection
}

func (e *Executor) resolveAndExecute(ctx context.Context, field *Field, source interface{}, selection *Selection) (interface{}, error) {
	if field.Expensive {
		// TODO: Skip goroutine for cached value
		ctx, release := concurrencylimiter.Acquire(ctx)
		return fork(func() (interface{}, error) {
			defer release()

			value := reflect.ValueOf(source)
			// cache the body of resolve and excecute so that if the source doesn't change, we
			// don't need to recompute
			key := resolveAndExecuteCacheKey{field: field, source: source, selection: selection}

			// some types can't be put in a map; for those, use a always different value
			// as source
			if value.IsValid() && !value.Type().Comparable() {
				// TODO: Warn, or somehow prevent using type-system?
				key.source = new(byte)
			}

			// TODO: Consider cacheing resolve and execute independently
			resolvedValue, err := reactive.Cache(ctx, key, func(ctx context.Context) (interface{}, error) {
				value, err := safeResolve(ctx, field, source, selection.Args, selection.SelectionSet)
				if err != nil {
					return nil, err
				}

				// Release concurrency token before recursing into execute. It will attempt to
				// grab another concurrency token.
				release()

				e.mu.Lock()
				value, err = e.execute(ctx, field.Type, value, selection.SelectionSet)
				e.mu.Unlock()

				if err != nil {
					return nil, err
				}
				return await(value)
			})

			return resolvedValue, err
		}), nil
	}

	value, err := safeResolve(ctx, field, source, selection.Args, selection.SelectionSet)
	if err != nil {
		return nil, err
	}
	return e.execute(ctx, field.Type, value, selection.SelectionSet)
}

func (e *Executor) executeUnion(ctx context.Context, typ *Union, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	value := reflect.ValueOf(source)
	if value.Kind() == reflect.Ptr && value.IsNil() {
		return nil, nil
	}

	fields := make(map[string]interface{})
	for _, selection := range selectionSet.Selections {
		if selection.Name == "__typename" {
			fields[selection.Alias] = typ.Name
			continue
		}
	}

	// For every inline fragment spread, check if the current concrete type
	// matches and execute that object.
	var possibleTypes []string
	for typString, graphqlTyp := range typ.Types {
		inner := reflect.ValueOf(source)
		if inner.Kind() == reflect.Ptr && inner.Elem().Kind() == reflect.Struct {
			inner = inner.Elem()
		}

		inner = inner.FieldByName(typString)
		if inner.IsNil() {
			continue
		}
		possibleTypes = append(possibleTypes, graphqlTyp.String())

		for _, fragment := range selectionSet.Fragments {
			if fragment.On != typString {
				continue
			}
			resolved, err := e.executeObject(ctx, graphqlTyp, inner.Interface(), fragment.SelectionSet)
			if err != nil {
				return nil, nestPathError(typString, err)
			}

			for k, v := range resolved.(map[string]interface{}) {
				fields[k] = v
			}
		}
	}

	if len(possibleTypes) > 1 {
		return nil, fmt.Errorf("union type field should only return one value, but received: %s", strings.Join(possibleTypes, " "))
	}
	return fields, nil
}

// executeObject executes an object query
func (e *Executor) executeObject(ctx context.Context, typ *Object, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	value := reflect.ValueOf(source)
	if value.Kind() == reflect.Ptr && value.IsNil() {
		return nil, nil
	}

	selections := Flatten(selectionSet)

	fields := make(map[string]interface{})

	// for every selection, resolve the value and store it in the output object
	for _, selection := range selections {
		if selection.Name == "__typename" {
			fields[selection.Alias] = typ.Name
			continue
		}

		field := typ.Fields[selection.Name]
		resolved, err := e.resolveAndExecute(ctx, field, source, selection)
		if err != nil {
			return nil, nestPathError(selection.Alias, err)
		}
		fields[selection.Alias] = resolved
	}

	if typ.KeyField != nil {
		value, err := e.resolveAndExecute(ctx, typ.KeyField, source, &Selection{})
		if err != nil {
			return nil, nestPathError("__key", err)
		}
		fields["__key"] = value
	}

	return fields, nil
}

var emptyList = []interface{}{}

// executeList executes a set query
func (e *Executor) executeList(ctx context.Context, typ *List, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	if reflect.ValueOf(source).IsNil() {
		return emptyList, nil
	}

	// iterate over arbitrary slice types using reflect
	slice := reflect.ValueOf(source)
	items := make([]interface{}, slice.Len())

	// resolve every element in the slice
	for i := 0; i < slice.Len(); i++ {
		value := slice.Index(i)
		resolved, err := e.execute(ctx, typ.Type, value.Interface(), selectionSet)
		if err != nil {
			return nil, nestPathError(fmt.Sprint(i), err)
		}
		items[i] = resolved
	}

	return items, nil
}

// execute executes a query by dispatches according to typ
func (e *Executor) execute(ctx context.Context, typ Type, source interface{}, selectionSet *SelectionSet) (interface{}, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	switch typ := typ.(type) {
	case *Scalar:
		if typ.Unwrapper != nil {
			return typ.Unwrapper(source)
		}
		return unwrap(source), nil
	case *Enum:
		val := unwrap(source)
		if mapVal, ok := typ.ReverseMap[val]; ok {
			return mapVal, nil
		}
		return nil, errors.New("enum is not valid")
	case *Union:
		return e.executeUnion(ctx, typ, source, selectionSet)
	case *Object:
		return e.executeObject(ctx, typ, source, selectionSet)
	case *List:
		return e.executeList(ctx, typ, source, selectionSet)
	case *NonNull:
		return e.execute(ctx, typ.Type, source, selectionSet)
	default:
		panic(typ)
	}
}

type ExecutorRunner interface {
	Execute(ctx context.Context, typ Type, source interface{}, query *Query) (interface{}, error)
}

type Executor struct {
	mu sync.Mutex
}

// Execute executes a query by dispatches according to typ
func (e *Executor) Execute(ctx context.Context, typ Type, source interface{}, query *Query) (interface{}, error) {
	e.mu.Lock()
	value, err := e.execute(ctx, typ, source, query.SelectionSet)
	e.mu.Unlock()

	// Await the promise if things look good so far.
	if err == nil {
		value, err = await(value)
	}

	// Maybe error wrap if we have an error and a name to attach.
	if err != nil && query.Name != "" {
		err = nestPathError(query.Name, err)
	}

	return value, err
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/testutil"
)

var _ = testutil.Defer(func() {
	Describe("Leveldb external", func() {
		o := &opt.Options{
			BlockCache:           opt.NoCache,
			BlockRestartInterval: 5,
			BlockSize:            50,
			Compression:          opt.NoCompression,
			MaxOpenFiles:         0,
			Strict:               opt.StrictAll,
			WriteBuffer:          1000,
		}

		Describe("write test", func() {
			It("should do write correctly", func(done Done) {
				db := newTestingDB(o, nil, nil)
				t := testutil.DBTesting{
					DB:      db,
					Deleted: testutil.KeyValue_Generate(nil, 500, 1, 50, 5, 5).Clone(),
				}
				testutil.DoDBTesting(&t)
				db.TestClose()
				done <- true
			}, 9.0)
		})

		Describe("read test", func() {
			testutil.AllKeyValueTesting(nil, func(kv testutil.KeyValue) testutil.DB {
				// Building the DB.
				db := newTestingDB(o, nil, nil)
				kv.IterateShuffled(nil, func(i int, key, value []byte) {
					err := db.TestPut(key, value)
					Expect(err).NotTo(HaveOccurred())
				})
				testutil.Defer("teardown", func() {
					db.TestClose()
				})

				return db
			})
		})
	})
})
<commit_msg>leveldb: Increase 'write test' timeout to 20 secs<commit_after>// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/testutil"
)

var _ = testutil.Defer(func() {
	Describe("Leveldb external", func() {
		o := &opt.Options{
			BlockCache:           opt.NoCache,
			BlockRestartInterval: 5,
			BlockSize:            50,
			Compression:          opt.NoCompression,
			MaxOpenFiles:         0,
			Strict:               opt.StrictAll,
			WriteBuffer:          1000,
		}

		Describe("write test", func() {
			It("should do write correctly", func(done Done) {
				db := newTestingDB(o, nil, nil)
				t := testutil.DBTesting{
					DB:      db,
					Deleted: testutil.KeyValue_Generate(nil, 500, 1, 50, 5, 5).Clone(),
				}
				testutil.DoDBTesting(&t)
				db.TestClose()
				done <- true
			}, 20.0)
		})

		Describe("read test", func() {
			testutil.AllKeyValueTesting(nil, func(kv testutil.KeyValue) testutil.DB {
				// Building the DB.
				db := newTestingDB(o, nil, nil)
				kv.IterateShuffled(nil, func(i int, key, value []byte) {
					err := db.TestPut(key, value)
					Expect(err).NotTo(HaveOccurred())
				})
				testutil.Defer("teardown", func() {
					db.TestClose()
				})

				return db
			})
		})
	})
})
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"net/http"

	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/crypto"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/types"

	"github.com/julienschmidt/httprouter"
)

type (
	// ExplorerBlock is a block with some extra information such as the id and
	// height. This information is provided for programs that may not be
	// complex enough to compute the ID on their own.
	ExplorerBlock struct {
		MinerPayoutIDs []types.CoinOutputID  `json:"minerpayoutids"`
		Transactions   []ExplorerTransaction `json:"transactions"`
		RawBlock       types.Block           `json:"rawblock"`

		modules.BlockFacts
	}

	// ExplorerTransaction is a transcation with some extra information such as
	// the parent block. This information is provided for programs that may not
	// be complex enough to compute the extra information on their own.
	ExplorerTransaction struct {
		ID             types.TransactionID `json:"id"`
		Height         types.BlockHeight   `json:"height"`
		Parent         types.BlockID       `json:"parent"`
		RawTransaction types.Transaction   `json:"rawtransaction"`

		CoinInputOutputs       []types.CoinOutput         `json:"coininputoutputs"` // the outputs being spent
		CoinOutputIDs          []types.CoinOutputID       `json:"coinoutputids"`
		BlockStakeInputOutputs []types.BlockStakeOutput   `json:"blockstakeinputoutputs"` // the outputs being spent
		BlockStakeOutputIDs    []types.BlockStakeOutputID `json:"blockstakeoutputids"`
	}

	// ExplorerGET is the object returned as a response to a GET request to
	// /explorer.
	ExplorerGET struct {
		modules.BlockFacts
	}

	// ExplorerBlockGET is the object returned by a GET request to
	// /explorer/block.
	ExplorerBlockGET struct {
		Block ExplorerBlock `json:"block"`
	}

	// ExplorerHashGET is the object returned as a response to a GET request to
	// /explorer/hash. The HashType will indicate whether the hash corresponds
	// to a block id, a transaction id, a siacoin output id, a file contract
	// id, or a siafund output id. In the case of a block id, 'Block' will be
	// filled out and all the rest of the fields will be blank. In the case of
	// a transaction id, 'Transaction' will be filled out and all the rest of
	// the fields will be blank. For everything else, 'Transactions' and
	// 'Blocks' will/may be filled out and everything else will be blank.
	ExplorerHashGET struct {
		HashType     string                `json:"hashtype"`
		Block        ExplorerBlock         `json:"block"`
		Blocks       []ExplorerBlock       `json:"blocks"`
		Transaction  ExplorerTransaction   `json:"transaction"`
		Transactions []ExplorerTransaction `json:"transactions"`
	}
)

// buildExplorerTransaction takes a transaction and the height + id of the
// block it appears in an uses that to build an explorer transaction.
func (api *API) buildExplorerTransaction(height types.BlockHeight, parent types.BlockID, txn types.Transaction) (et ExplorerTransaction) {
	// Get the header information for the transaction.
	et.ID = txn.ID()
	et.Height = height
	et.Parent = parent
	et.RawTransaction = txn

	// Add the siacoin outputs that correspond with each siacoin input.
	for _, sci := range txn.CoinInputs {
		sco, exists := api.explorer.CoinOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding coin output")
		}
		et.CoinInputOutputs = append(et.CoinInputOutputs, sco)
	}

	for i := range txn.CoinOutputs {
		et.CoinOutputIDs = append(et.CoinOutputIDs, txn.CoinOutputID(uint64(i)))
	}

	// Add the siafund outputs that correspond to each siacoin input.
	for _, sci := range txn.BlockStakeInputs {
		sco, exists := api.explorer.BlockStakeOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding blockstake output")
		}
		et.BlockStakeInputOutputs = append(et.BlockStakeInputOutputs, sco)
	}

	for i := range txn.BlockStakeOutputs {
		et.BlockStakeOutputIDs = append(et.BlockStakeOutputIDs, txn.BlockStakeOutputID(uint64(i)))
	}

	return et
}

// buildExplorerBlock takes a block and its height and uses it to construct an
// explorer block.
func (api *API) buildExplorerBlock(height types.BlockHeight, block types.Block) ExplorerBlock {
	var mpoids []types.CoinOutputID
	for i := range block.MinerPayouts {
		mpoids = append(mpoids, block.MinerPayoutID(uint64(i)))
	}

	var etxns []ExplorerTransaction
	for _, txn := range block.Transactions {
		etxns = append(etxns, api.buildExplorerTransaction(height, block.ID(), txn))
	}

	facts, exists := api.explorer.BlockFacts(height)
	if build.DEBUG && !exists {
		panic("incorrect request to buildExplorerBlock - block does not exist")
	}

	return ExplorerBlock{
		MinerPayoutIDs: mpoids,
		Transactions:   etxns,
		RawBlock:       block,

		BlockFacts: facts,
	}
}

// explorerHandler handles API calls to /explorer/blocks/:height.
func (api *API) explorerBlocksHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Parse the height that's being requested.
	var height types.BlockHeight
	_, err := fmt.Sscan(ps.ByName("height"), &height)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}

	// Fetch and return the explorer block.
	block, exists := api.cs.BlockAtHeight(height)
	if !exists {
		WriteError(w, Error{"no block found at input height in call to /explorer/block"}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, ExplorerBlockGET{
		Block: api.buildExplorerBlock(height, block),
	})
}

// buildTransactionSet returns the blocks and transactions that are associated
// with a set of transaction ids.
func (api *API) buildTransactionSet(txids []types.TransactionID) (txns []ExplorerTransaction, blocks []ExplorerBlock) {
	for _, txid := range txids {
		// Get the block containing the transaction - in the case of miner
		// payouts, the block might be the transaction.
		block, height, exists := api.explorer.Transaction(txid)
		if !exists && build.DEBUG {
			panic("explorer pointing to nonexistent txn")
		}

		// Check if the block is the transaction.
		if types.TransactionID(block.ID()) == txid {
			blocks = append(blocks, api.buildExplorerBlock(height, block))
		} else {
			// Find the transaction within the block with the correct id.
			for _, t := range block.Transactions {
				if t.ID() == txid {
					txns = append(txns, api.buildExplorerTransaction(height, block.ID(), t))
					break
				}
			}
		}
	}
	return txns, blocks
}

// explorerHashHandler handles GET requests to /explorer/hash/:hash.
func (api *API) explorerHashHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Scan the hash as a hash. If that fails, try scanning the hash as an
	// address.
	hash, err := scanHash(ps.ByName("hash"))
	if err != nil {
		addr, err := scanAddress(ps.ByName("hash"))
		if err != nil {
			WriteError(w, Error{err.Error()}, http.StatusBadRequest)
			return
		}

		// Try the hash as an unlock hash. Unlock hash is checked last because
		// unlock hashes do not have collision-free guarantees. Someone can create
		// an unlock hash that collides with another object id. They will not be
		// able to use the unlock hash, but they can disrupt the explorer. This is
		// handled by checking the unlock hash last. Anyone intentionally creating
		// a colliding unlock hash (such a collision can only happen if done
		// intentionally) will be unable to find their unlock hash in the
		// blockchain through the explorer hash lookup.
		txids := api.explorer.UnlockHash(addr)
		if len(txids) != 0 {
			txns, blocks := api.buildTransactionSet(txids)
			WriteJSON(w, ExplorerHashGET{
				HashType:     "unlockhash",
				Blocks:       blocks,
				Transactions: txns,
			})
			return
		}

		// Hash not found, return an error.
		WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
		return
	}

	// TODO: lookups on the zero hash are too expensive to allow. Need a
	// better way to handle this case.
	if hash == (crypto.Hash{}) {
		WriteError(w, Error{"can't lookup the empty unlock hash"}, http.StatusBadRequest)
		return
	}

	// Try the hash as a block id.
	block, height, exists := api.explorer.Block(types.BlockID(hash))
	if exists {
		WriteJSON(w, ExplorerHashGET{
			HashType: "blockid",
			Block:    api.buildExplorerBlock(height, block),
		})
		return
	}

	// Try the hash as a transaction id.
	block, height, exists = api.explorer.Transaction(types.TransactionID(hash))
	if exists {
		var txn types.Transaction
		for _, t := range block.Transactions {
			if t.ID() == types.TransactionID(hash) {
				txn = t
			}
		}
		WriteJSON(w, ExplorerHashGET{
			HashType:    HashTypeTransactionIDStr,
			Transaction: api.buildExplorerTransaction(height, block.ID(), txn),
		})
		return
	}

	// Try the hash as a siacoin output id.
	txids := api.explorer.CoinOutputID(types.CoinOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeCoinOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a siafund output id.
	txids = api.explorer.BlockStakeOutputID(types.BlockStakeOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeBlockStakeOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Hash not found, return an error.
	WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
}

// hash type string constants
const (
	HashTypeTransactionIDStr      = "transactionid"
	HashTypeCoinOutputIDStr       = "coinoutputid"
	HashTypeBlockStakeOutputIDStr = "blockstakeoutputid"
)

// explorerHandler handles API calls to /explorer
func (api *API) explorerHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	facts := api.explorer.LatestBlockFacts()
	WriteJSON(w, ExplorerGET{
		BlockFacts: facts,
	})
}
<commit_msg>add hex encoding of blocks/txns in explorer api<commit_after>package api

import (
	"encoding/hex"
	"fmt"
	"net/http"

	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/crypto"
	"github.com/rivine/rivine/encoding"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/types"

	"github.com/julienschmidt/httprouter"
)

type (
	// ExplorerBlock is a block with some extra information such as the id and
	// height. This information is provided for programs that may not be
	// complex enough to compute the ID on their own.
	ExplorerBlock struct {
		MinerPayoutIDs []types.CoinOutputID  `json:"minerpayoutids"`
		Transactions   []ExplorerTransaction `json:"transactions"`
		RawBlock       types.Block           `json:"rawblock"`
		HexBlock       string                `json:"hexblock"`

		modules.BlockFacts
	}

	// ExplorerTransaction is a transcation with some extra information such as
	// the parent block. This information is provided for programs that may not
	// be complex enough to compute the extra information on their own.
	ExplorerTransaction struct {
		ID             types.TransactionID `json:"id"`
		Height         types.BlockHeight   `json:"height"`
		Parent         types.BlockID       `json:"parent"`
		RawTransaction types.Transaction   `json:"rawtransaction"`
		HexTransaction string              `json:"hextransaction"`

		CoinInputOutputs       []types.CoinOutput         `json:"coininputoutputs"` // the outputs being spent
		CoinOutputIDs          []types.CoinOutputID       `json:"coinoutputids"`
		BlockStakeInputOutputs []types.BlockStakeOutput   `json:"blockstakeinputoutputs"` // the outputs being spent
		BlockStakeOutputIDs    []types.BlockStakeOutputID `json:"blockstakeoutputids"`
	}

	// ExplorerGET is the object returned as a response to a GET request to
	// /explorer.
	ExplorerGET struct {
		modules.BlockFacts
	}

	// ExplorerBlockGET is the object returned by a GET request to
	// /explorer/block.
	ExplorerBlockGET struct {
		Block ExplorerBlock `json:"block"`
	}

	// ExplorerHashGET is the object returned as a response to a GET request to
	// /explorer/hash. The HashType will indicate whether the hash corresponds
	// to a block id, a transaction id, a siacoin output id, a file contract
	// id, or a siafund output id. In the case of a block id, 'Block' will be
	// filled out and all the rest of the fields will be blank. In the case of
	// a transaction id, 'Transaction' will be filled out and all the rest of
	// the fields will be blank. For everything else, 'Transactions' and
	// 'Blocks' will/may be filled out and everything else will be blank.
	ExplorerHashGET struct {
		HashType     string                `json:"hashtype"`
		Block        ExplorerBlock         `json:"block"`
		Blocks       []ExplorerBlock       `json:"blocks"`
		Transaction  ExplorerTransaction   `json:"transaction"`
		Transactions []ExplorerTransaction `json:"transactions"`
	}
)

// buildExplorerTransaction takes a transaction and the height + id of the
// block it appears in an uses that to build an explorer transaction.
func (api *API) buildExplorerTransaction(height types.BlockHeight, parent types.BlockID, txn types.Transaction) (et ExplorerTransaction) {
	// Get the header information for the transaction.
	et.ID = txn.ID()
	et.Height = height
	et.Parent = parent
	et.RawTransaction = txn
	et.HexTransaction = hex.EncodeToString(encoding.Marshal(txn))

	// Add the siacoin outputs that correspond with each siacoin input.
	for _, sci := range txn.CoinInputs {
		sco, exists := api.explorer.CoinOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding coin output")
		}
		et.CoinInputOutputs = append(et.CoinInputOutputs, sco)
	}

	for i := range txn.CoinOutputs {
		et.CoinOutputIDs = append(et.CoinOutputIDs, txn.CoinOutputID(uint64(i)))
	}

	// Add the siafund outputs that correspond to each siacoin input.
	for _, sci := range txn.BlockStakeInputs {
		sco, exists := api.explorer.BlockStakeOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding blockstake output")
		}
		et.BlockStakeInputOutputs = append(et.BlockStakeInputOutputs, sco)
	}

	for i := range txn.BlockStakeOutputs {
		et.BlockStakeOutputIDs = append(et.BlockStakeOutputIDs, txn.BlockStakeOutputID(uint64(i)))
	}

	return et
}

// buildExplorerBlock takes a block and its height and uses it to construct an
// explorer block.
func (api *API) buildExplorerBlock(height types.BlockHeight, block types.Block) ExplorerBlock {
	var mpoids []types.CoinOutputID
	for i := range block.MinerPayouts {
		mpoids = append(mpoids, block.MinerPayoutID(uint64(i)))
	}

	var etxns []ExplorerTransaction
	for _, txn := range block.Transactions {
		etxns = append(etxns, api.buildExplorerTransaction(height, block.ID(), txn))
	}

	facts, exists := api.explorer.BlockFacts(height)
	if build.DEBUG && !exists {
		panic("incorrect request to buildExplorerBlock - block does not exist")
	}

	return ExplorerBlock{
		MinerPayoutIDs: mpoids,
		Transactions:   etxns,
		RawBlock:       block,
		HexBlock:       hex.EncodeToString(encoding.Marshal(block)),

		BlockFacts: facts,
	}
}

// explorerHandler handles API calls to /explorer/blocks/:height.
func (api *API) explorerBlocksHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Parse the height that's being requested.
	var height types.BlockHeight
	_, err := fmt.Sscan(ps.ByName("height"), &height)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}

	// Fetch and return the explorer block.
	block, exists := api.cs.BlockAtHeight(height)
	if !exists {
		WriteError(w, Error{"no block found at input height in call to /explorer/block"}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, ExplorerBlockGET{
		Block: api.buildExplorerBlock(height, block),
	})
}

// buildTransactionSet returns the blocks and transactions that are associated
// with a set of transaction ids.
func (api *API) buildTransactionSet(txids []types.TransactionID) (txns []ExplorerTransaction, blocks []ExplorerBlock) {
	for _, txid := range txids {
		// Get the block containing the transaction - in the case of miner
		// payouts, the block might be the transaction.
		block, height, exists := api.explorer.Transaction(txid)
		if !exists && build.DEBUG {
			panic("explorer pointing to nonexistent txn")
		}

		// Check if the block is the transaction.
		if types.TransactionID(block.ID()) == txid {
			blocks = append(blocks, api.buildExplorerBlock(height, block))
		} else {
			// Find the transaction within the block with the correct id.
			for _, t := range block.Transactions {
				if t.ID() == txid {
					txns = append(txns, api.buildExplorerTransaction(height, block.ID(), t))
					break
				}
			}
		}
	}
	return txns, blocks
}

// explorerHashHandler handles GET requests to /explorer/hash/:hash.
func (api *API) explorerHashHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Scan the hash as a hash. If that fails, try scanning the hash as an
	// address.
	hash, err := scanHash(ps.ByName("hash"))
	if err != nil {
		addr, err := scanAddress(ps.ByName("hash"))
		if err != nil {
			WriteError(w, Error{err.Error()}, http.StatusBadRequest)
			return
		}

		// Try the hash as an unlock hash. Unlock hash is checked last because
		// unlock hashes do not have collision-free guarantees. Someone can create
		// an unlock hash that collides with another object id. They will not be
		// able to use the unlock hash, but they can disrupt the explorer. This is
		// handled by checking the unlock hash last. Anyone intentionally creating
		// a colliding unlock hash (such a collision can only happen if done
		// intentionally) will be unable to find their unlock hash in the
		// blockchain through the explorer hash lookup.
		txids := api.explorer.UnlockHash(addr)
		if len(txids) != 0 {
			txns, blocks := api.buildTransactionSet(txids)
			WriteJSON(w, ExplorerHashGET{
				HashType:     "unlockhash",
				Blocks:       blocks,
				Transactions: txns,
			})
			return
		}

		// Hash not found, return an error.
		WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
		return
	}

	// TODO: lookups on the zero hash are too expensive to allow. Need a
	// better way to handle this case.
	if hash == (crypto.Hash{}) {
		WriteError(w, Error{"can't lookup the empty unlock hash"}, http.StatusBadRequest)
		return
	}

	// Try the hash as a block id.
	block, height, exists := api.explorer.Block(types.BlockID(hash))
	if exists {
		WriteJSON(w, ExplorerHashGET{
			HashType: "blockid",
			Block:    api.buildExplorerBlock(height, block),
		})
		return
	}

	// Try the hash as a transaction id.
	block, height, exists = api.explorer.Transaction(types.TransactionID(hash))
	if exists {
		var txn types.Transaction
		for _, t := range block.Transactions {
			if t.ID() == types.TransactionID(hash) {
				txn = t
			}
		}
		WriteJSON(w, ExplorerHashGET{
			HashType:    HashTypeTransactionIDStr,
			Transaction: api.buildExplorerTransaction(height, block.ID(), txn),
		})
		return
	}

	// Try the hash as a siacoin output id.
	txids := api.explorer.CoinOutputID(types.CoinOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeCoinOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a siafund output id.
	txids = api.explorer.BlockStakeOutputID(types.BlockStakeOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeBlockStakeOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Hash not found, return an error.
	WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
}

// hash type string constants
const (
	HashTypeTransactionIDStr      = "transactionid"
	HashTypeCoinOutputIDStr       = "coinoutputid"
	HashTypeBlockStakeOutputIDStr = "blockstakeoutputid"
)

// explorerHandler handles API calls to /explorer
func (api *API) explorerHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	facts := api.explorer.LatestBlockFacts()
	WriteJSON(w, ExplorerGET{
		BlockFacts: facts,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"regexp"

	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/sqs"
)

const (
	invalidContent = "InvalidMessageContents"
)

var (
	emailRegexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
)

type ResponseError struct {
	Errors map[string]string `json:"errors"`
}

func NewResponseError(errors map[string]string) *ResponseError {
	return &ResponseError{errors}
}

func NewBaseResponseError(errorMsg string) *ResponseError {
	return &ResponseError{map[string]string{"base": errorMsg}}
}

type SendEmailRequest struct {
	Email Email `json:"email"`
}

type Email struct {
	FromEmail string `json:"fromEmail"`
	FromName  string `json:"fromName"`
	ToEmail   string `json:"toEmail"`
	ToName    string `json:"toName"`
	Subject   string `json:"subject"`
	Body      string `json:"body"`
}

type SendEmailResponse struct {
	MessageId string `json:"messageId"`
}

func (e Email) Validate() (bool, *ResponseError) {
	errors := make(map[string]string)

	if valid, errorMsg := validateEmail("From email", e.FromEmail); !valid {
		errors["fromEmail"] = errorMsg
	}
	if valid, errorMsg := validateEmail("To email", e.ToEmail); !valid {
		errors["toEmail"] = errorMsg
	}

	if len(errors) > 0 {
		return false, NewResponseError(errors)
	}
	return true, nil
}

func validateEmail(fieldName, email string) (bool, string) {
	if email == "" {
		return false, fieldName + " is required"
	}
	if !emailRegexp.MatchString(email) {
		return false, fieldName + " is not a valid email"
	}
	return true, ""
}

func respondWithError(w http.ResponseWriter, respErr *ResponseError, httpStatus int) {
	w.WriteHeader(httpStatus)
	errorBytes, err := json.Marshal(respErr)
	if err != nil {
		// This should never happen
		panic("Could not marshal error response: " + err.Error())
	}

	w.Write(errorBytes)
}

func SendEmailHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, config.MaxBodySizeBytes))
	if err != nil {
		respondWithError(w, NewBaseResponseError("Could not read body"), http.StatusBadRequest)
		return
	}
	defer r.Body.Close()

	var request SendEmailRequest
	if err := json.Unmarshal(body, &request); err != nil {
		respondWithError(w, NewBaseResponseError("Could not decode JSON body"), http.StatusBadRequest)
		return
	}
	email := request.Email
	if valid, respErr := email.Validate(); !valid {
		respondWithError(w, respErr, http.StatusUnprocessableEntity)
		return
	}

	// get a random queue url from config
	queueUrl := config.QueueUrls[rand.Intn(len(config.QueueUrls))]

	bodyStr := string(body)
	resp, err := sqsClient.SendMessage(&sqs.SendMessageInput{
		QueueUrl:    &queueUrl,
		MessageBody: &bodyStr,
	})
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == invalidContent {
			respondWithError(
				w,
				NewBaseResponseError("Body contains characters outside the allowed set"),
				http.StatusBadRequest,
			)
			return
		} else {
			respondWithError(w, NewBaseResponseError("Service unavailable"), http.StatusServiceUnavailable)
			return
		}
	}

	response := &SendEmailResponse{MessageId: *resp.MessageId}
	respBytes, err := json.Marshal(response)
	if err != nil {
		// This should never happen
		panic("Could not marshal response: " + err.Error())
	}

	w.Write(respBytes)
}
<commit_msg>Added Content-Type & Access-Control-Allow-Origin headers<commit_after>package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"regexp"

	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/sqs"
)

const (
	invalidContent = "InvalidMessageContents"
)

var (
	emailRegexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
)

type ResponseError struct {
	Errors map[string]string `json:"errors"`
}

func NewResponseError(errors map[string]string) *ResponseError {
	return &ResponseError{errors}
}

func NewBaseResponseError(errorMsg string) *ResponseError {
	return &ResponseError{map[string]string{"base": errorMsg}}
}

type SendEmailRequest struct {
	Email Email `json:"email"`
}

type Email struct {
	FromEmail string `json:"fromEmail"`
	FromName  string `json:"fromName"`
	ToEmail   string `json:"toEmail"`
	ToName    string `json:"toName"`
	Subject   string `json:"subject"`
	Body      string `json:"body"`
}

type SendEmailResponse struct {
	MessageId string `json:"messageId"`
}

func (e Email) Validate() (bool, *ResponseError) {
	errors := make(map[string]string)

	if valid, errorMsg := validateEmail("From email", e.FromEmail); !valid {
		errors["fromEmail"] = errorMsg
	}
	if valid, errorMsg := validateEmail("To email", e.ToEmail); !valid {
		errors["toEmail"] = errorMsg
	}

	if len(errors) > 0 {
		return false, NewResponseError(errors)
	}
	return true, nil
}

func validateEmail(fieldName, email string) (bool, string) {
	if email == "" {
		return false, fieldName + " is required"
	}
	if !emailRegexp.MatchString(email) {
		return false, fieldName + " is not a valid email"
	}
	return true, ""
}

func respondWithError(w http.ResponseWriter, respErr *ResponseError, httpStatus int) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(httpStatus)
	errorBytes, err := json.Marshal(respErr)
	if err != nil {
		// This should never happen
		panic("Could not marshal error response: " + err.Error())
	}

	w.Write(errorBytes)
}

func SendEmailHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, config.MaxBodySizeBytes))
	if err != nil {
		respondWithError(w, NewBaseResponseError("Could not read body"), http.StatusBadRequest)
		return
	}
	defer r.Body.Close()

	var request SendEmailRequest
	if err := json.Unmarshal(body, &request); err != nil {
		respondWithError(w, NewBaseResponseError("Could not decode JSON body"), http.StatusBadRequest)
		return
	}
	email := request.Email
	if valid, respErr := email.Validate(); !valid {
		respondWithError(w, respErr, http.StatusUnprocessableEntity)
		return
	}

	// get a random queue url from config
	queueUrl := config.QueueUrls[rand.Intn(len(config.QueueUrls))]

	bodyStr := string(body)
	resp, err := sqsClient.SendMessage(&sqs.SendMessageInput{
		QueueUrl:    &queueUrl,
		MessageBody: &bodyStr,
	})
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == invalidContent {
			respondWithError(
				w,
				NewBaseResponseError("Body contains characters outside the allowed set"),
				http.StatusBadRequest,
			)
			return
		} else {
			respondWithError(w, NewBaseResponseError("Service unavailable"), http.StatusServiceUnavailable)
			return
		}
	}

	response := &SendEmailResponse{MessageId: *resp.MessageId}
	respBytes, err := json.Marshal(response)
	if err != nil {
		// This should never happen
		panic("Could not marshal response: " + err.Error())
	}

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Content-Type", "application/json")
	w.Write(respBytes)
}
<|endoftext|>


<commit_before>package api

import (
	"io"
	"io/ioutil"
)

// ListTestCases returns a list of test cases
func (c *Client) ListTestCases(organization string) (bool, []byte, error) {
	path := "/organisations/" + organization + "/test_cases"

	return c.fetch(path)
}

// TestCaseValidate will send a test case definition (JS) to the API
// to validate.
func (c *Client) TestCaseValidate(organization string, fileName string, data io.Reader) (bool, string, error) {
	// TODO how to pass options here?
	//      defining a struct maybe, but where?
	//      finally: add options here
	extraParams := map[string]string{
		"organisation_uid": organization,
	}

	req, err := fileUploadRequest(c.APIEndpoint+"/test_cases/validate", "POST", extraParams, "test_case[javascript_definition]", fileName, "application/javascript", data)
	if err != nil {
		return false, "", err
	}

	response, err := c.doRequestRaw(req)
	if err != nil {
		return false, "", err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, "", err
	}

	defer response.Body.Close()

	if response.StatusCode != 200 {
		return false, string(body), nil
	}

	return true, string(body), nil
}

// TestCaseCreate will send a test case definition (JS) to the API
// to create it.
func (c *Client) TestCaseCreate(organization string, testCaseName string, fileName string, data io.Reader) (bool, string, error) {
	// TODO how to pass options here?
	//      defining a struct maybe, but where?
	//      finally: add options here
	extraParams := map[string]string{
		"test_case[name]": testCaseName,
	}

	req, err := fileUploadRequest(c.APIEndpoint+"/organisations/"+organization+"/test_cases", "POST", extraParams, "test_case[javascript_definition]", fileName, "application/javascript", data)
	if err != nil {
		return false, "", err
	}

	response, err := c.doRequestRaw(req)
	if err != nil {
		return false, "", err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, "", err
	}

	defer response.Body.Close()

	return false, string(body), nil
}

// TestCaseUpdate will send a test case definition (JS) to the API
// to update an existing test case it.
func (c *Client) TestCaseUpdate(testCaseUID string, fileName string, data io.Reader) (bool, string, error) {
	// TODO how to pass options here?
	//      defining a struct maybe, but where?
	//      finally: add options here
	extraParams := map[string]string{}

	req, err := fileUploadRequest(c.APIEndpoint+"/test_cases/"+testCaseUID, "PATCH", extraParams, "test_case[javascript_definition]", fileName, "application/javascript", data)
	if err != nil {
		return false, "", err
	}

	response, err := c.doRequestRaw(req)
	if err != nil {
		return false, "", err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, "", err
	}

	defer response.Body.Close()

	return false, string(body), nil
}

// DownloadTestCaseDefinition returns the JS definition
// of a given test case
func (c *Client) DownloadTestCaseDefinition(uid string) (bool, []byte, error) {
	path := "/test_cases/" + uid + "/download"

	return c.fetch(path)
}
<commit_msg>Report success if test case was updated (#34)<commit_after>package api

import (
	"io"
	"io/ioutil"
)

// ListTestCases returns a list of test cases
func (c *Client) ListTestCases(organization string) (bool, []byte, error) {
	path := "/organisations/" + organization + "/test_cases"

	return c.fetch(path)
}

// TestCaseValidate will send a test case definition (JS) to the API
// to validate.
func (c *Client) TestCaseValidate(organization string, fileName string, data io.Reader) (bool, string, error) {
	// TODO how to pass options here?
	//      defining a struct maybe, but where?
	//      finally: add options here
	extraParams := map[string]string{
		"organisation_uid": organization,
	}

	req, err := fileUploadRequest(c.APIEndpoint+"/test_cases/validate", "POST", extraParams, "test_case[javascript_definition]", fileName, "application/javascript", data)
	if err != nil {
		return false, "", err
	}

	response, err := c.doRequestRaw(req)
	if err != nil {
		return false, "", err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, "", err
	}

	defer response.Body.Close()

	if response.StatusCode != 200 {
		return false, string(body), nil
	}

	return true, string(body), nil
}

// TestCaseCreate will send a test case definition (JS) to the API
// to create it.
func (c *Client) TestCaseCreate(organization string, testCaseName string, fileName string, data io.Reader) (bool, string, error) {
	// TODO how to pass options here?
	//      defining a struct maybe, but where?
	//      finally: add options here
	extraParams := map[string]string{
		"test_case[name]": testCaseName,
	}

	req, err := fileUploadRequest(c.APIEndpoint+"/organisations/"+organization+"/test_cases", "POST", extraParams, "test_case[javascript_definition]", fileName, "application/javascript", data)
	if err != nil {
		return false, "", err
	}

	response, err := c.doRequestRaw(req)
	if err != nil {
		return false, "", err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, "", err
	}

	defer response.Body.Close()

	return false, string(body), nil
}

// TestCaseUpdate will send a test case definition (JS) to the API
// to update an existing test case it.
func (c *Client) TestCaseUpdate(testCaseUID string, fileName string, data io.Reader) (bool, string, error) {
	// TODO how to pass options here?
	//      defining a struct maybe, but where?
	//      finally: add options here
	extraParams := map[string]string{}

	req, err := fileUploadRequest(c.APIEndpoint+"/test_cases/"+testCaseUID, "PATCH", extraParams, "test_case[javascript_definition]", fileName, "application/javascript", data)
	if err != nil {
		return false, "", err
	}

	response, err := c.doRequestRaw(req)
	if err != nil {
		return false, "", err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, "", err
	}

	defer response.Body.Close()

	return true, string(body), nil
}

// DownloadTestCaseDefinition returns the JS definition
// of a given test case
func (c *Client) DownloadTestCaseDefinition(uid string) (bool, []byte, error) {
	path := "/test_cases/" + uid + "/download"

	return c.fetch(path)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package audit

import (
	"context"
	"fmt"
	"sync"

	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/klog/v2"
)

// The key type is unexported to prevent collisions
type key int

const (
	// auditAnnotationsKey is the context key for the audit annotations.
	// TODO: consolidate all audit info under the AuditContext, rather than storing 3 separate keys.
	auditAnnotationsKey key = iota

	// auditKey is the context key for storing the audit event that is being
	// captured and the evaluated policy that applies to the given request.
	auditKey

	// auditAnnotationsMutexKey is the context key for the audit annotations mutex.
	auditAnnotationsMutexKey
)

// annotations = *[]annotation instead of a map to preserve order of insertions
type annotation struct {
	key, value string
}

// WithAuditAnnotations returns a new context that can store audit annotations
// via the AddAuditAnnotation function.  This function is meant to be called from
// an early request handler to allow all later layers to set audit annotations.
// This is required to support flows where handlers that come before WithAudit
// (such as WithAuthentication) wish to set audit annotations.
func WithAuditAnnotations(parent context.Context) context.Context {
	// this should never really happen, but prevent double registration of this slice
	if _, ok := parent.Value(auditAnnotationsKey).(*[]annotation); ok {
		return parent
	}
	parent = withAuditAnnotationsMutex(parent)

	var annotations []annotation // avoid allocations until we actually need it
	return genericapirequest.WithValue(parent, auditAnnotationsKey, &annotations)
}

// AddAuditAnnotation sets the audit annotation for the given key, value pair.
// It is safe to call at most parts of request flow that come after WithAuditAnnotations.
// The notable exception being that this function must not be called via a
// defer statement (i.e. after ServeHTTP) in a handler that runs before WithAudit
// as at that point the audit event has already been sent to the audit sink.
// Handlers that are unaware of their position in the overall request flow should
// prefer AddAuditAnnotation over LogAnnotation to avoid dropping annotations.
func AddAuditAnnotation(ctx context.Context, key, value string) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		klog.ErrorS(nil, "Attempted to add audit annotations from unsupported request chain", "annotation", fmt.Sprintf("%s=%s", key, value))
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	ae := AuditEventFrom(ctx)
	var ctxAnnotations *[]annotation
	if ae == nil {
		ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
	}

	addAuditAnnotationLocked(ae, ctxAnnotations, key, value)
}

// AddAuditAnnotations is a bulk version of AddAuditAnnotation. Refer to AddAuditAnnotation for
// restrictions on when this can be called.
// keysAndValues are the key-value pairs to add, and must have an even number of items.
func AddAuditAnnotations(ctx context.Context, keysAndValues ...string) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		klog.ErrorS(nil, "Attempted to add audit annotations from unsupported request chain", "annotations", keysAndValues)
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	ae := AuditEventFrom(ctx)
	var ctxAnnotations *[]annotation
	if ae == nil {
		ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
	}

	if len(keysAndValues)%2 != 0 {
		klog.Errorf("Dropping mismatched audit annotation %q", keysAndValues[len(keysAndValues)-1])
	}
	for i := 0; i < len(keysAndValues); i += 2 {
		addAuditAnnotationLocked(ae, ctxAnnotations, keysAndValues[i], keysAndValues[i+1])
	}
}

// AddAuditAnnotationsMap is a bulk version of AddAuditAnnotation. Refer to AddAuditAnnotation for
// restrictions on when this can be called.
func AddAuditAnnotationsMap(ctx context.Context, annotations map[string]string) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		klog.ErrorS(nil, "Attempted to add audit annotations from unsupported request chain", "annotations", annotations)
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	ae := AuditEventFrom(ctx)
	var ctxAnnotations *[]annotation
	if ae == nil {
		ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
	}

	for k, v := range annotations {
		addAuditAnnotationLocked(ae, ctxAnnotations, k, v)
	}
}

// addAuditAnnotationLocked is the shared code for recording an audit annotation. This method should
// only be called while the auditAnnotationsMutex is locked.
func addAuditAnnotationLocked(ae *auditinternal.Event, annotations *[]annotation, key, value string) {
	if ae != nil {
		logAnnotation(ae, key, value)
	} else if annotations != nil {
		*annotations = append(*annotations, annotation{key: key, value: value})
	}
}

// This is private to prevent reads/write to the slice from outside of this package.
// The audit event should be directly read to get access to the annotations.
func addAuditAnnotationsFrom(ctx context.Context, ev *auditinternal.Event) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		klog.Errorf("Attempted to copy audit annotations from unsupported request chain")
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	annotations, ok := ctx.Value(auditAnnotationsKey).(*[]annotation)
	if !ok {
		return // no annotations to copy
	}

	for _, kv := range *annotations {
		logAnnotation(ev, kv.key, kv.value)
	}
}

// LogAnnotation fills in the Annotations according to the key value pair.
func logAnnotation(ae *auditinternal.Event, key, value string) {
	if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
		return
	}
	if ae.Annotations == nil {
		ae.Annotations = make(map[string]string)
	}
	if v, ok := ae.Annotations[key]; ok && v != value {
		klog.Warningf("Failed to set annotations[%q] to %q for audit:%q, it has already been set to %q", key, value, ae.AuditID, ae.Annotations[key])
		return
	}
	ae.Annotations[key] = value
}

// WithAuditContext returns a new context that stores the pair of the audit
// configuration object that applies to the given request and
// the audit event that is going to be written to the API audit log.
func WithAuditContext(parent context.Context, ev *AuditContext) context.Context {
	parent = withAuditAnnotationsMutex(parent)
	return genericapirequest.WithValue(parent, auditKey, ev)
}

// AuditEventFrom returns the audit event struct on the ctx
func AuditEventFrom(ctx context.Context) *auditinternal.Event {
	if o := AuditContextFrom(ctx); o != nil {
		return o.Event
	}
	return nil
}

// AuditContextFrom returns the pair of the audit configuration object
// that applies to the given request and the audit event that is going to
// be written to the API audit log.
func AuditContextFrom(ctx context.Context) *AuditContext {
	ev, _ := ctx.Value(auditKey).(*AuditContext)
	return ev
}

// WithAuditAnnotationMutex adds a mutex for guarding context.AddAuditAnnotation.
func withAuditAnnotationsMutex(parent context.Context) context.Context {
	if _, ok := parent.Value(auditAnnotationsMutexKey).(*sync.Mutex); ok {
		return parent
	}
	var mutex sync.Mutex
	return genericapirequest.WithValue(parent, auditAnnotationsMutexKey, &mutex)
}

// AuditAnnotationsMutex returns the audit annotations mutex from the context.
func auditAnnotationsMutex(ctx context.Context) (*sync.Mutex, bool) {
	mutex, ok := ctx.Value(auditAnnotationsMutexKey).(*sync.Mutex)
	return mutex, ok
}
<commit_msg>Avoid log spam in servers without auditing enabled<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package audit

import (
	"context"
	"sync"

	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/klog/v2"
)

// The key type is unexported to prevent collisions
type key int

const (
	// auditAnnotationsKey is the context key for the audit annotations.
	// TODO: consolidate all audit info under the AuditContext, rather than storing 3 separate keys.
	auditAnnotationsKey key = iota

	// auditKey is the context key for storing the audit event that is being
	// captured and the evaluated policy that applies to the given request.
	auditKey

	// auditAnnotationsMutexKey is the context key for the audit annotations mutex.
	auditAnnotationsMutexKey
)

// annotations = *[]annotation instead of a map to preserve order of insertions
type annotation struct {
	key, value string
}

// WithAuditAnnotations returns a new context that can store audit annotations
// via the AddAuditAnnotation function.  This function is meant to be called from
// an early request handler to allow all later layers to set audit annotations.
// This is required to support flows where handlers that come before WithAudit
// (such as WithAuthentication) wish to set audit annotations.
func WithAuditAnnotations(parent context.Context) context.Context {
	// this should never really happen, but prevent double registration of this slice
	if _, ok := parent.Value(auditAnnotationsKey).(*[]annotation); ok {
		return parent
	}
	parent = withAuditAnnotationsMutex(parent)

	var annotations []annotation // avoid allocations until we actually need it
	return genericapirequest.WithValue(parent, auditAnnotationsKey, &annotations)
}

// AddAuditAnnotation sets the audit annotation for the given key, value pair.
// It is safe to call at most parts of request flow that come after WithAuditAnnotations.
// The notable exception being that this function must not be called via a
// defer statement (i.e. after ServeHTTP) in a handler that runs before WithAudit
// as at that point the audit event has already been sent to the audit sink.
// Handlers that are unaware of their position in the overall request flow should
// prefer AddAuditAnnotation over LogAnnotation to avoid dropping annotations.
func AddAuditAnnotation(ctx context.Context, key, value string) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		// auditing is not enabled
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	ae := AuditEventFrom(ctx)
	var ctxAnnotations *[]annotation
	if ae == nil {
		ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
	}

	addAuditAnnotationLocked(ae, ctxAnnotations, key, value)
}

// AddAuditAnnotations is a bulk version of AddAuditAnnotation. Refer to AddAuditAnnotation for
// restrictions on when this can be called.
// keysAndValues are the key-value pairs to add, and must have an even number of items.
func AddAuditAnnotations(ctx context.Context, keysAndValues ...string) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		// auditing is not enabled
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	ae := AuditEventFrom(ctx)
	var ctxAnnotations *[]annotation
	if ae == nil {
		ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
	}

	if len(keysAndValues)%2 != 0 {
		klog.Errorf("Dropping mismatched audit annotation %q", keysAndValues[len(keysAndValues)-1])
	}
	for i := 0; i < len(keysAndValues); i += 2 {
		addAuditAnnotationLocked(ae, ctxAnnotations, keysAndValues[i], keysAndValues[i+1])
	}
}

// AddAuditAnnotationsMap is a bulk version of AddAuditAnnotation. Refer to AddAuditAnnotation for
// restrictions on when this can be called.
func AddAuditAnnotationsMap(ctx context.Context, annotations map[string]string) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		// auditing is not enabled
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	ae := AuditEventFrom(ctx)
	var ctxAnnotations *[]annotation
	if ae == nil {
		ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
	}

	for k, v := range annotations {
		addAuditAnnotationLocked(ae, ctxAnnotations, k, v)
	}
}

// addAuditAnnotationLocked is the shared code for recording an audit annotation. This method should
// only be called while the auditAnnotationsMutex is locked.
func addAuditAnnotationLocked(ae *auditinternal.Event, annotations *[]annotation, key, value string) {
	if ae != nil {
		logAnnotation(ae, key, value)
	} else if annotations != nil {
		*annotations = append(*annotations, annotation{key: key, value: value})
	}
}

// This is private to prevent reads/write to the slice from outside of this package.
// The audit event should be directly read to get access to the annotations.
func addAuditAnnotationsFrom(ctx context.Context, ev *auditinternal.Event) {
	mutex, ok := auditAnnotationsMutex(ctx)
	if !ok {
		// auditing is not enabled
		return
	}

	mutex.Lock()
	defer mutex.Unlock()

	annotations, ok := ctx.Value(auditAnnotationsKey).(*[]annotation)
	if !ok {
		return // no annotations to copy
	}

	for _, kv := range *annotations {
		logAnnotation(ev, kv.key, kv.value)
	}
}

// LogAnnotation fills in the Annotations according to the key value pair.
func logAnnotation(ae *auditinternal.Event, key, value string) {
	if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
		return
	}
	if ae.Annotations == nil {
		ae.Annotations = make(map[string]string)
	}
	if v, ok := ae.Annotations[key]; ok && v != value {
		klog.Warningf("Failed to set annotations[%q] to %q for audit:%q, it has already been set to %q", key, value, ae.AuditID, ae.Annotations[key])
		return
	}
	ae.Annotations[key] = value
}

// WithAuditContext returns a new context that stores the pair of the audit
// configuration object that applies to the given request and
// the audit event that is going to be written to the API audit log.
func WithAuditContext(parent context.Context, ev *AuditContext) context.Context {
	parent = withAuditAnnotationsMutex(parent)
	return genericapirequest.WithValue(parent, auditKey, ev)
}

// AuditEventFrom returns the audit event struct on the ctx
func AuditEventFrom(ctx context.Context) *auditinternal.Event {
	if o := AuditContextFrom(ctx); o != nil {
		return o.Event
	}
	return nil
}

// AuditContextFrom returns the pair of the audit configuration object
// that applies to the given request and the audit event that is going to
// be written to the API audit log.
func AuditContextFrom(ctx context.Context) *AuditContext {
	ev, _ := ctx.Value(auditKey).(*AuditContext)
	return ev
}

// WithAuditAnnotationMutex adds a mutex for guarding context.AddAuditAnnotation.
func withAuditAnnotationsMutex(parent context.Context) context.Context {
	if _, ok := parent.Value(auditAnnotationsMutexKey).(*sync.Mutex); ok {
		return parent
	}
	var mutex sync.Mutex
	return genericapirequest.WithValue(parent, auditAnnotationsMutexKey, &mutex)
}

// AuditAnnotationsMutex returns the audit annotations mutex from the context.
func auditAnnotationsMutex(ctx context.Context) (*sync.Mutex, bool) {
	mutex, ok := ctx.Value(auditAnnotationsMutexKey).(*sync.Mutex)
	return mutex, ok
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

// XMl Structs
type Tag struct {
	EnumID      int    `xml:"enumID,attr"`
	Type        string `xml:"type,attr"`
	Value       int    `xml:"value,attr"`
	StringValue string `xml:",chardata"` //populated when Type=="string"
}

type Entity struct {
	Version string `xml:"version,attr"`
	CardID  string `xml:"CardID,attr"`
	Tags    []Tag  `xml:"Tag"`
}

type CardDefs struct {
	Entities []Entity `xml:"Entity"`
}

// Conversion tables, credit to https://github.com/Sembiance/hearthstonejson
func EnumIDToString() map[int]string {
	return map[int]string{
		185: "CardName",
		183: "CardSet",
		202: "CardType",
		201: "Faction",
		199: "Class",
		203: "Rarity",
		48:  "Cost",
		251: "AttackVisualType",
		184: "CardTextInHand",
		47:  "Atk",
		45:  "Health",
		321: "Collectible",
		342: "ArtistName",
		351: "FlavorText",
		32:  "TriggerVisual",
		330: "EnchantmentBirthVisual",
		331: "EnchantmentIdleVisual",
		268: "DevState",
		365: "HowToGetThisGoldCard",
		190: "Taunt",
		364: "HowToGetThisCard",
		338: "OneTurnEffect",
		293: "Morph",
		208: "Freeze",
		252: "CardTextInPlay",
		325: "TargetingArrowText",
		189: "Windfury",
		218: "Battlecry",
		200: "Race",
		192: "Spellpower",
		187: "Durability",
		197: "Charge",
		362: "Aura",
		361: "HealTarget",
		349: "ImmuneToSpellpower",
		194: "Divine Shield",
		350: "AdjacentBuff",
		217: "Deathrattle",
		191: "Stealth",
		220: "Combo",
		339: "Silence",
		212: "Enrage",
		370: "AffectedBySpellPower",
		240: "Cant Be Damaged",
		114: "Elite",
		219: "Secret",
		363: "Poisonous",
		215: "Recall",
		340: "Counter",
		205: "Summoned",
		367: "AIMustPlay",
		335: "InvisibleDeathrattle",
		377: "UKNOWN_HasOnDrawEffect",
		388: "SparePart",
		389: "UNKNOWN_DuneMaulShaman",
		380: "UNKNOWN_Blackrock_Heroes",
		396: "UNKNOWN_Grand_Tournement_Fallen_Hero",
		401: "UNKNOWN_BroodAffliction",
		402: "UNKNOWN_Intense_Gaze",
		403: "Inspire",
		404: "UNKNOWN_Grand_Tournament_Arcane_Blast",
	}
}

func CardSetIDToString() map[int]string {
	return map[int]string{
		2:  "Basic",
		3:  "Classic",
		4:  "Reward",
		5:  "Missions",
		7:  "System",
		8:  "Debug",
		11: "Promotion",
		12: "Curse of Naxxramas",
		13: "Goblins vs Gnomes",
		14: "Blackrock Mountain",
		15: "The Grand Tournament",
		16: "Credits",
		17: "Hero Skins",
		18: "Tavern Brawl",
	}
}

func CardTypeIDToString() map[int]string {
	return map[int]string{
		3:  "Hero",
		4:  "Minion",
		5:  "Spell",
		6:  "Enchantment",
		7:  "Weapon",
		10: "Hero Power",
	}
}

func RarityIDToString() map[int]string {
	return map[int]string{
		0: "undefined",
		1: "Common",
		2: "Free",
		3: "Rare",
		4: "Epic",
		5: "Legendary",
	}
}

func CardRaceIDToString() map[int]string {
	return map[int]string{
		14: "Murloc",
		15: "Demon",
		20: "Beast",
		21: "Totem",
		23: "Pirate",
		24: "Dragon",
		17: "Mech",
	}
}

func ClassIDToString() map[int]string {
	return map[int]string{
		0:  "undefined",
		2:  "Druid",
		3:  "Hunter",
		4:  "Mage",
		5:  "Paladin",
		6:  "Priest",
		7:  "Rogue",
		8:  "Shaman",
		9:  "Warlock",
		10: "Warrior",
		11: "Dream",
	}
}

func FactionIDToString() map[int]string {
	return map[int]string{
		1: "Horde",
		2: "Alliance",
		3: "Neutral",
	}
}

func Mechanics() []string {
	return []string{
		"Windfury", "Combo", "Secret", "Battlecry", "Deathrattle",
		"Taunt", "Stealth", "Spellpower", "Enrage", "Freeze",
		"Charge", "Overload", "Divine Shield", "Silence", "Morph",
		"OneTurnEffect", "Poisonous", "Aura", "AdjacentBuff",
		"HealTarget", "GrantCharge", "ImmuneToSpellpower",
		"AffectedBySpellPower", "Summoned", "Inspire",
	}
}

func IsMechanic(s string) bool {
	for _, mechanic := range Mechanics() {
		if strings.ToLower(mechanic) == strings.ToLower(s) {
			return true
		}
	}
	return false
}

// JSON Card struct
type JsonCard struct {
	Name        string   `json:"name"`
	ID          string   `json:"id"`
	Attack      int      `json:"attack,omitempty"`
	Health      int      `json:"health,omitempty"`
	Cost        int      `json:"cost,omitempty"`
	Durability  int      `json:"durability,omitempty"`
	Type        string   `json:"type"`
	Rarity      string   `json:"rarity"`
	Text        string   `json:"text,omitempty"`
	Flavor      string   `json:"flavor,omitempty"`
	Set         string   `json:"set"`
	Class       string   `json:"class,omitempty"`
	Race        string   `json:"race,omitempty"`
	Mechanics   []string `json:"mechanics,omitempty"`
	Faction     string   `json:"faction,omitempty"`
	Collectible bool     `json:"collectible"`
}

func EntityToJson(e Entity) JsonCard {
	var card JsonCard
	card.ID = e.CardID
	for _, tag := range e.Tags {
		enum := EnumIDToString()[tag.EnumID]
		switch {
		case enum == "CardName":
			card.Name = tag.StringValue
		case enum == "CardSet":
			card.Set = CardSetIDToString()[tag.Value]
		case enum == "CardType":
			card.Type = CardTypeIDToString()[tag.Value]
		case enum == "CardRace":
			card.Race = CardRaceIDToString()[tag.Value]
		case enum == "Class":
			card.Class = ClassIDToString()[tag.Value]
		case enum == "Rarity":
			card.Rarity = RarityIDToString()[tag.Value]
		case enum == "Faction":
			card.Faction = FactionIDToString()[tag.Value]
		case enum == "Atk":
			card.Attack = tag.Value
		case enum == "Health":
			card.Health = tag.Value
		case enum == "Durability":
			card.Durability = tag.Value
		case enum == "Cost":
			card.Cost = tag.Value
		case enum == "CardTextInHand":
			card.Text = tag.StringValue
		case enum == "FlavorText":
			card.Flavor = tag.StringValue
		case enum == "Collectible":
			card.Collectible = tag.Value == 1
		case IsMechanic(enum):
			card.Mechanics = append(card.Mechanics, enum)
		}

	}
	if len(card.Class) == 0 {
		card.Class = "Neutral"
	}
	return card
}

func PrintUsageAndExit() {
	fmt.Println("Usage: hearthstone-json path/to/carddef.xml")
	os.Exit(1)
}

func main() {
	// Get input from stdin or from an xml file supplied as the first
	// positional argument
	input := os.Stdin
	if len(os.Args) > 1 {
		arg := os.Args[1]
		// Handle help -- just in case!
		if arg == "-h" || arg == "--help" {
			PrintUsageAndExit()
		}
		// Open the xml file
		xmlPath, err := filepath.Abs(arg)
		if err != nil {
			fmt.Println(err)
			PrintUsageAndExit()
		}
		input, err = os.Open(xmlPath)
		if err != nil {
			fmt.Println(err)
			PrintUsageAndExit()
		}
	}
	defer input.Close()
	// Decode the XML
	var cardDefs CardDefs
	if err := xml.NewDecoder(input).Decode(&cardDefs); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// Transform the data
	var jsonCards []JsonCard
	for _, entity := range cardDefs.Entities {
		jsonCards = append(jsonCards, EntityToJson(entity))
	}
	// Output JSON!
	jsonData, err := json.MarshalIndent(jsonCards, "", " ")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	output := os.Stdout
	defer output.Close()
	output.Write(jsonData)
}
<commit_msg>Attempt to match the API provided by http://hearthstonejson.com<commit_after>package main

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

// XMl Structs
type Tag struct {
	EnumID      int    `xml:"enumID,attr"`
	Type        string `xml:"type,attr"`
	Value       int    `xml:"value,attr"`
	StringValue string `xml:",chardata"` //populated when Type=="string"
}

type Entity struct {
	Version string `xml:"version,attr"`
	CardID  string `xml:"CardID,attr"`
	Tags    []Tag  `xml:"Tag"`
}

type CardDefs struct {
	Entities []Entity `xml:"Entity"`
}

// Conversion tables, credit to https://github.com/Sembiance/hearthstonejson
func EnumIDToString() map[int]string {
	return map[int]string{
		185: "CardName",
		183: "CardSet",
		202: "CardType",
		201: "Faction",
		199: "Class",
		203: "Rarity",
		48:  "Cost",
		251: "AttackVisualType",
		184: "CardTextInHand",
		47:  "Atk",
		45:  "Health",
		321: "Collectible",
		342: "ArtistName",
		351: "FlavorText",
		32:  "TriggerVisual",
		330: "EnchantmentBirthVisual",
		331: "EnchantmentIdleVisual",
		268: "DevState",
		365: "HowToGetThisGoldCard",
		190: "Taunt",
		364: "HowToGetThisCard",
		338: "OneTurnEffect",
		293: "Morph",
		208: "Freeze",
		252: "CardTextInPlay",
		325: "TargetingArrowText",
		189: "Windfury",
		218: "Battlecry",
		200: "Race",
		192: "Spellpower",
		187: "Durability",
		197: "Charge",
		362: "Aura",
		361: "HealTarget",
		349: "ImmuneToSpellpower",
		194: "Divine Shield",
		350: "AdjacentBuff",
		217: "Deathrattle",
		191: "Stealth",
		220: "Combo",
		339: "Silence",
		212: "Enrage",
		370: "AffectedBySpellPower",
		240: "Cant Be Damaged",
		114: "Elite",
		219: "Secret",
		363: "Poisonous",
		215: "Recall",
		340: "Counter",
		205: "Summoned",
		367: "AIMustPlay",
		335: "InvisibleDeathrattle",
		377: "UKNOWN_HasOnDrawEffect",
		388: "SparePart",
		389: "UNKNOWN_DuneMaulShaman",
		380: "UNKNOWN_Blackrock_Heroes",
		396: "UNKNOWN_Grand_Tournement_Fallen_Hero",
		401: "UNKNOWN_BroodAffliction",
		402: "UNKNOWN_Intense_Gaze",
		403: "Inspire",
		404: "UNKNOWN_Grand_Tournament_Arcane_Blast",
	}
}

func CardSetIDToString() map[int]string {
	return map[int]string{
		2:  "Basic",
		3:  "Classic",
		4:  "Reward",
		5:  "Missions",
		7:  "System",
		8:  "Debug",
		11: "Promotion",
		12: "Curse of Naxxramas",
		13: "Goblins vs Gnomes",
		14: "Blackrock Mountain",
		15: "The Grand Tournament",
		16: "Credits",
		17: "Hero Skins",
		18: "Tavern Brawl",
	}
}

func CardTypeIDToString() map[int]string {
	return map[int]string{
		3:  "Hero",
		4:  "Minion",
		5:  "Spell",
		6:  "Enchantment",
		7:  "Weapon",
		10: "Hero Power",
	}
}

func RarityIDToString() map[int]string {
	return map[int]string{
		0: "undefined",
		1: "Common",
		2: "Free",
		3: "Rare",
		4: "Epic",
		5: "Legendary",
	}
}

func CardRaceIDToString() map[int]string {
	return map[int]string{
		14: "Murloc",
		15: "Demon",
		20: "Beast",
		21: "Totem",
		23: "Pirate",
		24: "Dragon",
		17: "Mech",
	}
}

func ClassIDToString() map[int]string {
	return map[int]string{
		0:  "undefined",
		2:  "Druid",
		3:  "Hunter",
		4:  "Mage",
		5:  "Paladin",
		6:  "Priest",
		7:  "Rogue",
		8:  "Shaman",
		9:  "Warlock",
		10: "Warrior",
		11: "Dream",
	}
}

func FactionIDToString() map[int]string {
	return map[int]string{
		1: "Horde",
		2: "Alliance",
		3: "Neutral",
	}
}

func Mechanics() []string {
	return []string{
		"Windfury", "Combo", "Secret", "Battlecry", "Deathrattle",
		"Taunt", "Stealth", "Spellpower", "Enrage", "Freeze",
		"Charge", "Overload", "Divine Shield", "Silence", "Morph",
		"OneTurnEffect", "Poisonous", "Aura", "AdjacentBuff",
		"HealTarget", "GrantCharge", "ImmuneToSpellpower",
		"AffectedBySpellPower", "Summoned", "Inspire",
	}
}

func IsMechanic(s string) bool {
	for _, mechanic := range Mechanics() {
		if strings.ToLower(mechanic) == strings.ToLower(s) {
			return true
		}
	}
	return false
}

// JSON Card struct
type JsonCard struct {
	Name         string   `json:"name"`
	ID           string   `json:"id"`
	Attack       int      `json:"attack,omitempty"`
	Health       int      `json:"health,omitempty"`
	Cost         int      `json:"cost,omitempty"`
	Durability   int      `json:"durability,omitempty"`
	Type         string   `json:"type"`
	Rarity       string   `json:"rarity"`
	Text         string   `json:"text,omitempty"`
	InPlayText   string   `json:"inPlayText,omitempty"`
	Flavor       string   `json:"flavor,omitempty"`
	Set          string   `json:"set"`
	Class        string   `json:"playerClass,omitempty"`
	Race         string   `json:"race,omitempty"`
	Mechanics    []string `json:"mechanics,omitempty"`
	Faction      string   `json:"faction,omitempty"`
	Artist       string   `json:"artist,omitempty"`
	Collectible  bool     `json:"collectible"`
	Elite        bool     `json:"elite,omitempty"`
	HowToGet     string   `json:"howToGet,omitempty"`
	HowToGetGold string   `json:"howToGetGold,omitempty"`
}

func EntityToJson(e Entity) JsonCard {
	var card JsonCard
	card.ID = e.CardID
	for _, tag := range e.Tags {
		enum := EnumIDToString()[tag.EnumID]
		switch {
		case enum == "CardName":
			card.Name = tag.StringValue
		case enum == "CardSet":
			card.Set = CardSetIDToString()[tag.Value]
		case enum == "CardType":
			card.Type = CardTypeIDToString()[tag.Value]
		case enum == "CardRace":
			card.Race = CardRaceIDToString()[tag.Value]
		case enum == "Class":
			card.Class = ClassIDToString()[tag.Value]
		case enum == "Rarity":
			card.Rarity = RarityIDToString()[tag.Value]
		case enum == "Faction":
			card.Faction = FactionIDToString()[tag.Value]
		case enum == "Atk":
			card.Attack = tag.Value
		case enum == "Health":
			card.Health = tag.Value
		case enum == "Durability":
			card.Durability = tag.Value
		case enum == "Cost":
			card.Cost = tag.Value
		case enum == "CardTextInHand":
			card.Text = tag.StringValue
		case enum == "CardTextInPlay":
			card.InPlayText = tag.StringValue
		case enum == "FlavorText":
			card.Flavor = tag.StringValue
		case enum == "Collectible":
			card.Collectible = tag.Value == 1
		case enum == "ArtistName":
			card.Artist = tag.StringValue
		case enum == "Elite":
			card.Elite = tag.Value == 1
		case enum == "HowToGetThisCard":
			card.HowToGet = tag.StringValue
		case enum == "HowToGetThisGoldCard":
			card.HowToGetGold = tag.StringValue
		case IsMechanic(enum):
			card.Mechanics = append(card.Mechanics, enum)
		}

	}
	if len(card.Class) == 0 {
		card.Class = "Neutral"
	}
	return card
}

func PrintUsageAndExit() {
	fmt.Println("Transform a Hearthstone xml file extracted from cardxml0.unity3d to JSON")
	fmt.Println("Usage: hearthstone-json path/to/enUS.txt")
	os.Exit(1)
}

func main() {
	// Get input from stdin or from an xml file supplied as the first
	// positional argument
	input := os.Stdin
	if len(os.Args) > 1 {
		arg := os.Args[1]
		// Handle help -- just in case!
		if arg == "-h" || arg == "--help" {
			PrintUsageAndExit()
		}
		// Open the xml file
		xmlPath, err := filepath.Abs(arg)
		if err != nil {
			fmt.Println(err)
			PrintUsageAndExit()
		}
		input, err = os.Open(xmlPath)
		if err != nil {
			fmt.Println(err)
			PrintUsageAndExit()
		}
	}
	defer input.Close()
	// Decode the XML
	var cardDefs CardDefs
	if err := xml.NewDecoder(input).Decode(&cardDefs); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// Transform the data
	var jsonCards []JsonCard
	for _, entity := range cardDefs.Entities {
		jsonCards = append(jsonCards, EntityToJson(entity))
	}
	// Output JSON!
	jsonData, err := json.MarshalIndent(jsonCards, "", " ")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	output := os.Stdout
	defer output.Close()
	output.Write(jsonData)
}
<|endoftext|>


<commit_before>package cuckoofilter

import (
	"encoding/json"
	"sync"

	"github.com/seiflotfy/skizze/counters/abstract"
	"github.com/seiflotfy/skizze/counters/wrappers/cuckoofilter/cuckoofilter"
	"github.com/seiflotfy/skizze/storage"
	"github.com/seiflotfy/skizze/utils"
)

var logger = utils.GetLogger()

/*
Domain is the toplevel domain to control the HLL implementation
*/
type Domain struct {
	*abstract.Info
	impl *cuckoofilter.CuckooFilter
	lock sync.RWMutex
}

/*
NewDomain ...
*/
func NewDomain(info *abstract.Info) (*Domain, error) {
	return &Domain{info, cuckoofilter.NewCuckooFilter(info), sync.RWMutex{}}, nil
}

/*
Add ...
*/
func (d *Domain) Add(value []byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := d.impl.InsertUnique(value)
	err := d.Save()
	return ok, err
}

/*
AddMultiple ...
*/
func (d *Domain) AddMultiple(values [][]byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := true
	for _, value := range values {
		okk := d.impl.InsertUnique(value)
		if okk == false {
			ok = okk
		}
	}
	err := d.Save()
	return ok, err
}

/*
Remove ...
*/
func (d *Domain) Remove(value []byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := d.impl.Delete(value)
	err := d.Save()
	return ok, err
}

/*
RemoveMultiple ...
*/
func (d *Domain) RemoveMultiple(values [][]byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := true
	for _, value := range values {
		okk := d.impl.Delete(value)
		if okk == false {
			ok = okk
		}
	}
	err := d.Save()
	return ok, err
}

/*
GetCount ...
*/
func (d *Domain) GetCount() uint {
	return uint(d.impl.GetCount())
}

/*
Clear ...
*/
func (d *Domain) Clear() (bool, error) {
	return true, nil
}

/*
Save ...
*/
func (d *Domain) Save() error {
	count := d.impl.GetCount()
	d.Info.State["count"] = uint64(count)
	infoData, err := json.Marshal(d.Info)
	if err != nil {
		return err
	}
	err = storage.GetManager().SaveInfo(d.Info.ID, infoData)
	return err
}
<commit_msg>Save info explicity on cuckoofilter NewDomain<commit_after>package cuckoofilter

import (
	"encoding/json"
	"sync"

	"github.com/seiflotfy/skizze/counters/abstract"
	"github.com/seiflotfy/skizze/counters/wrappers/cuckoofilter/cuckoofilter"
	"github.com/seiflotfy/skizze/storage"
	"github.com/seiflotfy/skizze/utils"
)

var logger = utils.GetLogger()

/*
Domain is the toplevel domain to control the HLL implementation
*/
type Domain struct {
	*abstract.Info
	impl *cuckoofilter.CuckooFilter
	lock sync.RWMutex
}

/*
NewDomain ...
*/
func NewDomain(info *abstract.Info) (*Domain, error) {
	d := &Domain{info, cuckoofilter.NewCuckooFilter(info), sync.RWMutex{}}
	d.Save()
	return d, nil
}

/*
Add ...
*/
func (d *Domain) Add(value []byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := d.impl.InsertUnique(value)
	err := d.Save()
	return ok, err
}

/*
AddMultiple ...
*/
func (d *Domain) AddMultiple(values [][]byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := true
	for _, value := range values {
		okk := d.impl.InsertUnique(value)
		if okk == false {
			ok = okk
		}
	}
	err := d.Save()
	return ok, err
}

/*
Remove ...
*/
func (d *Domain) Remove(value []byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := d.impl.Delete(value)
	err := d.Save()
	return ok, err
}

/*
RemoveMultiple ...
*/
func (d *Domain) RemoveMultiple(values [][]byte) (bool, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	ok := true
	for _, value := range values {
		okk := d.impl.Delete(value)
		if okk == false {
			ok = okk
		}
	}
	err := d.Save()
	return ok, err
}

/*
GetCount ...
*/
func (d *Domain) GetCount() uint {
	return uint(d.impl.GetCount())
}

/*
Clear ...
*/
func (d *Domain) Clear() (bool, error) {
	return true, nil
}

/*
Save ...
*/
func (d *Domain) Save() error {
	count := d.impl.GetCount()
	d.Info.State["count"] = uint64(count)
	infoData, err := json.Marshal(d.Info)
	if err != nil {
		return err
	}
	err = storage.GetManager().SaveInfo(d.Info.ID, infoData)
	return err
}
<|endoftext|>