<commit_before>// Copied with small adaptations from the reflect package in the
// Go source tree.

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The deepdiff package implements a version of reflect.DeepEquals that
// also returns an error message describing the first difference found.
package deepdiff

import (
	"reflect"
	"fmt"
	"unsafe"
)

// During deepValueEqual, must keep track of checks that are
// in progress.  The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
type visit struct {
	a1  uintptr
	a2  uintptr
	typ reflect.Type
}

type MismatchError struct {
	v1, v2 reflect.Value
	Path string
	How string
}

func (err *MismatchError) Error() string {
	return fmt.Sprintf("mismatch at %s: %s; obtained %#v; expected %#v", err.Path, err.How, interfaceOf(err.v1), interfaceOf(err.v2))
}

// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(path string, v1, v2 reflect.Value, visited map[visit]bool, depth int) (ok bool, err error) {
	errorf := func(f string, a ...interface{}) error {
		return &MismatchError{
			v1: v1,
			v2: v2,
			Path: path,
			How: fmt.Sprintf(f, a...),
		}
	}
	if !v1.IsValid() || !v2.IsValid() {
		if v1.IsValid() == v2.IsValid() {
			return true, nil
		}
		return false, errorf("validity mismatch")
	}
	if v1.Type() != v2.Type() {
		return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type())
	}

	// if depth > 10 { panic("deepValueEqual") }	// for debugging
	hard := func(k reflect.Kind) bool {
		switch k {
		case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
			return true
		}
		return false
	}

	if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
		addr1 := v1.UnsafeAddr()
		addr2 := v2.UnsafeAddr()
		if addr1 > addr2 {
			// Canonicalize order to reduce number of entries in visited.
			addr1, addr2 = addr2, addr1
		}

		// Short circuit if references are identical ...
		if addr1 == addr2 {
			return true, nil
		}

		// ... or already seen
		typ := v1.Type()
		v := visit{addr1, addr2, typ}
		if visited[v] {
			return true, nil
		}

		// Remember for later.
		visited[v] = true
	}

	switch v1.Kind() {
	case reflect.Array:
		if v1.Len() != v2.Len() {
			// can't happen!
			return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len())
		}
		for i := 0; i < v1.Len(); i++ {
			if ok, err := deepValueEqual(
				fmt.Sprintf("%s[%d]", path, i),
				v1.Index(i), v2.Index(i), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Slice:
		// We treat a nil slice the same as an empty slice.
		if v1.Len() != v2.Len() {
			return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len())
		}
		if v1.Pointer() == v2.Pointer() {
			return true, nil
		}
		for i := 0; i < v1.Len(); i++ {
			if ok, err := deepValueEqual(
				fmt.Sprintf("%s[%d]", path, i),
				v1.Index(i), v2.Index(i), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Interface:
		if v1.IsNil() || v2.IsNil() {
			if v1.IsNil() != v2.IsNil() {
				return false, fmt.Errorf("nil vs non-nil interface mismatch")
			}
			return true, nil
		}
		return deepValueEqual(path, v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Ptr:
		return deepValueEqual("(*" + path + ")", v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Struct:
		for i, n := 0, v1.NumField(); i < n; i++ {
			path := path + "." + v1.Type().Field(i).Name
			if ok, err := deepValueEqual(path, v1.Field(i), v2.Field(i), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Map:
		if v1.IsNil() != v2.IsNil() {
			return false, errorf("nil vs non-nil mismatch")
		}
		if v1.Len() != v2.Len() {
			return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len())
		}
		if v1.Pointer() == v2.Pointer() {
			return true, nil
		}
		for _, k := range v1.MapKeys() {
			var p string
			if k.CanInterface() {
				p = path + "[" + fmt.Sprintf("%#v", k.Interface())+ "]"
			} else {
				p = path + "[someKey]"
			}
			if ok, err := deepValueEqual(p, v1.MapIndex(k), v2.MapIndex(k), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Func:
		if v1.IsNil() && v2.IsNil() {
			return true, nil
		}
		// Can't do better than this:
		return false, errorf("non-nil functions")
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if v1.Int() != v2.Int() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if v1.Uint() != v2.Uint() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Float32, reflect.Float64:
		if v1.Float() != v2.Float() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Complex64, reflect.Complex128:
		if v1.Complex() != v2.Complex() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Bool:
		if v1.Bool() != v2.Bool() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.String:
		if v1.String() != v2.String() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Chan, reflect.UnsafePointer:
		if v1.Pointer() != v2.Pointer() {
			return false, errorf("unequal")
		}
		return true, nil
	default:
		panic("unexpected type " + v1.Type().String())
	}
}

func DeepEqual(a1, a2 interface{}) bool {
	ok, _ := DeepDiff(a1, a2)
	return ok
}

// DeepDiff tests for deep equality. It uses normal == equality where
// possible but will scan elements of arrays, slices, maps, and fields of
// structs. In maps, keys are compared with == but elements use deep
// equality. DeepEqual correctly handles recursive types. Functions are equal
// only if they are both nil.
// DeepEqual differs from reflect.DeepEqual in that an empty
// slice is equal to a nil slice, and an empty map is equal to a nil map.
// If the two values compare unequal, the resulting error
// holds the first difference encountered.
func DeepDiff(a1, a2 interface{}) (bool, error) {
	errorf := func(f string, a ...interface{}) error {
		return &MismatchError{
			v1: reflect.ValueOf(a1),
			v2: reflect.ValueOf(a2),
			Path: "$",
			How: fmt.Sprintf(f, a...),
		}
	}
	if a1 == nil || a2 == nil {
		return a1 == a2, errorf("nil vs non-nil mismatch")
	}
	v1 := reflect.ValueOf(a1)
	v2 := reflect.ValueOf(a2)
	if v1.Type() != v2.Type() {
		return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type())
	}
	return deepValueEqual("$", v1, v2, make(map[visit]bool), 0)
}

type flag uintptr

// copied from reflect/value.go
const (
	flagRO flag = 1 << iota
)

// interfaceOf returns v.Interface() even if v.CanInterface() == false.
// This enables us to call fmt.Printf on a value even if it's
// derived from inside an unexported field.
func interfaceOf(v reflect.Value) interface{} {
	if !v.IsValid() {
		return nil
	}
	return bypass(v).Interface()
}

var flagValOffset = func() uintptr {
	field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
	if !ok {
		panic("reflect.Value has no flag field")
	}
	return field.Offset
}()

// Sanity checks against future reflect package changes.
func init() {
	field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
	if !ok {
		panic("reflect.Value has no flag field")
	}
	if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
		panic("reflect.Value flag field has changed kind")
	}
	var t struct {
		a int
		A int
	}
	vA := reflect.ValueOf(t).FieldByName("A")
	va := reflect.ValueOf(t).FieldByName("a")
	flagA := *flagField(&vA)
	flaga := *flagField(&va)
	if flagA & flagRO != 0 || flaga & flagRO == 0 {
		panic("reflect.Value read-only flag has changed value")
	}
}

func flagField(v *reflect.Value) *flag {
	return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}

func bypass(v reflect.Value) reflect.Value {
	if !v.IsValid() || v.CanInterface() {
		return v
	}
	*flagField(&v) &^= flagRO
	return v
}
<commit_msg>deepdiff: fixes<commit_after>// Copied with small adaptations from the reflect package in the
// Go source tree.

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The deepdiff package implements a version of reflect.DeepEquals that
// also returns an error message describing the first difference found.
package deepdiff

import (
	"reflect"
	"fmt"
	"unsafe"
)

// During deepValueEqual, must keep track of checks that are
// in progress.  The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
type visit struct {
	a1  uintptr
	a2  uintptr
	typ reflect.Type
}

type mismatchError struct {
	v1, v2 reflect.Value
	path string
	how string
}

func (err *mismatchError) Error() string {
	path := err.path
	if path == "" {
		path = "top level"
	}
	return fmt.Sprintf("mismatch at %s: %s; obtained %#v; expected %#v", err.path, err.how, interfaceOf(err.v1), interfaceOf(err.v2))
}

// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(path string, v1, v2 reflect.Value, visited map[visit]bool, depth int) (ok bool, err error) {
	errorf := func(f string, a ...interface{}) error {
		return &mismatchError{
			v1: v1,
			v2: v2,
			path: path,
			how: fmt.Sprintf(f, a...),
		}
	}
	if !v1.IsValid() || !v2.IsValid() {
		if v1.IsValid() == v2.IsValid() {
			return true, nil
		}
		return false, errorf("validity mismatch")
	}
	if v1.Type() != v2.Type() {
		return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type())
	}

	// if depth > 10 { panic("deepValueEqual") }	// for debugging
	hard := func(k reflect.Kind) bool {
		switch k {
		case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
			return true
		}
		return false
	}

	if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
		addr1 := v1.UnsafeAddr()
		addr2 := v2.UnsafeAddr()
		if addr1 > addr2 {
			// Canonicalize order to reduce number of entries in visited.
			addr1, addr2 = addr2, addr1
		}

		// Short circuit if references are identical ...
		if addr1 == addr2 {
			return true, nil
		}

		// ... or already seen
		typ := v1.Type()
		v := visit{addr1, addr2, typ}
		if visited[v] {
			return true, nil
		}

		// Remember for later.
		visited[v] = true
	}

	switch v1.Kind() {
	case reflect.Array:
		if v1.Len() != v2.Len() {
			// can't happen!
			return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len())
		}
		for i := 0; i < v1.Len(); i++ {
			if ok, err := deepValueEqual(
				fmt.Sprintf("%s[%d]", path, i),
				v1.Index(i), v2.Index(i), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Slice:
		// We treat a nil slice the same as an empty slice.
		if v1.Len() != v2.Len() {
			return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len())
		}
		if v1.Pointer() == v2.Pointer() {
			return true, nil
		}
		for i := 0; i < v1.Len(); i++ {
			if ok, err := deepValueEqual(
				fmt.Sprintf("%s[%d]", path, i),
				v1.Index(i), v2.Index(i), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Interface:
		if v1.IsNil() || v2.IsNil() {
			if v1.IsNil() != v2.IsNil() {
				return false, fmt.Errorf("nil vs non-nil interface mismatch")
			}
			return true, nil
		}
		return deepValueEqual(path, v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Ptr:
		return deepValueEqual("(*" + path + ")", v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Struct:
		for i, n := 0, v1.NumField(); i < n; i++ {
			path := path + "." + v1.Type().Field(i).Name
			if ok, err := deepValueEqual(path, v1.Field(i), v2.Field(i), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Map:
		if v1.IsNil() != v2.IsNil() {
			return false, errorf("nil vs non-nil mismatch")
		}
		if v1.Len() != v2.Len() {
			return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len())
		}
		if v1.Pointer() == v2.Pointer() {
			return true, nil
		}
		for _, k := range v1.MapKeys() {
			var p string
			if k.CanInterface() {
				p = path + "[" + fmt.Sprintf("%#v", k.Interface())+ "]"
			} else {
				p = path + "[someKey]"
			}
			if ok, err := deepValueEqual(p, v1.MapIndex(k), v2.MapIndex(k), visited, depth+1); !ok {
				return false, err
			}
		}
		return true, nil
	case reflect.Func:
		if v1.IsNil() && v2.IsNil() {
			return true, nil
		}
		// Can't do better than this:
		return false, errorf("non-nil functions")
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if v1.Int() != v2.Int() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if v1.Uint() != v2.Uint() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Float32, reflect.Float64:
		if v1.Float() != v2.Float() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Complex64, reflect.Complex128:
		if v1.Complex() != v2.Complex() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Bool:
		if v1.Bool() != v2.Bool() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.String:
		if v1.String() != v2.String() {
			return false, errorf("unequal")
		}
		return true, nil
	case reflect.Chan, reflect.UnsafePointer:
		if v1.Pointer() != v2.Pointer() {
			return false, errorf("unequal")
		}
		return true, nil
	default:
		panic("unexpected type " + v1.Type().String())
	}
}

func DeepEqual(a1, a2 interface{}) bool {
	ok, _ := DeepDiff(a1, a2)
	return ok
}

// DeepDiff tests for deep equality. It uses normal == equality where
// possible but will scan elements of arrays, slices, maps, and fields of
// structs. In maps, keys are compared with == but elements use deep
// equality. DeepEqual correctly handles recursive types. Functions are equal
// only if they are both nil.
// DeepEqual differs from reflect.DeepEqual in that an empty
// slice is equal to a nil slice, and an empty map is equal to a nil map.
// If the two values compare unequal, the resulting error
// holds the first difference encountered.
func DeepDiff(a1, a2 interface{}) (bool, error) {
	errorf := func(f string, a ...interface{}) error {
		return &mismatchError{
			v1: reflect.ValueOf(a1),
			v2: reflect.ValueOf(a2),
			path: "",
			how: fmt.Sprintf(f, a...),
		}
	}
	if a1 == nil || a2 == nil {
		return a1 == a2, errorf("nil vs non-nil mismatch")
	}
	v1 := reflect.ValueOf(a1)
	v2 := reflect.ValueOf(a2)
	if v1.Type() != v2.Type() {
		return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type())
	}
	return deepValueEqual("", v1, v2, make(map[visit]bool), 0)
}

// interfaceOf returns v.Interface() even if v.CanInterface() == false.
// This enables us to call fmt.Printf on a value even if it's derived
// from inside an unexported field.
func interfaceOf(v reflect.Value) interface{} {
	if !v.IsValid() {
		return nil
	}
	return bypassCanInterface(v).Interface()
}

type flag uintptr

// copied from reflect/value.go
const (
	flagRO flag = 1 << iota
)

var flagValOffset = func() uintptr {
	field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
	if !ok {
		panic("reflect.Value has no flag field")
	}
	return field.Offset
}()

func flagField(v *reflect.Value) *flag {
	return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}

// bypassCanInterface returns a version of v that
// bypasses the CanInterface check.
func bypassCanInterface(v reflect.Value) reflect.Value {
	if !v.IsValid() || v.CanInterface() {
		return v
	}
	*flagField(&v) &^= flagRO
	return v
}

// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
	field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
	if !ok {
		panic("reflect.Value has no flag field")
	}
	if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
		panic("reflect.Value flag field has changed kind")
	}
	var t struct {
		a int
		A int
	}
	vA := reflect.ValueOf(t).FieldByName("A")
	va := reflect.ValueOf(t).FieldByName("a")
	flagA := *flagField(&vA)
	flaga := *flagField(&va)
	if flagA & flagRO != 0 || flaga & flagRO == 0 {
		panic("reflect.Value read-only flag has changed value")
	}
}
<|endoftext|>


<commit_before>package annotations

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"regexp"

	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

var uuidExtractRegex = regexp.MustCompile(".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$")

// Service interface. Compatible with the baserwftapp service EXCEPT for
// 1) the Write function, which has signature Write(thing interface{}) error...
// 2) the DecodeJson function, which has signature DecodeJSON(*json.Decoder) (thing interface{}, identity string, err error)
// The problem is that we have a list of things, and the uuid is for a related OTHER thing
// TODO - move to implement a shared defined Service interface?
type Service interface {
	Write(contentUUID string, thing interface{}) (err error)
	Read(contentUUID string) (thing interface{}, found bool, err error)
	Delete(contentUUID string) (found bool, err error)
	Check() (err error)
	DecodeJSON(*json.Decoder) (thing interface{}, err error)
	Count() (int, error)
	Initialise() error
}

//holds the Neo4j-specific information
type service struct {
	cypherRunner    neoutils.CypherRunner
	indexManager    neoutils.IndexManager
	platformVersion string
}

//NewAnnotationsService instantiate driver
func NewAnnotationsService(cypherRunner neoutils.CypherRunner, indexManager neoutils.IndexManager, platformVersion string) service {
	if platformVersion == "" {
		log.Fatalf("PlatformVersion was not specified!")
	}
	return service{cypherRunner, indexManager, platformVersion}
}

// DecodeJSON decodes to a list of annotations, for ease of use this is a struct itself
func (s service) DecodeJSON(dec *json.Decoder) (interface{}, error) {
	a := annotations{}
	err := dec.Decode(&a)
	return a, err
}

func (s service) Read(contentUUID string) (thing interface{}, found bool, err error) {
	results := []annotation{}

	//TODO shouldn't return Provenances if none of the scores, agentRole or atTime are set
	statementTemplate := `
					MATCH (c:Thing{uuid:{contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing)
					MATCH (cc:Thing)
					WITH c, cc, rel, {id:cc.uuid,prefLabel:cc.prefLabel,types:labels(cc),predicate:type(rel)} as thing,
					collect(
						{scores:[
							{scoringSystem:'%s', value:rel.relevanceScore},
							{scoringSystem:'%s', value:rel.confidenceScore}],
						agentRole:rel.annotatedBy,
						atTime:rel.annotatedDate}) as provenances
					RETURN thing, provenances ORDER BY thing.id
									`
	statement := fmt.Sprintf(statementTemplate, relevanceScoringSystem, confidenceScoringSystem)

	query := &neoism.CypherQuery{
		Statement:  statement,
		Parameters: neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion},
		Result:     &results,
	}
	err = s.cypherRunner.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v", contentUUID, query.Statement, err)
		return annotations{}, false, fmt.Errorf("Error accessing Annotations datastore for uuid: %s", contentUUID)
	}
	log.Debugf("CypherResult Read Annotations for uuid: %s was: %+v", contentUUID, results)
	if (len(results)) == 0 {
		return annotations{}, false, nil
	}

	for idx := range results {
		mapToResponseFormat(&results[idx])
	}

	return results, true, nil
}

//Delete removes all the annotations for this content. Ignore the nodes on either end -
//may leave nodes that are only 'things' inserted by this writer: clean up
//as a result of this will need to happen externally if required
func (s service) Delete(contentUUID string) (bool, error) {

	var deleteStatement string

	if s.platformVersion == "v2" {
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel:MENTIONS{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	} else {
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	}

	query := &neoism.CypherQuery{
		Statement:    deleteStatement,
		Parameters:   neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion},
		IncludeStats: true,
	}

	err := s.cypherRunner.CypherBatch([]*neoism.CypherQuery{query})

	stats, err := query.Stats()
	if err != nil {
		return false, err
	}

	var found bool
	if stats.ContainsUpdates {
		found = true
	}

	return found, err
}

//Write a set of annotations associated with a piece of content. Any annotations
//already there will be removed
func (s service) Write(contentUUID string, thing interface{}) (err error) {
	annotationsToWrite := thing.(annotations)

	if contentUUID == "" {
		return errors.New("Content uuid is required")
	}
	if err := validateAnnotations(&annotationsToWrite); err != nil {
		log.Warnf("Validation of supplied annotations failed")
		return err
	}

	if len(annotationsToWrite) == 0 {
		log.Warnf("No new annotations supplied for content uuid: %s", contentUUID)
	}

	queries := append([]*neoism.CypherQuery{}, dropAllAnnotationsQuery(contentUUID, s.platformVersion))

	var statements = []string{}
	for _, annotationToWrite := range annotationsToWrite {
		query, err := createAnnotationQuery(contentUUID, annotationToWrite, s.platformVersion)
		if err != nil {
			return err
		}
		statements = append(statements, query.Statement)
		queries = append(queries, query)
	}
	log.Infof("Updated Annotations for content uuid: %s", contentUUID)
	log.Debugf("For update, ran statements: %+v", statements)

	return s.cypherRunner.CypherBatch(queries)
}

// Check tests neo4j by running a simple cypher query
func (s service) Check() error {
	return neoutils.Check(s.cypherRunner)
}

func (s service) Count() (int, error) {
	results := []struct {
		Count int `json:"c"`
	}{}

	query := &neoism.CypherQuery{
		Statement:  `MATCH ()-[r{platformVersion:{platformVersion}}]->() RETURN count(r) as c`,
		Parameters: neoism.Props{"platformVersion": s.platformVersion},
		Result:     &results,
	}

	err := s.cypherRunner.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}

func (s service) Initialise() error {
	return nil // No constraints need to be set up
}

func createAnnotationRelationship(relation string) (statement string) {
	stmt := `
                MERGE (content:Thing{uuid:{contentID}})
                MERGE (upp:UPPIdentifier{value:{conceptID}})
                MERGE (upp)-[:IDENTIFIES]->(concept:Thing) ON CREATE SET concept.uuid = {conceptID}
                MERGE (content)-[pred:%s{platformVersion:{platformVersion}}]->(concept)
                SET pred={annProps}
                `
	statement = fmt.Sprintf(stmt, relation)
	return statement
}

func getRelationshipFromPredicate(predicate string) (relation string) {
	if predicate != "" {
		relation = relations[predicate]
	} else {
		relation = relations["mentions"]
	}
	return relation
}

func createAnnotationQuery(contentUUID string, ann annotation, platformVersion string) (*neoism.CypherQuery, error) {
	query := neoism.CypherQuery{}
	thingID, err := extractUUIDFromURI(ann.Thing.ID)
	if err != nil {
		return nil, err
	}

	//todo temporary change to deal with multiple provenances
	/*if len(ann.Provenances) > 1 {
		return nil, errors.New("Cannot insert a MENTIONS annotation with multiple provenances")
	}*/

	var prov provenance
	params := map[string]interface{}{}
	params["platformVersion"] = platformVersion

	if len(ann.Provenances) >= 1 {
		prov = ann.Provenances[0]
		annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, supplied, err := extractDataFromProvenance(&prov)

		if err != nil {
			log.Infof("ERROR=%s", err)
			return nil, err
		}

		if supplied == true {
			if annotatedBy != "" {
				params["annotatedBy"] = annotatedBy
			}
			if prov.AtTime != "" {
				params["annotatedDateEpoch"] = annotatedDateEpoch
				params["annotatedDate"] = prov.AtTime
			}
			params["relevanceScore"] = relevanceScore
			params["confidenceScore"] = confidenceScore
		}
	}

	relation := getRelationshipFromPredicate(ann.Thing.Predicate)
	query.Statement = createAnnotationRelationship(relation)
	query.Parameters = map[string]interface{}{
		"contentID":       contentUUID,
		"conceptID":       thingID,
		"platformVersion": platformVersion,
		"annProps":        params,
	}
	return &query, nil
}

func extractDataFromProvenance(prov *provenance) (string, int64, float64, float64, bool, error) {
	if len(prov.Scores) == 0 {
		return "", -1, -1, -1, false, nil
	}
	var annotatedBy string
	var annotatedDateEpoch int64
	var confidenceScore, relevanceScore float64
	var err error
	if prov.AgentRole != "" {
		annotatedBy, err = extractUUIDFromURI(prov.AgentRole)
	}
	if prov.AtTime != "" {
		annotatedDateEpoch, err = convertAnnotatedDateToEpoch(prov.AtTime)
	}
	relevanceScore, confidenceScore, err = extractScores(prov.Scores)

	if err != nil {
		return "", -1, -1, -1, true, err
	}
	return annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, true, nil
}

func extractUUIDFromURI(uri string) (string, error) {
	result := uuidExtractRegex.FindStringSubmatch(uri)
	if len(result) == 2 {
		return result[1], nil
	}
	return "", fmt.Errorf("Couldn't extract uuid from uri %s", uri)
}

func convertAnnotatedDateToEpoch(annotatedDateString string) (int64, error) {
	datetimeEpoch, err := time.Parse(time.RFC3339, annotatedDateString)

	if err != nil {
		return 0, err
	}

	return datetimeEpoch.Unix(), nil
}

func extractScores(scores []score) (float64, float64, error) {
	var relevanceScore, confidenceScore float64
	for _, score := range scores {
		scoringSystem := score.ScoringSystem
		value := score.Value
		switch scoringSystem {
		case relevanceScoringSystem:
			relevanceScore = value
		case confidenceScoringSystem:
			confidenceScore = value
		}
	}
	return relevanceScore, confidenceScore, nil
}

func dropAllAnnotationsQuery(contentUUID string, platformVersion string) *neoism.CypherQuery {

	var matchStmtTemplate string

	//TODO hard-coded verification:
	// -> necessary for brands - which got written by content-api with isClassifiedBy relationship, and should not be deleted by annotations-rw
	// -> so far brands are the only v2 concepts which have isClassifiedBy relationship; as soon as this changes: implementation needs to be updated
	if platformVersion == "v2" {
		matchStmtTemplate = `OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r:MENTIONS{platformVersion:{platformVersion}}]->(t:Thing)
                        DELETE r`
	} else {
		matchStmtTemplate = `OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r]->(t:Thing)
			WHERE r.platformVersion={platformVersion}
                        DELETE r`
	}

	query := neoism.CypherQuery{}
	query.Statement = matchStmtTemplate
	query.Parameters = neoism.Props{"contentID": contentUUID, "platformVersion": platformVersion}
	return &query
}

func validateAnnotations(annotations *annotations) error {
	//TODO - for consistency, we should probably just not create the annotation?
	for _, annotation := range *annotations {
		if annotation.Thing.ID == "" {
			return ValidationError{fmt.Sprintf("Concept uuid missing for annotation %+v", annotation)}
		}
	}
	return nil
}

//ValidationError is thrown when the annotations are not valid because mandatory information is missing
type ValidationError struct {
	Msg string
}

func (v ValidationError) Error() string {
	return v.Msg
}

func mapToResponseFormat(ann *annotation) {
	ann.Thing.ID = mapper.IDURL(ann.Thing.ID)
	// We expect only ONE provenance - provenance value is considered valid even if the AgentRole is not specified. See: v1 - isClassifiedBy
	for idx := range ann.Provenances {
		if ann.Provenances[idx].AgentRole != "" {
			ann.Provenances[idx].AgentRole = mapper.IDURL(ann.Provenances[idx].AgentRole)
		}
	}
}
<commit_msg>Insert default Identifier label beside UPPIdentifier.<commit_after>package annotations

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"regexp"

	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

var uuidExtractRegex = regexp.MustCompile(".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$")

// Service interface. Compatible with the baserwftapp service EXCEPT for
// 1) the Write function, which has signature Write(thing interface{}) error...
// 2) the DecodeJson function, which has signature DecodeJSON(*json.Decoder) (thing interface{}, identity string, err error)
// The problem is that we have a list of things, and the uuid is for a related OTHER thing
// TODO - move to implement a shared defined Service interface?
type Service interface {
	Write(contentUUID string, thing interface{}) (err error)
	Read(contentUUID string) (thing interface{}, found bool, err error)
	Delete(contentUUID string) (found bool, err error)
	Check() (err error)
	DecodeJSON(*json.Decoder) (thing interface{}, err error)
	Count() (int, error)
	Initialise() error
}

//holds the Neo4j-specific information
type service struct {
	cypherRunner    neoutils.CypherRunner
	indexManager    neoutils.IndexManager
	platformVersion string
}

//NewAnnotationsService instantiate driver
func NewAnnotationsService(cypherRunner neoutils.CypherRunner, indexManager neoutils.IndexManager, platformVersion string) service {
	if platformVersion == "" {
		log.Fatalf("PlatformVersion was not specified!")
	}
	return service{cypherRunner, indexManager, platformVersion}
}

// DecodeJSON decodes to a list of annotations, for ease of use this is a struct itself
func (s service) DecodeJSON(dec *json.Decoder) (interface{}, error) {
	a := annotations{}
	err := dec.Decode(&a)
	return a, err
}

func (s service) Read(contentUUID string) (thing interface{}, found bool, err error) {
	results := []annotation{}

	//TODO shouldn't return Provenances if none of the scores, agentRole or atTime are set
	statementTemplate := `
					MATCH (c:Thing{uuid:{contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing)
					MATCH (cc:Thing)
					WITH c, cc, rel, {id:cc.uuid,prefLabel:cc.prefLabel,types:labels(cc),predicate:type(rel)} as thing,
					collect(
						{scores:[
							{scoringSystem:'%s', value:rel.relevanceScore},
							{scoringSystem:'%s', value:rel.confidenceScore}],
						agentRole:rel.annotatedBy,
						atTime:rel.annotatedDate}) as provenances
					RETURN thing, provenances ORDER BY thing.id
									`
	statement := fmt.Sprintf(statementTemplate, relevanceScoringSystem, confidenceScoringSystem)

	query := &neoism.CypherQuery{
		Statement:  statement,
		Parameters: neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion},
		Result:     &results,
	}
	err = s.cypherRunner.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v", contentUUID, query.Statement, err)
		return annotations{}, false, fmt.Errorf("Error accessing Annotations datastore for uuid: %s", contentUUID)
	}
	log.Debugf("CypherResult Read Annotations for uuid: %s was: %+v", contentUUID, results)
	if (len(results)) == 0 {
		return annotations{}, false, nil
	}

	for idx := range results {
		mapToResponseFormat(&results[idx])
	}

	return results, true, nil
}

//Delete removes all the annotations for this content. Ignore the nodes on either end -
//may leave nodes that are only 'things' inserted by this writer: clean up
//as a result of this will need to happen externally if required
func (s service) Delete(contentUUID string) (bool, error) {

	var deleteStatement string

	if s.platformVersion == "v2" {
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel:MENTIONS{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	} else {
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	}

	query := &neoism.CypherQuery{
		Statement:    deleteStatement,
		Parameters:   neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion},
		IncludeStats: true,
	}

	err := s.cypherRunner.CypherBatch([]*neoism.CypherQuery{query})

	stats, err := query.Stats()
	if err != nil {
		return false, err
	}

	var found bool
	if stats.ContainsUpdates {
		found = true
	}

	return found, err
}

//Write a set of annotations associated with a piece of content. Any annotations
//already there will be removed
func (s service) Write(contentUUID string, thing interface{}) (err error) {
	annotationsToWrite := thing.(annotations)

	if contentUUID == "" {
		return errors.New("Content uuid is required")
	}
	if err := validateAnnotations(&annotationsToWrite); err != nil {
		log.Warnf("Validation of supplied annotations failed")
		return err
	}

	if len(annotationsToWrite) == 0 {
		log.Warnf("No new annotations supplied for content uuid: %s", contentUUID)
	}

	queries := append([]*neoism.CypherQuery{}, dropAllAnnotationsQuery(contentUUID, s.platformVersion))

	var statements = []string{}
	for _, annotationToWrite := range annotationsToWrite {
		query, err := createAnnotationQuery(contentUUID, annotationToWrite, s.platformVersion)
		if err != nil {
			return err
		}
		statements = append(statements, query.Statement)
		queries = append(queries, query)
	}
	log.Infof("Updated Annotations for content uuid: %s", contentUUID)
	log.Debugf("For update, ran statements: %+v", statements)

	return s.cypherRunner.CypherBatch(queries)
}

// Check tests neo4j by running a simple cypher query
func (s service) Check() error {
	return neoutils.Check(s.cypherRunner)
}

func (s service) Count() (int, error) {
	results := []struct {
		Count int `json:"c"`
	}{}

	query := &neoism.CypherQuery{
		Statement:  `MATCH ()-[r{platformVersion:{platformVersion}}]->() RETURN count(r) as c`,
		Parameters: neoism.Props{"platformVersion": s.platformVersion},
		Result:     &results,
	}

	err := s.cypherRunner.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}

func (s service) Initialise() error {
	return nil // No constraints need to be set up
}

func createAnnotationRelationship(relation string) (statement string) {
	stmt := `
                MERGE (content:Thing{uuid:{contentID}})
                MERGE (upp:Identifier:UPPIdentifier{value:{conceptID}})
                MERGE (upp)-[:IDENTIFIES]->(concept:Thing) ON CREATE SET concept.uuid = {conceptID}
                MERGE (content)-[pred:%s{platformVersion:{platformVersion}}]->(concept)
                SET pred={annProps}
                `
	statement = fmt.Sprintf(stmt, relation)
	return statement
}

func getRelationshipFromPredicate(predicate string) (relation string) {
	if predicate != "" {
		relation = relations[predicate]
	} else {
		relation = relations["mentions"]
	}
	return relation
}

func createAnnotationQuery(contentUUID string, ann annotation, platformVersion string) (*neoism.CypherQuery, error) {
	query := neoism.CypherQuery{}
	thingID, err := extractUUIDFromURI(ann.Thing.ID)
	if err != nil {
		return nil, err
	}

	//todo temporary change to deal with multiple provenances
	/*if len(ann.Provenances) > 1 {
		return nil, errors.New("Cannot insert a MENTIONS annotation with multiple provenances")
	}*/

	var prov provenance
	params := map[string]interface{}{}
	params["platformVersion"] = platformVersion

	if len(ann.Provenances) >= 1 {
		prov = ann.Provenances[0]
		annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, supplied, err := extractDataFromProvenance(&prov)

		if err != nil {
			log.Infof("ERROR=%s", err)
			return nil, err
		}

		if supplied == true {
			if annotatedBy != "" {
				params["annotatedBy"] = annotatedBy
			}
			if prov.AtTime != "" {
				params["annotatedDateEpoch"] = annotatedDateEpoch
				params["annotatedDate"] = prov.AtTime
			}
			params["relevanceScore"] = relevanceScore
			params["confidenceScore"] = confidenceScore
		}
	}

	relation := getRelationshipFromPredicate(ann.Thing.Predicate)
	query.Statement = createAnnotationRelationship(relation)
	query.Parameters = map[string]interface{}{
		"contentID":       contentUUID,
		"conceptID":       thingID,
		"platformVersion": platformVersion,
		"annProps":        params,
	}
	return &query, nil
}

func extractDataFromProvenance(prov *provenance) (string, int64, float64, float64, bool, error) {
	if len(prov.Scores) == 0 {
		return "", -1, -1, -1, false, nil
	}
	var annotatedBy string
	var annotatedDateEpoch int64
	var confidenceScore, relevanceScore float64
	var err error
	if prov.AgentRole != "" {
		annotatedBy, err = extractUUIDFromURI(prov.AgentRole)
	}
	if prov.AtTime != "" {
		annotatedDateEpoch, err = convertAnnotatedDateToEpoch(prov.AtTime)
	}
	relevanceScore, confidenceScore, err = extractScores(prov.Scores)

	if err != nil {
		return "", -1, -1, -1, true, err
	}
	return annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, true, nil
}

func extractUUIDFromURI(uri string) (string, error) {
	result := uuidExtractRegex.FindStringSubmatch(uri)
	if len(result) == 2 {
		return result[1], nil
	}
	return "", fmt.Errorf("Couldn't extract uuid from uri %s", uri)
}

func convertAnnotatedDateToEpoch(annotatedDateString string) (int64, error) {
	datetimeEpoch, err := time.Parse(time.RFC3339, annotatedDateString)

	if err != nil {
		return 0, err
	}

	return datetimeEpoch.Unix(), nil
}

func extractScores(scores []score) (float64, float64, error) {
	var relevanceScore, confidenceScore float64
	for _, score := range scores {
		scoringSystem := score.ScoringSystem
		value := score.Value
		switch scoringSystem {
		case relevanceScoringSystem:
			relevanceScore = value
		case confidenceScoringSystem:
			confidenceScore = value
		}
	}
	return relevanceScore, confidenceScore, nil
}

func dropAllAnnotationsQuery(contentUUID string, platformVersion string) *neoism.CypherQuery {

	var matchStmtTemplate string

	//TODO hard-coded verification:
	// -> necessary for brands - which got written by content-api with isClassifiedBy relationship, and should not be deleted by annotations-rw
	// -> so far brands are the only v2 concepts which have isClassifiedBy relationship; as soon as this changes: implementation needs to be updated
	if platformVersion == "v2" {
		matchStmtTemplate = `OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r:MENTIONS{platformVersion:{platformVersion}}]->(t:Thing)
                        DELETE r`
	} else {
		matchStmtTemplate = `OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r]->(t:Thing)
			WHERE r.platformVersion={platformVersion}
                        DELETE r`
	}

	query := neoism.CypherQuery{}
	query.Statement = matchStmtTemplate
	query.Parameters = neoism.Props{"contentID": contentUUID, "platformVersion": platformVersion}
	return &query
}

func validateAnnotations(annotations *annotations) error {
	//TODO - for consistency, we should probably just not create the annotation?
	for _, annotation := range *annotations {
		if annotation.Thing.ID == "" {
			return ValidationError{fmt.Sprintf("Concept uuid missing for annotation %+v", annotation)}
		}
	}
	return nil
}

//ValidationError is thrown when the annotations are not valid because mandatory information is missing
type ValidationError struct {
	Msg string
}

func (v ValidationError) Error() string {
	return v.Msg
}

func mapToResponseFormat(ann *annotation) {
	ann.Thing.ID = mapper.IDURL(ann.Thing.ID)
	// We expect only ONE provenance - provenance value is considered valid even if the AgentRole is not specified. See: v1 - isClassifiedBy
	for idx := range ann.Provenances {
		if ann.Provenances[idx].AgentRole != "" {
			ann.Provenances[idx].AgentRole = mapper.IDURL(ann.Provenances[idx].AgentRole)
		}
	}
}
<|endoftext|>


<commit_before>package todoist

import (
	"errors"
	"reflect"
	"strconv"
	"testing"
)

var testIDs = []struct {
	s string
	v ID
	e error
}{
	{
		"1000000",
		ID("1000000"),
		nil,
	},
	{
		"df43406d-db7e-4ea5-b3b4-c822ccdab3bf",
		ID("df43406d-db7e-4ea5-b3b4-c822ccdab3bf"),
		nil,
	},
	{
		"invalid",
		"",
		errors.New("Invalid ID: invalid"),
	},
}

func TestNewID(t *testing.T) {
	for _, test := range testIDs {
		v, err := NewID(test.s)
		if !reflect.DeepEqual(err, test.e) {
			t.Errorf("Expect %s, but got %s", test.e, err)
		} else if test.e == nil && v != test.v {
			t.Errorf("Expect %s, but got %s", test.v, v)
		}
	}
}

func TestID_MarshalJSON(t *testing.T) {
	test := testIDs[0]
	b, err := test.v.MarshalJSON()
	if err != nil || string(b) != test.s {
		t.Errorf("Expect %s, but got %s", strconv.Quote(test.s), string(b))
	}

	test = testIDs[1]
	b, err = test.v.MarshalJSON()
	if err != nil || string(b) != strconv.Quote(test.s) {
		t.Errorf("Expect %s, but got %s", strconv.Quote(test.s), string(b))
	}

	b, err = ID("0").MarshalJSON()
	if err != nil || string(b) != "null" {
		t.Errorf("Expect %s, but got %s", strconv.Quote(test.s), string(b))
	}
}

func TestID_UnmarshalJSON(t *testing.T) {
	for _, test := range testIDs {
		var v ID
		err := v.UnmarshalJSON([]byte(test.s))
		if !reflect.DeepEqual(err, test.e) {
			t.Errorf("Expect %s, but got %s", test.e, err)
		} else if test.e == nil && v != test.v {
			t.Errorf("Expect %s, but got %s", test.v, v)
		}
	}
	var v ID
	err := v.UnmarshalJSON([]byte("null"))
	if err != nil {
		t.Errorf("Unexpect error: %s", err)
	}
	if v != ID("0") {
		t.Errorf("Expect %s, but got %s", ID("0"), v)
	}
}

func TestIsTempID(t *testing.T) {
	test := testIDs[0]
	if IsTempID(test.v) == true {
		t.Errorf("%s is not temp id, but returns true", test.v)
	}
	test = testIDs[1]
	if IsTempID(test.v) == false {
		t.Errorf("%s is temp id, but returns false", test.v)
	}
}

func TestUUID_MarshalJSON(t *testing.T) {
	s := "c0afd2be-7576-4fc4-8b3c-696c4c6cf794"
	v := UUID(s)
	b, err := v.MarshalJSON()
	if err != nil || string(b) != strconv.Quote(s) {
		t.Errorf("Expect %s, but got %s", strconv.Quote(s), string(b))
	}
}

func TestUUID_UnmarshalJSON(t *testing.T) {
	s := "c0afd2be-7576-4fc4-8b3c-696c4c6cf794"
	var v UUID
	err := v.UnmarshalJSON([]byte(strconv.Quote(s)))
	if err != nil || v != UUID(s) {
		t.Errorf("Expect %s, but got %s", UUID(s), v)
	}

	s = "invalid"
	err = v.UnmarshalJSON([]byte(s))
	if err == nil {
		t.Error("Expect error, but no error")
	}
}
<commit_msg>fix test<commit_after>package todoist

import (
	"errors"
	"reflect"
	"strconv"
	"testing"
)

var testIDs = []struct {
	s string
	v ID
	e error
}{
	{
		"1000000",
		ID("1000000"),
		nil,
	},
	{
		"df43406d-db7e-4ea5-b3b4-c822ccdab3bf",
		ID("df43406d-db7e-4ea5-b3b4-c822ccdab3bf"),
		nil,
	},
	{
		"invalid",
		"",
		errors.New("invalid id: invalid"),
	},
}

func TestNewID(t *testing.T) {
	for _, test := range testIDs {
		v, err := NewID(test.s)
		if !reflect.DeepEqual(err, test.e) {
			t.Errorf("Expect %s, but got %s", test.e, err)
		} else if test.e == nil && v != test.v {
			t.Errorf("Expect %s, but got %s", test.v, v)
		}
	}
}

func TestID_MarshalJSON(t *testing.T) {
	test := testIDs[0]
	b, err := test.v.MarshalJSON()
	if err != nil || string(b) != test.s {
		t.Errorf("Expect %s, but got %s", strconv.Quote(test.s), string(b))
	}

	test = testIDs[1]
	b, err = test.v.MarshalJSON()
	if err != nil || string(b) != strconv.Quote(test.s) {
		t.Errorf("Expect %s, but got %s", strconv.Quote(test.s), string(b))
	}

	b, err = ID("0").MarshalJSON()
	if err != nil || string(b) != "null" {
		t.Errorf("Expect %s, but got %s", strconv.Quote(test.s), string(b))
	}
}

func TestID_UnmarshalJSON(t *testing.T) {
	for _, test := range testIDs {
		var v ID
		err := v.UnmarshalJSON([]byte(test.s))
		if !reflect.DeepEqual(err, test.e) {
			t.Errorf("Expect %s, but got %s", test.e, err)
		} else if test.e == nil && v != test.v {
			t.Errorf("Expect %s, but got %s", test.v, v)
		}
	}
	var v ID
	err := v.UnmarshalJSON([]byte("null"))
	if err != nil {
		t.Errorf("Unexpect error: %s", err)
	}
	if v != ID("0") {
		t.Errorf("Expect %s, but got %s", ID("0"), v)
	}
}

func TestIsTempID(t *testing.T) {
	test := testIDs[0]
	if IsTempID(test.v) == true {
		t.Errorf("%s is not temp id, but returns true", test.v)
	}
	test = testIDs[1]
	if IsTempID(test.v) == false {
		t.Errorf("%s is temp id, but returns false", test.v)
	}
}

func TestUUID_MarshalJSON(t *testing.T) {
	s := "c0afd2be-7576-4fc4-8b3c-696c4c6cf794"
	v := UUID(s)
	b, err := v.MarshalJSON()
	if err != nil || string(b) != strconv.Quote(s) {
		t.Errorf("Expect %s, but got %s", strconv.Quote(s), string(b))
	}
}

func TestUUID_UnmarshalJSON(t *testing.T) {
	s := "c0afd2be-7576-4fc4-8b3c-696c4c6cf794"
	var v UUID
	err := v.UnmarshalJSON([]byte(strconv.Quote(s)))
	if err != nil || v != UUID(s) {
		t.Errorf("Expect %s, but got %s", UUID(s), v)
	}

	s = "invalid"
	err = v.UnmarshalJSON([]byte(s))
	if err == nil {
		t.Error("Expect error, but no error")
	}
}
<|endoftext|>


<commit_before>package oauth

import (
	"net/http"

	"github.com/jinzhu/gorm"
	"github.com/stretchr/testify/mock"
)

// ServiceMock is a mocked object implementing ServiceInterface
type ServiceMock struct {
	mock.Mock
}

// ClientExists ...
func (_m *ServiceMock) ClientExists(clientID string) bool {
	ret := _m.Called(clientID)

	var r0 bool
	if rf, ok := ret.Get(0).(func(string) bool); ok {
		r0 = rf(clientID)
	} else {
		r0 = ret.Get(0).(bool)
	}

	return r0
}

// FindClientByClientID ...
func (_m *ServiceMock) FindClientByClientID(clientID string) (*Client, error) {
	ret := _m.Called(clientID)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(string) *Client); ok {
		r0 = rf(clientID)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(clientID)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateClient ...
func (_m *ServiceMock) CreateClient(clientID string, secret string, redirectURI string) (*Client, error) {
	ret := _m.Called(clientID, secret, redirectURI)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(string, string, string) *Client); ok {
		r0 = rf(clientID, secret, redirectURI)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string, string) error); ok {
		r1 = rf(clientID, secret, redirectURI)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateClientTx ...
func (_m *ServiceMock) CreateClientTx(tx *gorm.DB, clientID string, secret string, redirectURI string) (*Client, error) {
	ret := _m.Called(tx, clientID, secret, redirectURI)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(*gorm.DB, string, string, string) *Client); ok {
		r0 = rf(tx, clientID, secret, redirectURI)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*gorm.DB, string, string, string) error); ok {
		r1 = rf(tx, clientID, secret, redirectURI)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// AuthClient ...
func (_m *ServiceMock) AuthClient(clientID string, secret string) (*Client, error) {
	ret := _m.Called(clientID, secret)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(string, string) *Client); ok {
		r0 = rf(clientID, secret)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string) error); ok {
		r1 = rf(clientID, secret)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UserExists ...
func (_m *ServiceMock) UserExists(username string) bool {
	ret := _m.Called(username)

	var r0 bool
	if rf, ok := ret.Get(0).(func(string) bool); ok {
		r0 = rf(username)
	} else {
		r0 = ret.Get(0).(bool)
	}

	return r0
}

// FindUserByUsername ...
func (_m *ServiceMock) FindUserByUsername(username string) (*User, error) {
	ret := _m.Called(username)

	var r0 *User
	if rf, ok := ret.Get(0).(func(string) *User); ok {
		r0 = rf(username)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(username)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateUser ...
func (_m *ServiceMock) CreateUser(username string, password string) (*User, error) {
	ret := _m.Called(username, password)

	var r0 *User
	if rf, ok := ret.Get(0).(func(string, string) *User); ok {
		r0 = rf(username, password)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string) error); ok {
		r1 = rf(username, password)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateUserTx ...
func (_m *ServiceMock) CreateUserTx(tx *gorm.DB, username string, password string) (*User, error) {
	ret := _m.Called(tx, username, password)

	var r0 *User
	if rf, ok := ret.Get(0).(func(*gorm.DB, string, string) *User); ok {
		r0 = rf(tx, username, password)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*gorm.DB, string, string) error); ok {
		r1 = rf(tx, username, password)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// SetPassword ...
func (_m *ServiceMock) SetPassword(user *User, password string) error {
	ret := _m.Called(user, password)

	var r0 error
	if rf, ok := ret.Get(0).(func(*User, string) error); ok {
		r0 = rf(user, password)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// AuthUser ...
func (_m *ServiceMock) AuthUser(username string, thePassword string) (*User, error) {
	ret := _m.Called(username, thePassword)

	var r0 *User
	if rf, ok := ret.Get(0).(func(string, string) *User); ok {
		r0 = rf(username, thePassword)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string) error); ok {
		r1 = rf(username, thePassword)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetScope ...
func (_m *ServiceMock) GetScope(requestedScope string) (string, error) {
	ret := _m.Called(requestedScope)

	var r0 string
	if rf, ok := ret.Get(0).(func(string) string); ok {
		r0 = rf(requestedScope)
	} else {
		r0 = ret.Get(0).(string)
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(requestedScope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GrantAuthorizationCode ...
func (_m *ServiceMock) GrantAuthorizationCode(client *Client, user *User, redirectURI string, scope string) (*AuthorizationCode, error) {
	ret := _m.Called(client, user, redirectURI, scope)

	var r0 *AuthorizationCode
	if rf, ok := ret.Get(0).(func(*Client, *User, string, string) *AuthorizationCode); ok {
		r0 = rf(client, user, redirectURI, scope)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*AuthorizationCode)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*Client, *User, string, string) error); ok {
		r1 = rf(client, user, redirectURI, scope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GrantAccessToken ...
func (_m *ServiceMock) GrantAccessToken(client *Client, user *User, scope string) (*AccessToken, error) {
	ret := _m.Called(client, user, scope)

	var r0 *AccessToken
	if rf, ok := ret.Get(0).(func(*Client, *User, string) *AccessToken); ok {
		r0 = rf(client, user, scope)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*AccessToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*Client, *User, string) error); ok {
		r1 = rf(client, user, scope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetOrCreateRefreshToken ...
func (_m *ServiceMock) GetOrCreateRefreshToken(client *Client, user *User, scope string) (*RefreshToken, error) {
	ret := _m.Called(client, user, scope)

	var r0 *RefreshToken
	if rf, ok := ret.Get(0).(func(*Client, *User, string) *RefreshToken); ok {
		r0 = rf(client, user, scope)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*RefreshToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*Client, *User, string) error); ok {
		r1 = rf(client, user, scope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetValidRefreshToken ...
func (_m *ServiceMock) GetValidRefreshToken(token string, client *Client) (*RefreshToken, error) {
	ret := _m.Called(token, client)

	var r0 *RefreshToken
	if rf, ok := ret.Get(0).(func(string, *Client) *RefreshToken); ok {
		r0 = rf(token, client)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*RefreshToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, *Client) error); ok {
		r1 = rf(token, client)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// Authenticate ...
func (_m *ServiceMock) Authenticate(token string) (*AccessToken, error) {
	ret := _m.Called(token)

	var r0 *AccessToken
	if rf, ok := ret.Get(0).(func(string) *AccessToken); ok {
		r0 = rf(token)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*AccessToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(token)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

func (_m *ServiceMock) tokensHandler(w http.ResponseWriter, r *http.Request) {
	_m.Called(w, r)
}
<commit_msg>Added introspectHandler to service mock.<commit_after>package oauth

import (
	"net/http"

	"github.com/jinzhu/gorm"
	"github.com/stretchr/testify/mock"
)

// ServiceMock is a mocked object implementing ServiceInterface
type ServiceMock struct {
	mock.Mock
}

// ClientExists ...
func (_m *ServiceMock) ClientExists(clientID string) bool {
	ret := _m.Called(clientID)

	var r0 bool
	if rf, ok := ret.Get(0).(func(string) bool); ok {
		r0 = rf(clientID)
	} else {
		r0 = ret.Get(0).(bool)
	}

	return r0
}

// FindClientByClientID ...
func (_m *ServiceMock) FindClientByClientID(clientID string) (*Client, error) {
	ret := _m.Called(clientID)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(string) *Client); ok {
		r0 = rf(clientID)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(clientID)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateClient ...
func (_m *ServiceMock) CreateClient(clientID string, secret string, redirectURI string) (*Client, error) {
	ret := _m.Called(clientID, secret, redirectURI)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(string, string, string) *Client); ok {
		r0 = rf(clientID, secret, redirectURI)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string, string) error); ok {
		r1 = rf(clientID, secret, redirectURI)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateClientTx ...
func (_m *ServiceMock) CreateClientTx(tx *gorm.DB, clientID string, secret string, redirectURI string) (*Client, error) {
	ret := _m.Called(tx, clientID, secret, redirectURI)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(*gorm.DB, string, string, string) *Client); ok {
		r0 = rf(tx, clientID, secret, redirectURI)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*gorm.DB, string, string, string) error); ok {
		r1 = rf(tx, clientID, secret, redirectURI)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// AuthClient ...
func (_m *ServiceMock) AuthClient(clientID string, secret string) (*Client, error) {
	ret := _m.Called(clientID, secret)

	var r0 *Client
	if rf, ok := ret.Get(0).(func(string, string) *Client); ok {
		r0 = rf(clientID, secret)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*Client)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string) error); ok {
		r1 = rf(clientID, secret)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UserExists ...
func (_m *ServiceMock) UserExists(username string) bool {
	ret := _m.Called(username)

	var r0 bool
	if rf, ok := ret.Get(0).(func(string) bool); ok {
		r0 = rf(username)
	} else {
		r0 = ret.Get(0).(bool)
	}

	return r0
}

// FindUserByUsername ...
func (_m *ServiceMock) FindUserByUsername(username string) (*User, error) {
	ret := _m.Called(username)

	var r0 *User
	if rf, ok := ret.Get(0).(func(string) *User); ok {
		r0 = rf(username)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(username)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateUser ...
func (_m *ServiceMock) CreateUser(username string, password string) (*User, error) {
	ret := _m.Called(username, password)

	var r0 *User
	if rf, ok := ret.Get(0).(func(string, string) *User); ok {
		r0 = rf(username, password)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string) error); ok {
		r1 = rf(username, password)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateUserTx ...
func (_m *ServiceMock) CreateUserTx(tx *gorm.DB, username string, password string) (*User, error) {
	ret := _m.Called(tx, username, password)

	var r0 *User
	if rf, ok := ret.Get(0).(func(*gorm.DB, string, string) *User); ok {
		r0 = rf(tx, username, password)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*gorm.DB, string, string) error); ok {
		r1 = rf(tx, username, password)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// SetPassword ...
func (_m *ServiceMock) SetPassword(user *User, password string) error {
	ret := _m.Called(user, password)

	var r0 error
	if rf, ok := ret.Get(0).(func(*User, string) error); ok {
		r0 = rf(user, password)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// AuthUser ...
func (_m *ServiceMock) AuthUser(username string, thePassword string) (*User, error) {
	ret := _m.Called(username, thePassword)

	var r0 *User
	if rf, ok := ret.Get(0).(func(string, string) *User); ok {
		r0 = rf(username, thePassword)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*User)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, string) error); ok {
		r1 = rf(username, thePassword)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetScope ...
func (_m *ServiceMock) GetScope(requestedScope string) (string, error) {
	ret := _m.Called(requestedScope)

	var r0 string
	if rf, ok := ret.Get(0).(func(string) string); ok {
		r0 = rf(requestedScope)
	} else {
		r0 = ret.Get(0).(string)
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(requestedScope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GrantAuthorizationCode ...
func (_m *ServiceMock) GrantAuthorizationCode(client *Client, user *User, redirectURI string, scope string) (*AuthorizationCode, error) {
	ret := _m.Called(client, user, redirectURI, scope)

	var r0 *AuthorizationCode
	if rf, ok := ret.Get(0).(func(*Client, *User, string, string) *AuthorizationCode); ok {
		r0 = rf(client, user, redirectURI, scope)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*AuthorizationCode)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*Client, *User, string, string) error); ok {
		r1 = rf(client, user, redirectURI, scope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GrantAccessToken ...
func (_m *ServiceMock) GrantAccessToken(client *Client, user *User, scope string) (*AccessToken, error) {
	ret := _m.Called(client, user, scope)

	var r0 *AccessToken
	if rf, ok := ret.Get(0).(func(*Client, *User, string) *AccessToken); ok {
		r0 = rf(client, user, scope)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*AccessToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*Client, *User, string) error); ok {
		r1 = rf(client, user, scope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetOrCreateRefreshToken ...
func (_m *ServiceMock) GetOrCreateRefreshToken(client *Client, user *User, scope string) (*RefreshToken, error) {
	ret := _m.Called(client, user, scope)

	var r0 *RefreshToken
	if rf, ok := ret.Get(0).(func(*Client, *User, string) *RefreshToken); ok {
		r0 = rf(client, user, scope)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*RefreshToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*Client, *User, string) error); ok {
		r1 = rf(client, user, scope)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetValidRefreshToken ...
func (_m *ServiceMock) GetValidRefreshToken(token string, client *Client) (*RefreshToken, error) {
	ret := _m.Called(token, client)

	var r0 *RefreshToken
	if rf, ok := ret.Get(0).(func(string, *Client) *RefreshToken); ok {
		r0 = rf(token, client)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*RefreshToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, *Client) error); ok {
		r1 = rf(token, client)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// Authenticate ...
func (_m *ServiceMock) Authenticate(token string) (*AccessToken, error) {
	ret := _m.Called(token)

	var r0 *AccessToken
	if rf, ok := ret.Get(0).(func(string) *AccessToken); ok {
		r0 = rf(token)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*AccessToken)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(token)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

func (_m *ServiceMock) tokensHandler(w http.ResponseWriter, r *http.Request) {
	_m.Called(w, r)
}

func (_m *ServiceMock) introspectHandler(w http.ResponseWriter, r *http.Request) {
	_m.Called(w, r)
}
<|endoftext|>


<commit_before>package svc_test

import (
	"context"
	"strings"
	"testing"
	"time"

	"github.com/nerdalize/nerd/svc"
)

func TestUpdateDataset(t *testing.T) {
	di, clean := testDI(t)
	defer clean()

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()

	kube := svc.NewKube(di)
	out, err := kube.CreateDataset(ctx, &svc.CreateDatasetInput{Name: "my-dataset", Bucket: "bogus", Key: "my-key"})
	ok(t, err)

	_, err = kube.UpdateDataset(ctx, &svc.UpdateDatasetInput{
		Name:       out.Name,
		InputFor:   "j-123abc",
		OutputFrom: "j-456def",
		Size:       1337,
	})
	ok(t, err)

	o, err := kube.GetDataset(ctx, &svc.GetDatasetInput{Name: out.Name})
	ok(t, err)
	assert(t, strings.Contains(strings.Join(o.InputFor, ""), "j-123abc"), "expected dataset to be up to date")
	assert(t, strings.Contains(strings.Join(o.OutputFrom, ""), "j-456def"), "expected dataset to be up to date and to contain job info for output section")
	assert(t, o.Size == 1337, "expected dataset to be up to date and contain new size")
}
<commit_msg>Add test for updating dataset without new data<commit_after>package svc_test

import (
	"context"
	"strings"
	"testing"
	"time"

	"github.com/nerdalize/nerd/svc"
)

func TestUpdateDataset(t *testing.T) {
	di, clean := testDI(t)
	defer clean()

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()

	kube := svc.NewKube(di)
	out, err := kube.CreateDataset(ctx, &svc.CreateDatasetInput{Name: "my-dataset", Bucket: "bogus", Key: "my-key"})
	ok(t, err)

	newSize := uint64(1337)
	_, err = kube.UpdateDataset(ctx, &svc.UpdateDatasetInput{
		Name:       out.Name,
		InputFor:   "j-123abc",
		OutputFrom: "j-456def",
		Size:       &newSize,
	})
	ok(t, err)

	o, err := kube.GetDataset(ctx, &svc.GetDatasetInput{Name: out.Name})
	ok(t, err)
	assert(t, strings.Contains(strings.Join(o.InputFor, ""), "j-123abc"), "expected dataset to be up to date")
	assert(t, strings.Contains(strings.Join(o.OutputFrom, ""), "j-456def"), "expected dataset to be up to date and to contain job info for output section")
	assert(t, o.Size == 1337, "expected dataset to be up to date and contain new size")

	//Check if the output remains the same when not specifying any changes
	_, err = kube.UpdateDataset(ctx, &svc.UpdateDatasetInput{
		Name:       out.Name,
	})
	ok(t, err)

	o2, err := kube.GetDataset(ctx, &svc.GetDatasetInput{Name: out.Name})
	ok(t, err)
	equals(t, o.Size, o2.Size)
	equals(t, o.InputFor, o2.InputFor)
	equals(t, o.OutputFrom, o2.OutputFrom)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"bytes"
	"strings"

	"github.com/cpmech/gosl/io"
)

func main() {

	// catch errors
	defer func() {
		if err := recover(); err != nil {
			io.PfRed("ERROR: %v\n", err)
		}
	}()

	// input data
	filename, fnkey := io.ArgToFilename(0, "data/equations.txt", "", true)
	io.Pf("\n%s\n", io.ArgsTable("INPUT ARGUMENTS",
		"file with equations", "filename", filename,
	))

	io.Pforan("fnkey = %v\n", fnkey)

	// open file
	f, err := io.OpenFileR(filename)
	if err != nil {
		return
	}

	// constants
	MAXNIDX := 100
	INDICES := []string{"i", "j", "k", "l", "m", "n", "r", "s", "p", "q"}

	// read file
	var buf bytes.Buffer
	io.ReadLinesFile(f, func(idx int, line string) (stop bool) {

		// indices
		l := line
		for i := 0; i < MAXNIDX; i++ {
			l = strings.Replace(l, io.Sf("[%d]", i), io.Sf("_{%d}", i), -1)
		}
		for _, idx := range INDICES {
			l = strings.Replace(l, io.Sf("[%s]", idx), io.Sf("_{%s}", idx), -1)
		}

		// constants
		l = strings.Replace(l, "math.Sqrt2", "\\sqrt{2}", -1)
		l = strings.Replace(l, "SQ2", "\\sqrt{2}", -1)

		// functions
		l = strings.Replace(l, "math.Sqrt", "\\sqrt", -1)
		l = strings.Replace(l, "math.Pow", "\\pow", -1)
		l = strings.Replace(l, "math.Exp", "\\exp", -1)
		l = strings.Replace(l, "math.Sin", "\\sin", -1)
		l = strings.Replace(l, "math.Cos", "\\cos", -1)

		// star
		l = strings.Replace(l, "*", " \\, ", -1)

		// colon-equal
		l = strings.Replace(l, ":=", "=", -1)

		// add to results
		io.Ff(&buf, "%s\n", l)
		return
	})

	// write file
	io.WriteFileVD("/tmp/gosl", fnkey+".tex", &buf)
}
<commit_msg>Handle return error<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"bytes"
	"os"
	"strings"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

func status(err error) {
	if err != nil {
		io.Pf("ERROR: %v\n", err)
		chk.Verbose = true
		chk.CallerInfo(2)
		os.Exit(1)
	}
}

func main() {

	// catch errors
	defer func() {
		if err := recover(); err != nil {
			io.PfRed("ERROR: %v\n", err)
		}
	}()

	// input data
	filename, fnkey := io.ArgToFilename(0, "data/equations.txt", "", true)
	io.Pf("\n%s\n", io.ArgsTable("INPUT ARGUMENTS",
		"file with equations", "filename", filename,
	))

	io.Pforan("fnkey = %v\n", fnkey)

	// open file
	f, err := io.OpenFileR(filename)
	status(err)

	// constants
	MAXNIDX := 100
	INDICES := []string{"i", "j", "k", "l", "m", "n", "r", "s", "p", "q"}

	// read file
	var buf bytes.Buffer
	err = io.ReadLinesFile(f, func(idx int, line string) (stop bool) {

		// indices
		l := line
		for i := 0; i < MAXNIDX; i++ {
			l = strings.Replace(l, io.Sf("[%d]", i), io.Sf("_{%d}", i), -1)
		}
		for _, idx := range INDICES {
			l = strings.Replace(l, io.Sf("[%s]", idx), io.Sf("_{%s}", idx), -1)
		}

		// constants
		l = strings.Replace(l, "math.Sqrt2", "\\sqrt{2}", -1)
		l = strings.Replace(l, "SQ2", "\\sqrt{2}", -1)

		// functions
		l = strings.Replace(l, "math.Sqrt", "\\sqrt", -1)
		l = strings.Replace(l, "math.Pow", "\\pow", -1)
		l = strings.Replace(l, "math.Exp", "\\exp", -1)
		l = strings.Replace(l, "math.Sin", "\\sin", -1)
		l = strings.Replace(l, "math.Cos", "\\cos", -1)

		// star
		l = strings.Replace(l, "*", " \\, ", -1)

		// colon-equal
		l = strings.Replace(l, ":=", "=", -1)

		// add to results
		io.Ff(&buf, "%s\n", l)
		return
	})
	status(err)

	// write file
	io.WriteFileVD("/tmp/gosl", fnkey+".tex", &buf)
}
<|endoftext|>


<commit_before>package wall

import (
	"sync"

	"github.com/satori/go.uuid"

	"github.com/ivan1993spb/snake-server/engine"
	"github.com/ivan1993spb/snake-server/world"
)

const ruinsFactor = 0.20

var dotsMaskOne = engine.NewDotsMask([][]uint8{{1}})

var ruins = [...]*engine.DotsMask{
	dotsMaskOne,
	engine.DotsMaskSquare2x2,
	engine.DotsMaskTank,
	engine.DotsMaskHome,
	engine.DotsMaskCross,
	engine.DotsMaskDiagonal,
	engine.DotsMaskCrossSmall,
	engine.DotsMaskDiagonalSmall,
	engine.DotsMaskLabyrinth,
}

func calcRuinsCount(size uint16) int {
	return int(float32(size) * ruinsFactor)
}

type ErrCreateWallRuins string

func (e ErrCreateWallRuins) Error() string {
	return "cannot create wall ruins: " + string(e)
}

func NewWallRuins(world *world.World) (*Wall, error) {
	area, err := engine.NewArea(world.Width(), world.Height())
	if err != nil {
		return nil, ErrCreateWallRuins(err.Error())
	}

	size := area.Size()
	ruinsCount := calcRuinsCount(size)
	wallLocation := make(engine.Location, 0, ruinsCount)

	for len(wallLocation) < ruinsCount {
		for i := 0; i < len(ruins); i++ {
			mask := ruins[i].TurnRandom()

			if area.Width() >= mask.Width() && area.Height() >= mask.Height() {
				rect, err := area.NewRandomRect(mask.Width(), mask.Height(), 0, 0)
				if err != nil {
					continue
				}

				location := mask.Location(rect.X(), rect.Y())
				if len(location) > ruinsCount-len(wallLocation) {
					location = location[:ruinsCount-len(wallLocation)]
				}

				wallLocation = append(wallLocation, location...)
			}
		}
	}

	wall := &Wall{
		uuid:  uuid.Must(uuid.NewV4()).String(),
		world: world,
		mux:   &sync.RWMutex{},
	}

	if resultLocation, err := world.CreateObjectAvailableDots(wall, wallLocation); err != nil {
		return nil, ErrCreateWallRuins(err.Error())
	} else {
		wall.mux.Lock()
		wall.location = resultLocation
		wall.mux.Unlock()
	}

	return wall, nil
}
<commit_msg>Create new masks in ruins<commit_after>package wall

import (
	"sync"

	"github.com/satori/go.uuid"

	"github.com/ivan1993spb/snake-server/engine"
	"github.com/ivan1993spb/snake-server/world"
)

const ruinsFactor = 0.20

var dotsMaskOne = engine.NewDotsMask([][]uint8{{1}})

var ruins = []*engine.DotsMask{
	dotsMaskOne,
	engine.DotsMaskSquare2x2,
	engine.DotsMaskTank,
	engine.DotsMaskHome1,
	engine.DotsMaskHome2,
	engine.DotsMaskCross,
	engine.DotsMaskDiagonal,
	engine.DotsMaskCrossSmall,
	engine.DotsMaskDiagonalSmall,
	engine.DotsMaskLabyrinth,
	engine.DotsMaskTunnel1,
	engine.DotsMaskTunnel2,
}

func calcRuinsCount(size uint16) int {
	return int(float32(size) * ruinsFactor)
}

type ErrCreateWallRuins string

func (e ErrCreateWallRuins) Error() string {
	return "cannot create wall ruins: " + string(e)
}

func NewWallRuins(world *world.World) (*Wall, error) {
	area, err := engine.NewArea(world.Width(), world.Height())
	if err != nil {
		return nil, ErrCreateWallRuins(err.Error())
	}

	size := area.Size()
	ruinsCount := calcRuinsCount(size)
	wallLocation := make(engine.Location, 0, ruinsCount)

	for len(wallLocation) < ruinsCount {
		for i := 0; i < len(ruins); i++ {
			mask := ruins[i].TurnRandom()

			if area.Width() >= mask.Width() && area.Height() >= mask.Height() {
				rect, err := area.NewRandomRect(mask.Width(), mask.Height(), 0, 0)
				if err != nil {
					continue
				}

				location := mask.Location(rect.X(), rect.Y())
				if len(location) > ruinsCount-len(wallLocation) {
					location = location[:ruinsCount-len(wallLocation)]
				}

				wallLocation = append(wallLocation, location...)
			}
		}
	}

	wall := &Wall{
		uuid:  uuid.Must(uuid.NewV4()).String(),
		world: world,
		mux:   &sync.RWMutex{},
	}

	if resultLocation, err := world.CreateObjectAvailableDots(wall, wallLocation); err != nil {
		return nil, ErrCreateWallRuins(err.Error())
	} else {
		wall.mux.Lock()
		wall.location = resultLocation
		wall.mux.Unlock()
	}

	return wall, nil
}
<|endoftext|>


<commit_before>package gorpc

// Dispatcher .
type Dispatcher interface {
	Dispatch(call *Request) (callReturn *Response, err error)
	ID() uint16
}
<commit_msg>add String() string method to Dispatcher interface<commit_after>package gorpc

// Dispatcher .
type Dispatcher interface {
	Dispatch(call *Request) (callReturn *Response, err error)
	ID() uint16
	String() string
}
<|endoftext|>


<commit_before>package openapi3

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

	oas3 "github.com/getkin/kin-openapi/openapi3"
	"github.com/grokify/gocharts/data/table"
	"github.com/grokify/gotilla/encoding/jsonutil"
	"github.com/grokify/gotilla/text"
)

type SpecMore struct {
	Spec *oas3.Swagger
}

func ReadSpecMore(path string, validate bool) (*SpecMore, error) {
	spec, err := ReadFile(path, validate)
	if err != nil {
		return nil, err
	}
	return &SpecMore{Spec: spec}, nil
}

func (s *SpecMore) SchemaCount() int {
	if s.Spec == nil {
		return -1
	} else if s.Spec.Components.Schemas == nil {
		return 0
	}
	return len(s.Spec.Components.Schemas)
}

func (s *SpecMore) OperationsTable(columns *text.TextSet) (*table.TableData, error) {
	return operationsTable(s.Spec, columns)
}

func operationsTable(spec *oas3.Swagger, columns *text.TextSet) (*table.TableData, error) {
	if columns == nil {
		columns = &text.TextSet{Texts: DefaultColumns()}
	}
	tbl := table.NewTableData()
	tbl.Name = spec.Info.Title
	tbl.Columns = columns.DisplayTexts()

	tgs, err := SpecTagGroups(spec)
	if err != nil {
		return nil, err
	}

	VisitOperations(spec, func(path, method string, op *oas3.Operation) {
		row := []string{}

		for _, text := range columns.Texts {
			switch text.Slug {
			case "method":
				row = append(row, method)
			case "path":
				row = append(row, path)
			case "operationId":
				row = append(row, op.OperationID)
			case "summary":
				row = append(row, op.Summary)
			case "tags":
				row = append(row, strings.Join(op.Tags, ", "))
			case "x-tag-groups":
				row = append(row, strings.Join(
					tgs.GetTagGroupNamesForTagNames(op.Tags...), ", "))
			default:
				row = append(row, GetExtensionPropStringOrEmpty(op.ExtensionProps, text.Slug))
			}
		}

		tbl.Records = append(tbl.Records, row)
	})
	return &tbl, nil
}

func DefaultColumns() []text.Text {
	texts := []text.Text{
		{
			Display: "Method",
			Slug:    "method"},
		{
			Display: "Path",
			Slug:    "path"},
		{
			Display: "OperationID",
			Slug:    "operationId"},
		{
			Display: "Summary",
			Slug:    "summary"},
		{
			Display: "Tags",
			Slug:    "tags"},
	}
	return texts
}

/*
func (s *SpecMore) OperationsTableOld() (*table.TableData, error) {
	tbl := table.NewTableData()
	tbl.Name = "Operations"
	tgs, err := SpecTagGroups(s.Spec)
	if err != nil {
		return nil, err
	}
	addTagGroups := false
	if len(tgs.TagGroups) > 0 {
		addTagGroups = true
		tbl.Columns = []string{"OperationId", "Summary", "Path", "Method", "Tag Groups", "Tags"}
	} else {
		tbl.Columns = []string{"OperationId", "Summary", "Path", "Method", "Tags"}
	}
	ops := s.OperationMetas()
	for _, op := range ops {
		if addTagGroups {
			tagGroupNames := tgs.GetTagGroupNamesForTagNames(op.Tags...)
			tbl.Records = append(tbl.Records, []string{
				op.OperationID,
				op.Summary,
				op.Path,
				op.Method,
				strings.Join(tagGroupNames, ","),
				strings.Join(stringsutil.SliceCondenseSpace(op.Tags, true, true), ",")})
		} else {
			tbl.Records = append(tbl.Records, []string{
				op.OperationID,
				op.Summary,
				op.Path,
				op.Method,
				strings.Join(op.Tags, ",")})
		}
	}
	return &tbl, nil
}
*/

func (s *SpecMore) OperationMetas() []OperationMeta {
	ometas := []OperationMeta{}
	if s.Spec == nil {
		return ometas
	}
	for url, path := range s.Spec.Paths {
		if path.Connect != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodConnect, path.Connect))
		}
		if path.Delete != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodDelete, path.Delete))
		}
		if path.Get != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodGet, path.Get))
		}
		if path.Head != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodHead, path.Head))
		}
		if path.Options != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodOptions, path.Options))
		}
		if path.Patch != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPatch, path.Patch))
		}
		if path.Post != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPost, path.Post))
		}
		if path.Put != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPut, path.Put))
		}
		if path.Trace != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodTrace, path.Trace))
		}
	}

	return ometas
}

func (s *SpecMore) OperationsCount() uint {
	return uint(len(s.OperationMetas()))
}

func (s *SpecMore) WriteFileJSON(filename string, perm os.FileMode, prefix, indent string) error {
	jsonData, err := s.Spec.MarshalJSON()
	if err != nil {
		return err
	}
	pretty := false
	if len(prefix) > 0 || len(indent) > 0 {
		pretty = true
	}
	if pretty {
		jsonData = jsonutil.PrettyPrint(jsonData, "", "  ")
	}
	return ioutil.WriteFile(filename, jsonData, perm)
}

func (sm *SpecMore) WriteFileXLSX(filename string) error {
	tbl, err := sm.OperationsTable(nil)
	if err != nil {
		return err
	}
	return table.WriteXLSX(filename, tbl)
}

type TagsMore struct {
	Tags oas3.Tags
}

func (tg *TagsMore) Get(tagName string) *oas3.Tag {
	for _, tag := range tg.Tags {
		if tagName == tag.Name {
			return tag
		}
	}
	return nil
}
<commit_msg>feat: openapi3: add SpecMore.SchemaNames(); add SpecMore.SchemaNameExists()<commit_after>package openapi3

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

	"github.com/grokify/gotilla/type/stringsutil"

	oas3 "github.com/getkin/kin-openapi/openapi3"
	"github.com/grokify/gocharts/data/table"
	"github.com/grokify/gotilla/encoding/jsonutil"
	"github.com/grokify/gotilla/text"
)

type SpecMore struct {
	Spec *oas3.Swagger
}

func ReadSpecMore(path string, validate bool) (*SpecMore, error) {
	spec, err := ReadFile(path, validate)
	if err != nil {
		return nil, err
	}
	return &SpecMore{Spec: spec}, nil
}

func (s *SpecMore) SchemaCount() int {
	if s.Spec == nil {
		return -1
	} else if s.Spec.Components.Schemas == nil {
		return 0
	}
	return len(s.Spec.Components.Schemas)
}

func (s *SpecMore) OperationsTable(columns *text.TextSet) (*table.TableData, error) {
	return operationsTable(s.Spec, columns)
}

func operationsTable(spec *oas3.Swagger, columns *text.TextSet) (*table.TableData, error) {
	if columns == nil {
		columns = &text.TextSet{Texts: DefaultColumns()}
	}
	tbl := table.NewTableData()
	tbl.Name = spec.Info.Title
	tbl.Columns = columns.DisplayTexts()

	tgs, err := SpecTagGroups(spec)
	if err != nil {
		return nil, err
	}

	VisitOperations(spec, func(path, method string, op *oas3.Operation) {
		row := []string{}

		for _, text := range columns.Texts {
			switch text.Slug {
			case "method":
				row = append(row, method)
			case "path":
				row = append(row, path)
			case "operationId":
				row = append(row, op.OperationID)
			case "summary":
				row = append(row, op.Summary)
			case "tags":
				row = append(row, strings.Join(op.Tags, ", "))
			case "x-tag-groups":
				row = append(row, strings.Join(
					tgs.GetTagGroupNamesForTagNames(op.Tags...), ", "))
			default:
				row = append(row, GetExtensionPropStringOrEmpty(op.ExtensionProps, text.Slug))
			}
		}

		tbl.Records = append(tbl.Records, row)
	})
	return &tbl, nil
}

func DefaultColumns() []text.Text {
	texts := []text.Text{
		{
			Display: "Method",
			Slug:    "method"},
		{
			Display: "Path",
			Slug:    "path"},
		{
			Display: "OperationID",
			Slug:    "operationId"},
		{
			Display: "Summary",
			Slug:    "summary"},
		{
			Display: "Tags",
			Slug:    "tags"},
	}
	return texts
}

/*
func (s *SpecMore) OperationsTableOld() (*table.TableData, error) {
	tbl := table.NewTableData()
	tbl.Name = "Operations"
	tgs, err := SpecTagGroups(s.Spec)
	if err != nil {
		return nil, err
	}
	addTagGroups := false
	if len(tgs.TagGroups) > 0 {
		addTagGroups = true
		tbl.Columns = []string{"OperationId", "Summary", "Path", "Method", "Tag Groups", "Tags"}
	} else {
		tbl.Columns = []string{"OperationId", "Summary", "Path", "Method", "Tags"}
	}
	ops := s.OperationMetas()
	for _, op := range ops {
		if addTagGroups {
			tagGroupNames := tgs.GetTagGroupNamesForTagNames(op.Tags...)
			tbl.Records = append(tbl.Records, []string{
				op.OperationID,
				op.Summary,
				op.Path,
				op.Method,
				strings.Join(tagGroupNames, ","),
				strings.Join(stringsutil.SliceCondenseSpace(op.Tags, true, true), ",")})
		} else {
			tbl.Records = append(tbl.Records, []string{
				op.OperationID,
				op.Summary,
				op.Path,
				op.Method,
				strings.Join(op.Tags, ",")})
		}
	}
	return &tbl, nil
}
*/

func (s *SpecMore) OperationMetas() []OperationMeta {
	ometas := []OperationMeta{}
	if s.Spec == nil {
		return ometas
	}
	for url, path := range s.Spec.Paths {
		if path.Connect != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodConnect, path.Connect))
		}
		if path.Delete != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodDelete, path.Delete))
		}
		if path.Get != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodGet, path.Get))
		}
		if path.Head != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodHead, path.Head))
		}
		if path.Options != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodOptions, path.Options))
		}
		if path.Patch != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPatch, path.Patch))
		}
		if path.Post != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPost, path.Post))
		}
		if path.Put != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPut, path.Put))
		}
		if path.Trace != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodTrace, path.Trace))
		}
	}

	return ometas
}

func (s *SpecMore) OperationsCount() uint {
	return uint(len(s.OperationMetas()))
}

func (sm *SpecMore) SchemaNames() []string {
	schemaNames := []string{}
	for schemaName := range sm.Spec.Components.Schemas {
		schemaNames = append(schemaNames, schemaName)
	}
	return stringsutil.SliceCondenseSpace(schemaNames, true, true)
}

func (sm *SpecMore) SchemaNameExists(schemaName string, includeNil bool) bool {
	for schemaNameTry, schemaRef := range sm.Spec.Components.Schemas {
		if schemaNameTry == schemaName {
			if includeNil {
				return true
			} else if schemaRef == nil {
				return false
			}
			schemaRef.Ref = strings.TrimSpace(schemaRef.Ref)
			if len(schemaRef.Ref) > 0 {
				return true
			}
			if schemaRef.Value == nil {
				return false
			} else {
				return true
			}
		}
	}
	return false
}

func (s *SpecMore) WriteFileJSON(filename string, perm os.FileMode, prefix, indent string) error {
	jsonData, err := s.Spec.MarshalJSON()
	if err != nil {
		return err
	}
	pretty := false
	if len(prefix) > 0 || len(indent) > 0 {
		pretty = true
	}
	if pretty {
		jsonData = jsonutil.PrettyPrint(jsonData, "", "  ")
	}
	return ioutil.WriteFile(filename, jsonData, perm)
}

func (sm *SpecMore) WriteFileXLSX(filename string) error {
	tbl, err := sm.OperationsTable(nil)
	if err != nil {
		return err
	}
	return table.WriteXLSX(filename, tbl)
}

type TagsMore struct {
	Tags oas3.Tags
}

func (tg *TagsMore) Get(tagName string) *oas3.Tag {
	for _, tag := range tg.Tags {
		if tagName == tag.Name {
			return tag
		}
	}
	return nil
}
<|endoftext|>