<commit_before>package mmio

import (
	"bits"
	"unsafe"
)

//c:volatile
type U8 struct {
	r uint8
}

func PtrU8(addr unsafe.Pointer) *U8 {
	return (*U8)(addr)
}

func AsU8(addr *uint8) *U8 {
	return (*U8)(unsafe.Pointer(addr))
}

func (r *U8) SetBit(n int) {
	r.r |= uint8(1) << uint(n)
}

func (r *U8) ClearBit(n int) {
	r.r &^= uint8(1) << uint(n)
}

func (r *U8) Bit(n int) int {
	return int(r.r>>uint(n)) & 1
}

func (r *U8) StoreBit(n, v int) {
	mask := uint8(1) << uint(n)
	r.r = r.r&^mask | uint8(v<<uint(n))&mask
}

func (r *U8) Bits(mask uint8) uint8 {
	return r.r & mask
}

func (r *U8) StoreBits(mask, bits uint8) {
	r.r = r.r&^mask | bits&mask
}

func (r *U8) SetBits(mask uint8) {
	r.r |= mask
}

func (r *U8) ClearBits(mask uint8) {
	r.r &^= mask
}

func (r *U8) Load() uint8 {
	return r.r
}

func (r *U8) Store(v uint8) {
	r.r = v
}

func (r *U8) Addr() uintptr {
	return uintptr(unsafe.Pointer(r))
}

func (r *U8) Field(mask uint8) int {
	return bits.Field32(uint32(r.r), uint32(mask))
}

func (r *U8) SetField(mask uint8, v int) {
	r.StoreBits(mask, uint8(bits.Make32(v, uint32(mask))))
}

type UM8 struct {
	U    *U8
	Mask uint8
}

func (b UM8) Set()             { b.U.SetBits(b.Mask) }
func (b UM8) Clear()           { b.U.ClearBits(b.Mask) }
func (b UM8) Load() uint8      { return b.U.Bits(b.Mask) }
func (b UM8) Store(bits uint8) { b.U.StoreBits(b.Mask, bits) }
func (b UM8) LoadVal() int     { return b.U.Field(uint8(b.Mask)) }
func (b UM8) StoreVal(v int)   { b.U.SetField(b.Mask, v) }

//c:volatile
type U16 struct {
	r uint16
}

func PtrU16(addr unsafe.Pointer) *U16 {
	return (*U16)(addr)
}

func AsU16(addr *uint16) *U16 {
	return (*U16)(unsafe.Pointer(addr))
}

func (r *U16) SetBit(n int) {
	r.r |= uint16(1) << uint(n)
}

func (r *U16) ClearBit(n int) {
	r.r &^= uint16(1) << uint(n)
}

func (r *U16) Bit(n int) int {
	return int(r.r>>uint(n)) & 1
}

func (r *U16) StoreBit(n, v int) {
	mask := uint16(1) << uint(n)
	r.r = r.r&^mask | uint16(v<<uint(n))&mask
}

func (r *U16) Bits(mask uint16) uint16 {
	return r.r & mask
}

func (r *U16) StoreBits(mask, bits uint16) {
	r.r = r.r&^mask | bits&mask
}

func (r *U16) SetBits(mask uint16) {
	r.r |= mask
}

func (r *U16) ClearBits(mask uint16) {
	r.r &^= mask
}

func (r *U16) Load() uint16 {
	return r.r
}

func (r *U16) Store(v uint16) {
	r.r = v
}

func (r *U16) Addr() uintptr {
	return uintptr(unsafe.Pointer(r))
}

func (r *U16) Field(mask uint16) int {
	return bits.Field32(uint32(r.r), uint32(mask))
}

func (r *U16) SetField(mask uint16, v int) {
	r.StoreBits(mask, uint16(bits.Make32(v, uint32(mask))))
}

type UM16 struct {
	U    *U16
	Mask uint16
}

func (b UM16) Set()              { b.U.SetBits(b.Mask) }
func (b UM16) Clear()            { b.U.ClearBits(b.Mask) }
func (b UM16) Load() uint16      { return b.U.Bits(b.Mask) }
func (b UM16) Store(bits uint16) { b.U.StoreBits(b.Mask, bits) }
func (b UM16) LoadVal() int      { return b.U.Field(uint16(b.Mask)) }
func (b UM16) StoreVal(v int)    { b.U.SetField(b.Mask, v) }

//c:volatile
type U32 struct {
	r uint32
}

func PtrU32(addr unsafe.Pointer) *U32 {
	return (*U32)(addr)
}

func AsU32(addr *uint32) *U32 {
	return (*U32)(unsafe.Pointer(addr))
}

func (r *U32) SetBit(n int) {
	r.r |= uint32(1) << uint(n)
}

func (r *U32) ClearBit(n int) {
	r.r &^= uint32(1) << uint(n)
}

func (r *U32) Bit(n int) int {
	return int(r.r>>uint(n)) & 1
}

func (r *U32) StoreBit(n, v int) {
	mask := uint32(1) << uint(n)
	r.r = r.r&^mask | uint32(v<<uint(n))&mask
}
func (r *U32) Bits(mask uint32) uint32 {
	return r.r & mask
}

func (r *U32) StoreBits(mask, bits uint32) {
	r.r = r.r&^mask | bits&mask
}

func (r *U32) SetBits(mask uint32) {
	r.r |= mask
}

func (r *U32) ClearBits(mask uint32) {
	r.r &^= mask
}

func (r *U32) Load() uint32 {
	return r.r
}

func (r *U32) Store(v uint32) {
	r.r = v
}

func (r *U32) Addr() uintptr {
	return uintptr(unsafe.Pointer(r))
}

func (r *U32) Field(mask uint32) int {
	return bits.Field32(r.r, mask)
}

func (r *U32) SetField(mask uint32, v int) {
	r.StoreBits(mask, bits.Make32(v, mask))
}

type UM32 struct {
	U    *U32
	Mask uint32
}

func (b UM32) Set()              { b.U.SetBits(b.Mask) }
func (b UM32) Clear()            { b.U.ClearBits(b.Mask) }
func (b UM32) Load() uint32      { return b.U.Bits(b.Mask) }
func (b UM32) Store(bits uint32) { b.U.StoreBits(b.Mask, bits) }
func (b UM32) LoadVal() int      { return b.U.Field(uint32(b.Mask)) }
func (b UM32) StoreVal(v int)    { b.U.SetField(b.Mask, v) }
<commit_msg>mmio: Add fence.Compiler to most functions to avoid reordering memory ordinary accesses with volatile memory accesses<commit_after>// Package mmio provides data types that can be used to access memory mapped
// registers of peripherals. All methods in this package guarantee that compiler
// does not reorder method call with any memory load/store which is before it
// in source code.
package mmio

import (
	"sync/fence"
	"bits"
	"unsafe"
)

//c:volatile
type U8 struct {
	r uint8
}

func PtrU8(addr unsafe.Pointer) *U8 {
	return (*U8)(addr)
}

func AsU8(addr *uint8) *U8 {
	return (*U8)(unsafe.Pointer(addr))
}

func (r *U8) Addr() uintptr {
	return uintptr(unsafe.Pointer(r))
}

func (r *U8) SetBit(n int) {
	fence.Compiler()
	r.r |= uint8(1) << uint(n)
}

func (r *U8) ClearBit(n int) {
	fence.Compiler()
	r.r &^= uint8(1) << uint(n)
}

func (r *U8) Bit(n int) int {
	return int(r.r>>uint(n)) & 1
}

func (r *U8) StoreBit(n, v int) {
	mask := uint8(1) << uint(n)
	fence.Compiler()
	r.r = r.r&^mask | uint8(v<<uint(n))&mask
}

func (r *U8) Bits(mask uint8) uint8 {
	fence.Compiler()
	return r.r & mask
}

func (r *U8) StoreBits(mask, bits uint8) {
	fence.Compiler()
	r.r = r.r&^mask | bits&mask
}

func (r *U8) SetBits(mask uint8) {
	fence.Compiler()
	r.r |= mask
}

func (r *U8) ClearBits(mask uint8) {
	fence.Compiler()
	r.r &^= mask
}

func (r *U8) Load() uint8 {
	fence.Compiler()
	return r.r
}

func (r *U8) Store(v uint8) {
	fence.Compiler()
	r.r = v
}

func (r *U8) Field(mask uint8) int {
	fence.Compiler()
	return bits.Field32(uint32(r.r), uint32(mask))
}

func (r *U8) SetField(mask uint8, v int) {
	r.StoreBits(mask, uint8(bits.Make32(v, uint32(mask))))
}

type UM8 struct {
	U    *U8
	Mask uint8
}

func (b UM8) Set()             { b.U.SetBits(b.Mask) }
func (b UM8) Clear()           { b.U.ClearBits(b.Mask) }
func (b UM8) Load() uint8      { return b.U.Bits(b.Mask) }
func (b UM8) Store(bits uint8) { b.U.StoreBits(b.Mask, bits) }
func (b UM8) LoadVal() int     { return b.U.Field(uint8(b.Mask)) }
func (b UM8) StoreVal(v int)   { b.U.SetField(b.Mask, v) }

//c:volatile
type U16 struct {
	r uint16
}

func PtrU16(addr unsafe.Pointer) *U16 {
	return (*U16)(addr)
}

func AsU16(addr *uint16) *U16 {
	return (*U16)(unsafe.Pointer(addr))
}

func (r *U16) Addr() uintptr {
	return uintptr(unsafe.Pointer(r))
}

func (r *U16) SetBit(n int) {
	fence.Compiler()
	r.r |= uint16(1) << uint(n)
}

func (r *U16) ClearBit(n int) {
	fence.Compiler()
	r.r &^= uint16(1) << uint(n)
}

func (r *U16) Bit(n int) int {
	fence.Compiler()
	return int(r.r>>uint(n)) & 1
}

func (r *U16) StoreBit(n, v int) {
	mask := uint16(1) << uint(n)
	fence.Compiler()
	r.r = r.r&^mask | uint16(v<<uint(n))&mask
}

func (r *U16) Bits(mask uint16) uint16 {
	fence.Compiler()
	return r.r & mask
}

func (r *U16) StoreBits(mask, bits uint16) {
	fence.Compiler()
	r.r = r.r&^mask | bits&mask
}

func (r *U16) SetBits(mask uint16) {
	fence.Compiler()
	r.r |= mask
}

func (r *U16) ClearBits(mask uint16) {
	fence.Compiler()
	r.r &^= mask
}

func (r *U16) Load() uint16 {
	fence.Compiler()
	return r.r
}

func (r *U16) Store(v uint16) {
	fence.Compiler()
	r.r = v
}

func (r *U16) Field(mask uint16) int {
	fence.Compiler()
	return bits.Field32(uint32(r.r), uint32(mask))
}

func (r *U16) SetField(mask uint16, v int) {
	r.StoreBits(mask, uint16(bits.Make32(v, uint32(mask))))
}

type UM16 struct {
	U    *U16
	Mask uint16
}

func (b UM16) Set()              { b.U.SetBits(b.Mask) }
func (b UM16) Clear()            { b.U.ClearBits(b.Mask) }
func (b UM16) Load() uint16      { return b.U.Bits(b.Mask) }
func (b UM16) Store(bits uint16) { b.U.StoreBits(b.Mask, bits) }
func (b UM16) LoadVal() int      { return b.U.Field(uint16(b.Mask)) }
func (b UM16) StoreVal(v int)    { b.U.SetField(b.Mask, v) }

//c:volatile
type U32 struct {
	r uint32
}

func PtrU32(addr unsafe.Pointer) *U32 {
	return (*U32)(addr)
}

func AsU32(addr *uint32) *U32 {
	return (*U32)(unsafe.Pointer(addr))
}

func (r *U32) Addr() uintptr {
	return uintptr(unsafe.Pointer(r))
}

func (r *U32) SetBit(n int) {
	fence.Compiler()
	r.r |= uint32(1) << uint(n)
}

func (r *U32) ClearBit(n int) {
	fence.Compiler()
	r.r &^= uint32(1) << uint(n)
}

func (r *U32) Bit(n int) int {
	fence.Compiler()
	return int(r.r>>uint(n)) & 1
}

func (r *U32) StoreBit(n, v int) {
	mask := uint32(1) << uint(n)
	fence.Compiler()
	r.r = r.r&^mask | uint32(v<<uint(n))&mask
}
func (r *U32) Bits(mask uint32) uint32 {
	fence.Compiler()
	return r.r & mask
}

func (r *U32) StoreBits(mask, bits uint32) {
	fence.Compiler()
	r.r = r.r&^mask | bits&mask
}

func (r *U32) SetBits(mask uint32) {
	fence.Compiler()
	r.r |= mask
}

func (r *U32) ClearBits(mask uint32) {
	fence.Compiler()
	r.r &^= mask
}

func (r *U32) Load() uint32 {
	fence.Compiler()
	return r.r
}

func (r *U32) Store(v uint32) {
	fence.Compiler()
	r.r = v
}

func (r *U32) Field(mask uint32) int {
	fence.Compiler()
	return bits.Field32(r.r, mask)
}

func (r *U32) SetField(mask uint32, v int) {
	r.StoreBits(mask, bits.Make32(v, mask))
}

type UM32 struct {
	U    *U32
	Mask uint32
}

func (b UM32) Set()              { b.U.SetBits(b.Mask) }
func (b UM32) Clear()            { b.U.ClearBits(b.Mask) }
func (b UM32) Load() uint32      { return b.U.Bits(b.Mask) }
func (b UM32) Store(bits uint32) { b.U.StoreBits(b.Mask, bits) }
func (b UM32) LoadVal() int      { return b.U.Field(uint32(b.Mask)) }
func (b UM32) StoreVal(v int)    { b.U.SetField(b.Mask, v) }
<|endoftext|>


<commit_before>// +build noos

package rtos

import (
	"sync/fence"
	"syscall"
)

func sleepUntil(end int64) {
	for Nanosec() < end {
		syscall.SetAlarm(end)
		syscall.Alarm.Wait()
	}
}

func at(t int64) <-chan int64 {
	syscall.SetAt(t)
	return syscall.TimeChan()
}

type eventFlag struct {
	state syscall.Event
}

func atomicInitLoadState(p *syscall.Event) syscall.Event {
	state := syscall.AtomicLoadEvent(p)
	if state == 0 {
		state = syscall.AssignEventFlag()
		if !syscall.AtomicCompareAndSwapEvent(p, 0, state) {
			state = syscall.AtomicLoadEvent(p)
		}
	}
	return state
}

// ARMv7-M, -Os: 11 instructions, 28 bytes, stacking 4 registers.
func (f *EventFlag) reset(val int) {
	fence.RW_SMP() // Reset has RELEASE semantic.
	state := atomicInitLoadState(&f.state)
	event := state &^ 1
	new := event | syscall.Event(val&1)
	if state != new {
		syscall.AtomicStoreEvent(&f.state, new)
	}
}

// ARMv7-M, -Os: 19 instructions, 54 bytes, stacking 4 registers.
func (f *EventFlag) signal(val int) {
	fence.RW_SMP() // Signal has RELEASE semantic.
	state := atomicInitLoadState(&f.state)
	event := state &^ 1
	new := event | syscall.Event(val&1)
	if state != new {
		syscall.AtomicStoreEvent(&f.state, new)
		event.Send()
	}
}

/*
// ARMv7-M, -Os: 21 instructions, 58 bytes, stacking 6 registers.
func (f *EventFlag) set(val int, wkup bool) {
	fence.RW_SMP() // Set has RELEASE semantic.
	state := atomicInitLoadState(&f.state)
	event := state &^ 1
	new := event | syscall.Event(val&1)
	if state != new {
		syscall.AtomicStoreEvent(&f.state, new)
		if wkup {
			event.Send()
		}
	}
}
*/

func (f *EventFlag) value() int {
	// Not need  atomicInitLoadState because an uninitialized f is zero.
	v := int(syscall.AtomicLoadEvent(&f.state) & 1)
	fence.RW_SMP()
	return v
}

func (f *EventFlag) wait(val int, deadline int64) (done bool) {
	state := atomicInitLoadState(&f.state)
	event := state &^ 1
	need := event | syscall.Event(val&1)
	if deadline != 0 {
		event |= syscall.Alarm
	}
	for {
		done = (state == need)
		if done {
			break
		}
		if deadline != 0 {
			if Nanosec() >= deadline {
				break
			}
			syscall.SetAlarm(deadline)
		}
		event.Wait()
		state = syscall.AtomicLoadEvent(&f.state)
	}
	fence.RW_SMP() // Wait has ACQUIRE semantic.
	return
}

func waitEvent(val int, deadline int64, flags []*EventFlag) (ret uint32) {
	var sum syscall.Event
	if deadline != 0 {
		sum = syscall.Alarm
	}
	for n, f := range flags {
		state := atomicInitLoadState(&f.state)
		sum |= state
		ret |= uint32(state&1) << uint(n)
	}
	sum &^= 1
	var v32 uint32
	if val&1 == 0 {
		v32 = 1<<uint(len(flags)) - 1
	}
	for {
		if ret != v32 {
			break
		}
		if deadline != 0 {
			if Nanosec() >= deadline {
				break
			}
			syscall.SetAlarm(deadline)
		}
		sum.Wait()
		ret = 0
		for n, f := range flags {
			state := syscall.AtomicLoadEvent(&f.state)
			ret |= uint32(state&1) << uint(n)
		}
	}
	fence.RW_SMP() // WaitEvent has ACQUIRE semantic.
	return
}
<commit_msg>rtos: Remove useless comments.<commit_after>// +build noos

package rtos

import (
	"sync/fence"
	"syscall"
)

func sleepUntil(end int64) {
	for Nanosec() < end {
		syscall.SetAlarm(end)
		syscall.Alarm.Wait()
	}
}

func at(t int64) <-chan int64 {
	syscall.SetAt(t)
	return syscall.TimeChan()
}

type eventFlag struct {
	state syscall.Event
}

func atomicInitLoadState(p *syscall.Event) syscall.Event {
	state := syscall.AtomicLoadEvent(p)
	if state == 0 {
		state = syscall.AssignEventFlag()
		if !syscall.AtomicCompareAndSwapEvent(p, 0, state) {
			state = syscall.AtomicLoadEvent(p)
		}
	}
	return state
}

func (f *EventFlag) reset(val int) {
	fence.RW_SMP() // Reset has RELEASE semantic.
	state := atomicInitLoadState(&f.state)
	event := state &^ 1
	new := event | syscall.Event(val&1)
	if state != new {
		syscall.AtomicStoreEvent(&f.state, new)
	}
}

func (f *EventFlag) signal(val int) {
	fence.RW_SMP() // Signal has RELEASE semantic.
	state := atomicInitLoadState(&f.state)
	event := state &^ 1
	new := event | syscall.Event(val&1)
	if state != new {
		syscall.AtomicStoreEvent(&f.state, new)
		event.Send()
	}
}

func (f *EventFlag) value() int {
	// Not need  atomicInitLoadState because an uninitialized f is zero.
	v := int(syscall.AtomicLoadEvent(&f.state) & 1)
	fence.RW_SMP()
	return v
}

func (f *EventFlag) wait(val int, deadline int64) (done bool) {
	state := atomicInitLoadState(&f.state)
	event := state &^ 1
	need := event | syscall.Event(val&1)
	if deadline != 0 {
		event |= syscall.Alarm
	}
	for {
		done = (state == need)
		if done {
			break
		}
		if deadline != 0 {
			if Nanosec() >= deadline {
				break
			}
			syscall.SetAlarm(deadline)
		}
		event.Wait()
		state = syscall.AtomicLoadEvent(&f.state)
	}
	fence.RW_SMP() // Wait has ACQUIRE semantic.
	return
}

func waitEvent(val int, deadline int64, flags []*EventFlag) (ret uint32) {
	var sum syscall.Event
	if deadline != 0 {
		sum = syscall.Alarm
	}
	for n, f := range flags {
		state := atomicInitLoadState(&f.state)
		sum |= state
		ret |= uint32(state&1) << uint(n)
	}
	sum &^= 1
	var v32 uint32
	if val&1 == 0 {
		v32 = 1<<uint(len(flags)) - 1
	}
	for {
		if ret != v32 {
			break
		}
		if deadline != 0 {
			if Nanosec() >= deadline {
				break
			}
			syscall.SetAlarm(deadline)
		}
		sum.Wait()
		ret = 0
		for n, f := range flags {
			state := syscall.AtomicLoadEvent(&f.state)
			ret |= uint32(state&1) << uint(n)
		}
	}
	fence.RW_SMP() // WaitEvent has ACQUIRE semantic.
	return
}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package elasticsearch

import (
	"errors"
	"fmt"
	"strings"

	"time"

	"github.com/venicegeo/pz-gocommon/gocommon"
)

// MappingElementTypeName is just an alias for a string.
type MappingElementTypeName string

type QueryFormat struct {
	Size  int
	From  int
	Order bool
	Key   string
}

// Constants representing the supported data types for the Event parameters.
const (
	MappingElementTypeString  MappingElementTypeName = "string"
	MappingElementTypeBool    MappingElementTypeName = "boolean"
	MappingElementTypeInteger MappingElementTypeName = "integer"
	MappingElementTypeDouble  MappingElementTypeName = "double"
	MappingElementTypeDate    MappingElementTypeName = "date"
	MappingElementTypeFloat   MappingElementTypeName = "float"
	MappingElementTypeByte    MappingElementTypeName = "byte"
	MappingElementTypeShort   MappingElementTypeName = "short"
	MappingElementTypeLong    MappingElementTypeName = "long"
)

// IIndex is an interface to Elasticsearch Index methods
type IIndex interface {
	GetVersion() string

	IndexName() string
	IndexExists() bool
	TypeExists(typ string) bool
	ItemExists(typ string, id string) bool
	Create(settings string) error
	Close() error
	Delete() error
	PostData(typ string, id string, obj interface{}) (*IndexResponse, error)
	GetByID(typ string, id string) (*GetResult, error)
	DeleteByID(typ string, id string) (*DeleteResponse, error)
	FilterByMatchAll(typ string, format *piazza.JsonPagination) (*SearchResult, error)
	GetAllElements(typ string) (*SearchResult, error)
	FilterByTermQuery(typ string, name string, value interface{}) (*SearchResult, error)
	FilterByMatchQuery(typ string, name string, value interface{}) (*SearchResult, error)
	SearchByJSON(typ string, jsn string) (*SearchResult, error)
	SetMapping(typename string, jsn piazza.JsonString) error
	GetTypes() ([]string, error)
	GetMapping(typ string) (interface{}, error)
	AddPercolationQuery(id string, query piazza.JsonString) (*IndexResponse, error)
	DeletePercolationQuery(id string) (*DeleteResponse, error)
	AddPercolationDocument(typ string, doc interface{}) (*PercolateResponse, error)
}

// NewIndexInterface constructs an IIndex
func NewIndexInterface(sys *piazza.SystemConfig, index string, settings string, mocking bool) (IIndex, error) {
	var esi IIndex
	var err error

	if mocking {
		esi = NewMockIndex(index)
		return esi, nil
	}

	esi, err = NewIndex(sys, index, settings)
	if err != nil {
		return nil, err
	}

	if esi == nil {
		return nil, errors.New("Index creation failed: returned nil")
	}

	return esi, nil
}

// ConstructMappingSchema takes a map of parameter names to datatypes and
// returns the corresponding ES DSL for it.
func ConstructMappingSchema(name string, items map[string]MappingElementTypeName) (piazza.JsonString, error) {

	const template string = `{
		"%s":{
			"properties":{
				%s
			}
		}
	}`

	stuff := make([]string, len(items))
	i := 0
	for k, v := range items {
		stuff[i] = fmt.Sprintf(`"%s": {"type":"%s"}`, k, v)
		i++
	}

	json := fmt.Sprintf(template, name, strings.Join(stuff, ","))

	return piazza.JsonString(json), nil
}

// NewQueryFormat constructs a QueryFormat
func NewQueryFormat(params *piazza.JsonPagination) *QueryFormat {

	format := &QueryFormat{
		Size:  params.PerPage,
		From:  params.Page * params.PerPage,
		Key:   params.SortBy,
		Order: params.Order == piazza.SortOrderAscending,
	}

	return format
}

type GetData func() (bool, error)

func PollFunction(fn GetData) (bool, error) {
	timeout := time.After(5 * time.Second)
	tick := time.Tick(250 * time.Millisecond)
	for {
		select {
		case <-timeout:
			return false, errors.New("timeout reached")
		case <-tick:
			ok, err := fn()
			if err != nil {
				return false, err
			} else if ok {
				return true, nil
			}
		}
	}
}
<commit_msg>Added all es data types and array formats - implement later<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package elasticsearch

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

	"github.com/venicegeo/pz-gocommon/gocommon"
)

// MappingElementTypeName is just an alias for a string.
type MappingElementTypeName string

type QueryFormat struct {
	Size  int
	From  int
	Order bool
	Key   string
}

// Constants representing the supported data types for the Event parameters.
const (
	MappingElementTypeString      MappingElementTypeName = "string"
	MappingElementTypeLong        MappingElementTypeName = "long"
	MappingElementTypeInteger     MappingElementTypeName = "integer"
	MappingElementTypeShort       MappingElementTypeName = "short"
	MappingElementTypeByte        MappingElementTypeName = "byte"
	MappingElementTypeDouble      MappingElementTypeName = "double"
	MappingElementTypeFloat       MappingElementTypeName = "float"
	MappingElementTypeDate        MappingElementTypeName = "date"
	MappingElementTypeBool        MappingElementTypeName = "boolean"
	MappingElementTypeBinary      MappingElementTypeName = "binary"
	MappingElementTypeGeoPoint    MappingElementTypeName = "geo_point"
	MappingElementTypeGeoShape    MappingElementTypeName = "geo_shape"
	MappingElementTypeIp          MappingElementTypeName = "ip"
	MappingElementTypeCompletion  MappingElementTypeName = "completion"
	MappingElementTypeStringA     MappingElementTypeName = "[string]"
	MappingElementTypeLongA       MappingElementTypeName = "[long]"
	MappingElementTypeIntegerA    MappingElementTypeName = "[integer]"
	MappingElementTypeShortA      MappingElementTypeName = "[short]"
	MappingElementTypeByteA       MappingElementTypeName = "[byte]"
	MappingElementTypeDoubleA     MappingElementTypeName = "[double]"
	MappingElementTypeFloatA      MappingElementTypeName = "[float]"
	MappingElementTypeDateA       MappingElementTypeName = "[date]"
	MappingElementTypeBoolA       MappingElementTypeName = "[boolean]"
	MappingElementTypeBinaryA     MappingElementTypeName = "[binary]"
	MappingElementTypeGeoPointA   MappingElementTypeName = "[geo_point]"
	MappingElementTypeGeoShapeA   MappingElementTypeName = "[geo_shape]"
	MappingElementTypeIpA         MappingElementTypeName = "[ip]"
	MappingElementTypeCompletionA MappingElementTypeName = "[completion]"
)

// IIndex is an interface to Elasticsearch Index methods
type IIndex interface {
	GetVersion() string

	IndexName() string
	IndexExists() bool
	TypeExists(typ string) bool
	ItemExists(typ string, id string) bool
	Create(settings string) error
	Close() error
	Delete() error
	PostData(typ string, id string, obj interface{}) (*IndexResponse, error)
	GetByID(typ string, id string) (*GetResult, error)
	DeleteByID(typ string, id string) (*DeleteResponse, error)
	FilterByMatchAll(typ string, format *piazza.JsonPagination) (*SearchResult, error)
	GetAllElements(typ string) (*SearchResult, error)
	FilterByTermQuery(typ string, name string, value interface{}) (*SearchResult, error)
	FilterByMatchQuery(typ string, name string, value interface{}) (*SearchResult, error)
	SearchByJSON(typ string, jsn string) (*SearchResult, error)
	SetMapping(typename string, jsn piazza.JsonString) error
	GetTypes() ([]string, error)
	GetMapping(typ string) (interface{}, error)
	AddPercolationQuery(id string, query piazza.JsonString) (*IndexResponse, error)
	DeletePercolationQuery(id string) (*DeleteResponse, error)
	AddPercolationDocument(typ string, doc interface{}) (*PercolateResponse, error)
}

// NewIndexInterface constructs an IIndex
func NewIndexInterface(sys *piazza.SystemConfig, index string, settings string, mocking bool) (IIndex, error) {
	var esi IIndex
	var err error

	if mocking {
		esi = NewMockIndex(index)
		return esi, nil
	}

	esi, err = NewIndex(sys, index, settings)
	if err != nil {
		return nil, err
	}

	if esi == nil {
		return nil, errors.New("Index creation failed: returned nil")
	}

	return esi, nil
}

// ConstructMappingSchema takes a map of parameter names to datatypes and
// returns the corresponding ES DSL for it.
func ConstructMappingSchema(name string, items map[string]MappingElementTypeName) (piazza.JsonString, error) {

	const template string = `{
		"%s":{
			"properties":{
				%s
			}
		}
	}`

	stuff := make([]string, len(items))
	i := 0
	for k, v := range items {
		stuff[i] = fmt.Sprintf(`"%s": {"type":"%s"}`, k, v)
		i++
	}

	json := fmt.Sprintf(template, name, strings.Join(stuff, ","))

	return piazza.JsonString(json), nil
}

// NewQueryFormat constructs a QueryFormat
func NewQueryFormat(params *piazza.JsonPagination) *QueryFormat {

	format := &QueryFormat{
		Size:  params.PerPage,
		From:  params.Page * params.PerPage,
		Key:   params.SortBy,
		Order: params.Order == piazza.SortOrderAscending,
	}

	return format
}

type GetData func() (bool, error)

func PollFunction(fn GetData) (bool, error) {
	timeout := time.After(5 * time.Second)
	tick := time.Tick(250 * time.Millisecond)
	for {
		select {
		case <-timeout:
			return false, errors.New("timeout reached")
		case <-tick:
			ok, err := fn()
			if err != nil {
				return false, err
			} else if ok {
				return true, nil
			}
		}
	}
}

func IsValidMappingType(mappingValue string) bool {
	if string(MappingElementTypeString) == mappingValue ||
		string(MappingElementTypeLong) == mappingValue ||
		string(MappingElementTypeInteger) == mappingValue ||
		string(MappingElementTypeShort) == mappingValue ||
		string(MappingElementTypeByte) == mappingValue ||
		string(MappingElementTypeDouble) == mappingValue ||
		string(MappingElementTypeFloat) == mappingValue ||
		string(MappingElementTypeDate) == mappingValue ||
		string(MappingElementTypeBool) == mappingValue ||
		string(MappingElementTypeBinary) == mappingValue ||
		string(MappingElementTypeGeoPoint) == mappingValue ||
		string(MappingElementTypeGeoShape) == mappingValue ||
		string(MappingElementTypeIp) == mappingValue ||
		string(MappingElementTypeCompletion) == mappingValue ||
		string(MappingElementTypeStringA) == mappingValue ||
		string(MappingElementTypeLongA) == mappingValue ||
		string(MappingElementTypeIntegerA) == mappingValue ||
		string(MappingElementTypeShortA) == mappingValue ||
		string(MappingElementTypeByteA) == mappingValue ||
		string(MappingElementTypeDoubleA) == mappingValue ||
		string(MappingElementTypeFloatA) == mappingValue ||
		string(MappingElementTypeDateA) == mappingValue ||
		string(MappingElementTypeBoolA) == mappingValue ||
		string(MappingElementTypeBinaryA) == mappingValue ||
		string(MappingElementTypeGeoPointA) == mappingValue ||
		string(MappingElementTypeGeoShapeA) == mappingValue ||
		string(MappingElementTypeIpA) == mappingValue ||
		string(MappingElementTypeCompletionA) == mappingValue {
		return true
	}
	return false
}
func ValueIsValidArray(value string) bool {
	openCount, closedCount := 0, 0
	for i := 0; i < len(value); i++ {
		char := CharAt(value, i)
		if char == "[" {
			openCount++
		} else if char == "]" {
			closedCount++
		}
	}
	if openCount != 1 || closedCount != 1 {
		return false
	}
	if strings.HasPrefix(value, "]") && (strings.HasSuffix(value, "]") || strings.HasSuffix(value, "],")) {
		return true
	}
	return false
}
func CharAt(str string, index int) string {
	return str[index : index+1]
}

func RemoveWhitespace(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

func InsertString(str, insert string, index int) string {
	return str[:index] + insert + str[index:]
}
<|endoftext|>


<commit_before>package vips

import (
	"bytes"
	"io"
	"mime/multipart"

	"github.com/qor/qor/media_library"

	"gopkg.in/h2non/bimg.v0"
)

type bimgImageHandler struct{}

func (bimgImageHandler) CouldHandle(media media_library.MediaLibrary) bool {
	return media.IsImage()
}

func (bimgImageHandler) Handle(media media_library.MediaLibrary, file multipart.File, option *media_library.Option) error {
	// Save Original Image
	if err := media.Store(media.URL("original"), option, file); err == nil {
		file.Seek(0, 0)

		// Crop & Resize
		var buffer bytes.Buffer
		if _, err := io.Copy(&buffer, file); err != nil {
			return err
		}

		img := bimg.NewImage(buffer.Bytes())

		// Handle original image
		{
			bimgOption := bimg.Options{Interlace: true}

			// Crop original image if specified
			if cropOption := media.GetCropOption("original"); cropOption != nil {
				bimgOption.Top = cropOption.Min.Y
				bimgOption.Left = cropOption.Min.X
				bimgOption.AreaWidth = cropOption.Max.X - cropOption.Min.X
				bimgOption.AreaHeight = cropOption.Max.Y - cropOption.Min.Y
			}

			// Process & Save original image
			if buf, err := img.Process(bimgOption); err == nil {
				media.Store(media.URL(), option, bytes.NewReader(buf))
			} else {
				return err
			}
		}

		// Handle size images
		for key, size := range media.GetSizes() {
			bimgOption := bimg.Options{
				Interlace: true,
				Enlarge:   true,
				Width:     size.Width,
				Height:    size.Height,
			}

			if cropOption := media.GetCropOption(key); cropOption != nil {
				bimgOption.Top = cropOption.Min.Y
				bimgOption.Left = cropOption.Min.X
				bimgOption.AreaWidth = cropOption.Max.X - cropOption.Min.X
				bimgOption.AreaHeight = cropOption.Max.Y - cropOption.Min.Y
				bimgOption.Crop = true
			}

			// Process & Save size image
			if buf, err := img.Process(bimgOption); err == nil {
				media.Store(media.URL(key), option, bytes.NewReader(buf))
			} else {
				return err
			}
		}
		return nil
	} else {
		return err
	}
}

func init() {
	media_library.RegisterMediaLibraryHandler("image_handler", bimgImageHandler{})
}
<commit_msg>[vips] crop then resize images for sizes<commit_after>package vips

import (
	"bytes"
	"io"
	"mime/multipart"

	"github.com/qor/qor/media_library"

	"gopkg.in/h2non/bimg.v0"
)

type bimgImageHandler struct{}

func (bimgImageHandler) CouldHandle(media media_library.MediaLibrary) bool {
	return media.IsImage()
}

func (bimgImageHandler) Handle(media media_library.MediaLibrary, file multipart.File, option *media_library.Option) error {
	// Save Original Image
	if err := media.Store(media.URL("original"), option, file); err == nil {
		file.Seek(0, 0)

		// Crop & Resize
		var buffer bytes.Buffer
		if _, err := io.Copy(&buffer, file); err != nil {
			return err
		}

		img := bimg.NewImage(buffer.Bytes())

		// Handle original image
		{
			bimgOption := bimg.Options{Interlace: true}

			// Crop original image if specified
			if cropOption := media.GetCropOption("original"); cropOption != nil {
				bimgOption.Top = cropOption.Min.Y
				bimgOption.Left = cropOption.Min.X
				bimgOption.AreaWidth = cropOption.Max.X - cropOption.Min.X
				bimgOption.AreaHeight = cropOption.Max.Y - cropOption.Min.Y
			}

			// Process & Save original image
			if buf, err := img.Process(bimgOption); err == nil {
				media.Store(media.URL(), option, bytes.NewReader(buf))
			} else {
				return err
			}
		}

		// Handle size images
		for key, size := range media.GetSizes() {
			img := bimg.NewImage(buffer.Bytes())

			bimgOption := bimg.Options{
				Interlace: true,
			}

			if cropOption := media.GetCropOption(key); cropOption != nil {
				bimgOption.Top = cropOption.Min.Y
				bimgOption.Left = cropOption.Min.X
				bimgOption.AreaWidth = cropOption.Max.X - cropOption.Min.X
				bimgOption.AreaHeight = cropOption.Max.Y - cropOption.Min.Y
				bimgOption.Crop = true
				bimgOption.Force = true
			}

			// Process & Save size image
			if _, err := img.Process(bimgOption); err == nil {
				if buf, err := img.Process(bimg.Options{
					Width:   size.Width,
					Height:  size.Height,
					Crop:    true,
					Enlarge: true,
					Force:   true,
				}); err == nil {
					media.Store(media.URL(key), option, bytes.NewReader(buf))
				} else {
					return err
				}
			} else {
				return err
			}
		}
		return nil
	} else {
		return err
	}
}

func init() {
	media_library.RegisterMediaLibraryHandler("image_handler", bimgImageHandler{})
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 *
 */

package openid

import (
	"context"
	"fmt"
	"strconv"
	"time"

	jwtgo "github.com/dgrijalva/jwt-go"
	"github.com/mohae/deepcopy"
	"github.com/pkg/errors"

	"github.com/ory/fosite"
	"github.com/ory/fosite/token/jwt"
	"github.com/ory/go-convenience/stringslice"
)

const defaultExpiryTime = time.Hour

type Session interface {
	IDTokenClaims() *jwt.IDTokenClaims
	IDTokenHeaders() *jwt.Headers

	fosite.Session
}

// IDTokenSession is a session container for the id token
type DefaultSession struct {
	Claims    *jwt.IDTokenClaims
	Headers   *jwt.Headers
	ExpiresAt map[fosite.TokenType]time.Time
	Username  string
	Subject   string
}

func NewDefaultSession() *DefaultSession {
	return &DefaultSession{
		Claims: &jwt.IDTokenClaims{
			RequestedAt: time.Now().UTC(),
		},
		Headers: &jwt.Headers{},
	}
}

func (s *DefaultSession) Clone() fosite.Session {
	if s == nil {
		return nil
	}

	return deepcopy.Copy(s).(fosite.Session)
}

func (s *DefaultSession) SetExpiresAt(key fosite.TokenType, exp time.Time) {
	if s.ExpiresAt == nil {
		s.ExpiresAt = make(map[fosite.TokenType]time.Time)
	}
	s.ExpiresAt[key] = exp
}

func (s *DefaultSession) GetExpiresAt(key fosite.TokenType) time.Time {
	if s.ExpiresAt == nil {
		s.ExpiresAt = make(map[fosite.TokenType]time.Time)
	}

	if _, ok := s.ExpiresAt[key]; !ok {
		return time.Time{}
	}
	return s.ExpiresAt[key]
}

func (s *DefaultSession) GetUsername() string {
	if s == nil {
		return ""
	}
	return s.Username
}

func (s *DefaultSession) GetSubject() string {
	if s == nil {
		return ""
	}

	return s.Subject
}

func (s *DefaultSession) IDTokenHeaders() *jwt.Headers {
	if s.Headers == nil {
		s.Headers = &jwt.Headers{}
	}
	return s.Headers
}

func (s *DefaultSession) IDTokenClaims() *jwt.IDTokenClaims {
	if s.Claims == nil {
		s.Claims = &jwt.IDTokenClaims{}
	}
	return s.Claims
}

type DefaultStrategy struct {
	jwt.JWTStrategy

	Expiry time.Duration
	Issuer string

	MinParameterEntropy int
}

func (h DefaultStrategy) GenerateIDToken(ctx context.Context, requester fosite.Requester) (token string, err error) {
	if h.Expiry == 0 {
		h.Expiry = defaultExpiryTime
	}

	sess, ok := requester.GetSession().(Session)
	if !ok {
		return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because session must be of type fosite/handler/openid.Session."))
	}

	claims := sess.IDTokenClaims()
	if claims.Subject == "" {
		return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because subject is an empty string."))
	}

	if requester.GetRequestForm().Get("grant_type") != "refresh_token" {
		maxAge, err := strconv.ParseInt(requester.GetRequestForm().Get("max_age"), 10, 64)
		if err != nil {
			maxAge = 0
		}

		// Adds a bit of wiggle room for timing issues
		if claims.AuthTime.After(time.Now().UTC().Add(time.Second * 5)) {
			return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to validate OpenID Connect request because authentication time is in the future."))
		}

		if maxAge > 0 {
			if claims.AuthTime.IsZero() {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because authentication time claim is required when max_age is set."))
			} else if claims.RequestedAt.IsZero() {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because requested at claim is required when max_age is set."))
			} else if claims.AuthTime.Add(time.Second * time.Duration(maxAge)).Before(claims.RequestedAt) {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because authentication time does not satisfy max_age time."))
			}
		}

		prompt := requester.GetRequestForm().Get("prompt")
		if prompt != "" {
			if claims.AuthTime.IsZero() {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Unable to determine validity of prompt parameter because auth_time is missing in id token claims."))
			}
		}

		switch prompt {
		case "none":
			if claims.AuthTime.After(claims.RequestedAt) {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because prompt was set to \"none\" but auth_time happened after the authorization request was registered, indicating that the user was logged in during this request which is not allowed."))
			}
		case "login":
			if claims.AuthTime.Before(claims.RequestedAt) {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because prompt was set to \"login\" but auth_time happened before the authorization request was registered, indicating that the user was not re-authenticated which is forbidden."))
			}
		}

		// If acr_values was requested but no acr value was provided in the ID token, fall back to level 0 which means least
		// confidence in authentication.
		if requester.GetRequestForm().Get("acr_values") != "" && claims.AuthenticationContextClassReference == "" {
			claims.AuthenticationContextClassReference = "0"
		}

		if tokenHintString := requester.GetRequestForm().Get("id_token_hint"); tokenHintString != "" {
			tokenHint, err := h.JWTStrategy.Decode(ctx, tokenHintString)
			var ve *jwtgo.ValidationError
			if errors.As(err, &ve) && ve.Errors == jwtgo.ValidationErrorExpired {
				// Expired ID Tokens are allowed as values to id_token_hint
			} else if err != nil {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug(fmt.Sprintf("Unable to decode id token from id_token_hint parameter because %s.", err.Error())))
			}

			if hintClaims, ok := tokenHint.Claims.(jwtgo.MapClaims); !ok {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Unable to decode id token from id_token_hint to *jwt.StandardClaims."))
			} else if hintSub, _ := hintClaims["sub"].(string); hintSub == "" {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Provided id token from id_token_hint does not have a subject."))
			} else if hintSub != claims.Subject {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug(fmt.Sprintf("Subject from authorization mismatches id token subject from id_token_hint.")))
			}
		}
	}

	if claims.ExpiresAt.IsZero() {
		claims.ExpiresAt = time.Now().UTC().Add(h.Expiry)
	}

	if claims.ExpiresAt.Before(time.Now().UTC()) {
		return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because expiry claim can not be in the past."))
	}

	if claims.AuthTime.IsZero() {
		claims.AuthTime = time.Now().UTC()
	}

	if claims.Issuer == "" {
		claims.Issuer = h.Issuer
	}

	nonce := requester.GetRequestForm().Get("nonce")
	// OPTIONAL. String value used to associate a Client session with an ID Token, and to mitigate replay attacks.
	if len(nonce) == 0 {
	} else if len(nonce) > 0 && len(nonce) < h.MinParameterEntropy {
		// We're assuming that using less then, by default, 8 characters for the state can not be considered "unguessable"
		return "", errors.WithStack(fosite.ErrInsufficientEntropy.WithHintf("Parameter \"nonce\" is set but does not satisfy the minimum entropy of %d characters.", h.MinParameterEntropy))
	}

	claims.Nonce = nonce
	claims.Audience = stringslice.Unique(append(claims.Audience, requester.GetClient().GetID()))
	claims.IssuedAt = time.Now().UTC()

	token, _, err = h.JWTStrategy.Generate(ctx, claims.ToMapClaims(), sess.IDTokenHeaders())
	return token, err
}
<commit_msg>docs: document Session interface methods (#512)<commit_after>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 *
 */

package openid

import (
	"context"
	"fmt"
	"strconv"
	"time"

	jwtgo "github.com/dgrijalva/jwt-go"
	"github.com/mohae/deepcopy"
	"github.com/pkg/errors"

	"github.com/ory/fosite"
	"github.com/ory/fosite/token/jwt"
	"github.com/ory/go-convenience/stringslice"
)

const defaultExpiryTime = time.Hour

type Session interface {
	// IDTokenClaims returns a pointer to claims which will be modified in-place by handlers.
	// Session should store this pointer and return always the same pointer.
	IDTokenClaims() *jwt.IDTokenClaims
	// IDTokenHeaders returns a pointer to header values which will be modified in-place by handlers.
	// Session should store this pointer and return always the same pointer.
	IDTokenHeaders() *jwt.Headers

	fosite.Session
}

// IDTokenSession is a session container for the id token
type DefaultSession struct {
	Claims    *jwt.IDTokenClaims
	Headers   *jwt.Headers
	ExpiresAt map[fosite.TokenType]time.Time
	Username  string
	Subject   string
}

func NewDefaultSession() *DefaultSession {
	return &DefaultSession{
		Claims: &jwt.IDTokenClaims{
			RequestedAt: time.Now().UTC(),
		},
		Headers: &jwt.Headers{},
	}
}

func (s *DefaultSession) Clone() fosite.Session {
	if s == nil {
		return nil
	}

	return deepcopy.Copy(s).(fosite.Session)
}

func (s *DefaultSession) SetExpiresAt(key fosite.TokenType, exp time.Time) {
	if s.ExpiresAt == nil {
		s.ExpiresAt = make(map[fosite.TokenType]time.Time)
	}
	s.ExpiresAt[key] = exp
}

func (s *DefaultSession) GetExpiresAt(key fosite.TokenType) time.Time {
	if s.ExpiresAt == nil {
		s.ExpiresAt = make(map[fosite.TokenType]time.Time)
	}

	if _, ok := s.ExpiresAt[key]; !ok {
		return time.Time{}
	}
	return s.ExpiresAt[key]
}

func (s *DefaultSession) GetUsername() string {
	if s == nil {
		return ""
	}
	return s.Username
}

func (s *DefaultSession) GetSubject() string {
	if s == nil {
		return ""
	}

	return s.Subject
}

func (s *DefaultSession) IDTokenHeaders() *jwt.Headers {
	if s.Headers == nil {
		s.Headers = &jwt.Headers{}
	}
	return s.Headers
}

func (s *DefaultSession) IDTokenClaims() *jwt.IDTokenClaims {
	if s.Claims == nil {
		s.Claims = &jwt.IDTokenClaims{}
	}
	return s.Claims
}

type DefaultStrategy struct {
	jwt.JWTStrategy

	Expiry time.Duration
	Issuer string

	MinParameterEntropy int
}

func (h DefaultStrategy) GenerateIDToken(ctx context.Context, requester fosite.Requester) (token string, err error) {
	if h.Expiry == 0 {
		h.Expiry = defaultExpiryTime
	}

	sess, ok := requester.GetSession().(Session)
	if !ok {
		return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because session must be of type fosite/handler/openid.Session."))
	}

	claims := sess.IDTokenClaims()
	if claims.Subject == "" {
		return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because subject is an empty string."))
	}

	if requester.GetRequestForm().Get("grant_type") != "refresh_token" {
		maxAge, err := strconv.ParseInt(requester.GetRequestForm().Get("max_age"), 10, 64)
		if err != nil {
			maxAge = 0
		}

		// Adds a bit of wiggle room for timing issues
		if claims.AuthTime.After(time.Now().UTC().Add(time.Second * 5)) {
			return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to validate OpenID Connect request because authentication time is in the future."))
		}

		if maxAge > 0 {
			if claims.AuthTime.IsZero() {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because authentication time claim is required when max_age is set."))
			} else if claims.RequestedAt.IsZero() {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because requested at claim is required when max_age is set."))
			} else if claims.AuthTime.Add(time.Second * time.Duration(maxAge)).Before(claims.RequestedAt) {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because authentication time does not satisfy max_age time."))
			}
		}

		prompt := requester.GetRequestForm().Get("prompt")
		if prompt != "" {
			if claims.AuthTime.IsZero() {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Unable to determine validity of prompt parameter because auth_time is missing in id token claims."))
			}
		}

		switch prompt {
		case "none":
			if claims.AuthTime.After(claims.RequestedAt) {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because prompt was set to \"none\" but auth_time happened after the authorization request was registered, indicating that the user was logged in during this request which is not allowed."))
			}
		case "login":
			if claims.AuthTime.Before(claims.RequestedAt) {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because prompt was set to \"login\" but auth_time happened before the authorization request was registered, indicating that the user was not re-authenticated which is forbidden."))
			}
		}

		// If acr_values was requested but no acr value was provided in the ID token, fall back to level 0 which means least
		// confidence in authentication.
		if requester.GetRequestForm().Get("acr_values") != "" && claims.AuthenticationContextClassReference == "" {
			claims.AuthenticationContextClassReference = "0"
		}

		if tokenHintString := requester.GetRequestForm().Get("id_token_hint"); tokenHintString != "" {
			tokenHint, err := h.JWTStrategy.Decode(ctx, tokenHintString)
			var ve *jwtgo.ValidationError
			if errors.As(err, &ve) && ve.Errors == jwtgo.ValidationErrorExpired {
				// Expired ID Tokens are allowed as values to id_token_hint
			} else if err != nil {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug(fmt.Sprintf("Unable to decode id token from id_token_hint parameter because %s.", err.Error())))
			}

			if hintClaims, ok := tokenHint.Claims.(jwtgo.MapClaims); !ok {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Unable to decode id token from id_token_hint to *jwt.StandardClaims."))
			} else if hintSub, _ := hintClaims["sub"].(string); hintSub == "" {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug("Provided id token from id_token_hint does not have a subject."))
			} else if hintSub != claims.Subject {
				return "", errors.WithStack(fosite.ErrServerError.WithDebug(fmt.Sprintf("Subject from authorization mismatches id token subject from id_token_hint.")))
			}
		}
	}

	if claims.ExpiresAt.IsZero() {
		claims.ExpiresAt = time.Now().UTC().Add(h.Expiry)
	}

	if claims.ExpiresAt.Before(time.Now().UTC()) {
		return "", errors.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because expiry claim can not be in the past."))
	}

	if claims.AuthTime.IsZero() {
		claims.AuthTime = time.Now().UTC()
	}

	if claims.Issuer == "" {
		claims.Issuer = h.Issuer
	}

	nonce := requester.GetRequestForm().Get("nonce")
	// OPTIONAL. String value used to associate a Client session with an ID Token, and to mitigate replay attacks.
	if len(nonce) == 0 {
	} else if len(nonce) > 0 && len(nonce) < h.MinParameterEntropy {
		// We're assuming that using less then, by default, 8 characters for the state can not be considered "unguessable"
		return "", errors.WithStack(fosite.ErrInsufficientEntropy.WithHintf("Parameter \"nonce\" is set but does not satisfy the minimum entropy of %d characters.", h.MinParameterEntropy))
	}

	claims.Nonce = nonce
	claims.Audience = stringslice.Unique(append(claims.Audience, requester.GetClient().GetID()))
	claims.IssuedAt = time.Now().UTC()

	token, _, err = h.JWTStrategy.Generate(ctx, claims.ToMapClaims(), sess.IDTokenHeaders())
	return token, err
}
<|endoftext|>


<commit_before>package people

import (
	"encoding/json"
	"net/http"

	"fmt"
	fthealth "github.com/Financial-Times/go-fthealth/v1_1"
	"github.com/Financial-Times/service-status-go/gtg"
	"github.com/Financial-Times/transactionid-utils-go"
	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"
	"regexp"
	"strings"
)

const (
	urlPrefix = "http://api.ft.com/things/"
	validUUID = "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
)

// PeopleDriver for cypher queries
var PeopleDriver Driver
var CacheControlHeader string

//var maxAge = 24 * time.Hour

// HealthCheck does something
func HealthCheck() fthealth.Check {
	return fthealth.Check{
		BusinessImpact: "Unable to respond to Public People api requests",
		Name:           "Check connectivity to Neo4j - neoUrl is a parameter in hieradata for this service",
		PanicGuide:     "https://sites.google.com/a/ft.com/ft-technology-service-transition/home/run-book-library/public-people-api",
		Severity:       2,
		TechnicalSummary: `Cannot connect to Neo4j. If this check fails, check that Neo4j instance is up and running. You can find
				the neoUrl as a parameter in hieradata for this service. `,
		Checker: Checker,
	}
}

// Checker does more stuff
func Checker() (string, error) {
	err := PeopleDriver.CheckConnectivity()
	if err == nil {
		return "Connectivity to neo4j is ok", err
	}
	return "Error connecting to neo4j", err
}

func GTG() gtg.Status {
	statusCheck := func() gtg.Status {
		return gtgCheck(Checker)
	}

	return gtg.FailFastParallelCheck([]gtg.StatusChecker{statusCheck})()
}

func gtgCheck(handler func() (string, error)) gtg.Status {
	if _, err := handler(); err != nil {
		return gtg.Status{GoodToGo: false, Message: err.Error()}
	}
	return gtg.Status{GoodToGo: true}
}

// MethodNotAllowedHandler handles 405
func MethodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusMethodNotAllowed)
	return
}

// GetPerson is the public API
func GetPerson(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	requestId := vars["uuid"]
	transId := transactionidutils.GetTransactionIDFromRequest(r)
	w.Header().Set("X-Request-Id", transId)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	validRegexp := regexp.MustCompile(validUUID)
	if requestId == "" || !validRegexp.MatchString(requestId) {
		msg := fmt.Sprintf("Invalid request id %s", requestId)
		log.WithFields(log.Fields{"UUID": requestId, "transaction_id": transId}).Error(msg)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`"{\"message\":\"` + msg + `\"}"`))
		return
	}

	person, found, err := PeopleDriver.Read(requestId, transId)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`"{\"message\": \"Person could not be retrieved\"}"`))
		return
	}
	if !found {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(`"{\"message\":\"Person ` + requestId + ` not found in DB\"}"`))
		return
	}

	canonicalId := strings.TrimPrefix(person.ID, urlPrefix)
	if strings.Compare(canonicalId, requestId) != 0 {
		log.WithFields(log.Fields{"UUID": requestId}).Info("Person " + requestId + " is concorded to " + canonicalId + "; serving redirect")
		redirectURL := strings.Replace(r.URL.String(), requestId, canonicalId, 1)
		w.Header().Set("Location", redirectURL)
		w.WriteHeader(http.StatusMovedPermanently)
		w.Write([]byte(`"{\"message\":\"Person ` + requestId + ` is concorded, redirecting...\"}"`))
		return
	}

	w.Header().Set("Cache-Control", CacheControlHeader)
	w.WriteHeader(http.StatusOK)

	if err = json.NewEncoder(w).Encode(person); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`"{\"message\":\"Person could not be retrieved\"}"`))
	}
}
<commit_msg>Updated the healthcheck information<commit_after>package people

import (
	"encoding/json"
	"net/http"

	"fmt"
	fthealth "github.com/Financial-Times/go-fthealth/v1_1"
	"github.com/Financial-Times/service-status-go/gtg"
	"github.com/Financial-Times/transactionid-utils-go"
	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"
	"regexp"
	"strings"
)

const (
	urlPrefix = "http://api.ft.com/things/"
	validUUID = "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
)

// PeopleDriver for cypher queries
var PeopleDriver Driver
var CacheControlHeader string

//var maxAge = 24 * time.Hour

// HealthCheck does something
func HealthCheck() fthealth.Check {
	return fthealth.Check{
		BusinessImpact: "Unable to respond to Public People API requests",
		Name:           "Check connectivity to Neo4j",
		PanicGuide:     "https://dewey.in.ft.com/view/system/public-people-api",
		Severity:       2,
		TechnicalSummary: `Cannot connect to Neo4j. If this check fails, check that the Neo4J cluster is responding.  `,
		Checker: Checker,
	}
}

// Checker does more stuff
func Checker() (string, error) {
	err := PeopleDriver.CheckConnectivity()
	if err == nil {
		return "Connectivity to neo4j is ok", err
	}
	return "Error connecting to neo4j", err
}

func GTG() gtg.Status {
	statusCheck := func() gtg.Status {
		return gtgCheck(Checker)
	}

	return gtg.FailFastParallelCheck([]gtg.StatusChecker{statusCheck})()
}

func gtgCheck(handler func() (string, error)) gtg.Status {
	if _, err := handler(); err != nil {
		return gtg.Status{GoodToGo: false, Message: err.Error()}
	}
	return gtg.Status{GoodToGo: true}
}

// MethodNotAllowedHandler handles 405
func MethodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusMethodNotAllowed)
	return
}

// GetPerson is the public API
func GetPerson(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	requestId := vars["uuid"]
	transId := transactionidutils.GetTransactionIDFromRequest(r)
	w.Header().Set("X-Request-Id", transId)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	validRegexp := regexp.MustCompile(validUUID)
	if requestId == "" || !validRegexp.MatchString(requestId) {
		msg := fmt.Sprintf("Invalid request id %s", requestId)
		log.WithFields(log.Fields{"UUID": requestId, "transaction_id": transId}).Error(msg)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`"{\"message\":\"` + msg + `\"}"`))
		return
	}

	person, found, err := PeopleDriver.Read(requestId, transId)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`"{\"message\": \"Person could not be retrieved\"}"`))
		return
	}
	if !found {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(`"{\"message\":\"Person ` + requestId + ` not found in DB\"}"`))
		return
	}

	canonicalId := strings.TrimPrefix(person.ID, urlPrefix)
	if strings.Compare(canonicalId, requestId) != 0 {
		log.WithFields(log.Fields{"UUID": requestId}).Info("Person " + requestId + " is concorded to " + canonicalId + "; serving redirect")
		redirectURL := strings.Replace(r.URL.String(), requestId, canonicalId, 1)
		w.Header().Set("Location", redirectURL)
		w.WriteHeader(http.StatusMovedPermanently)
		w.Write([]byte(`"{\"message\":\"Person ` + requestId + ` is concorded, redirecting...\"}"`))
		return
	}

	w.Header().Set("Cache-Control", CacheControlHeader)
	w.WriteHeader(http.StatusOK)

	if err = json.NewEncoder(w).Encode(person); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`"{\"message\":\"Person could not be retrieved\"}"`))
	}
}
<|endoftext|>


<commit_before>package resource

import (
	"fmt"
	"log"
	"reflect"
	"strings"

	"github.com/davecgh/go-spew/spew"
	tfjson "github.com/hashicorp/terraform-json"
	tftest "github.com/hashicorp/terraform-plugin-test/v2"
	testing "github.com/mitchellh/go-testing-interface"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func runPostTestDestroy(t testing.T, c TestCase, wd *tftest.WorkingDir, factories map[string]func() (*schema.Provider, error)) error {
	t.Helper()

	err := runProviderCommand(t, func() error {
		wd.RequireDestroy(t)
		return nil
	}, wd, factories)
	if err != nil {
		return err
	}

	if c.CheckDestroy != nil {
		var statePostDestroy *terraform.State
		err := runProviderCommand(t, func() error {
			statePostDestroy = getState(t, wd)
			return nil
		}, wd, factories)
		if err != nil {
			return err
		}

		if err := c.CheckDestroy(statePostDestroy); err != nil {
			return err
		}
	}

	return nil
}

func runNewTest(t testing.T, c TestCase, helper *tftest.Helper) {
	t.Helper()

	spewConf := spew.NewDefaultConfig()
	spewConf.SortKeys = true
	wd := helper.RequireNewWorkingDir(t)

	defer func() {
		var statePreDestroy *terraform.State
		err := runProviderCommand(t, func() error {
			statePreDestroy = getState(t, wd)
			return nil
		}, wd, c.ProviderFactories)
		if err != nil {
			t.Fatalf("Error retrieving state, there may be dangling resources: %s", err.Error())
			return
		}

		if !stateIsEmpty(statePreDestroy) {
			runPostTestDestroy(t, c, wd, c.ProviderFactories)
		}

		wd.Close()
	}()

	providerCfg, err := testProviderConfig(c)
	if err != nil {
		t.Fatal(err)
	}

	wd.RequireSetConfig(t, providerCfg)
	err = runProviderCommand(t, func() error {
		wd.RequireInit(t)
		return nil
	}, wd, c.ProviderFactories)
	if err != nil {
		t.Fatalf("Error running init: %s", err.Error())
		return
	}

	// use this to track last step succesfully applied
	// acts as default for import tests
	var appliedCfg string

	for i, step := range c.Steps {
		if step.PreConfig != nil {
			step.PreConfig()
		}

		if step.SkipFunc != nil {
			skip, err := step.SkipFunc()
			if err != nil {
				t.Fatal(err)
			}
			if skip {
				log.Printf("[WARN] Skipping step %d", i)
				continue
			}
		}

		if step.ImportState {
			err := testStepNewImportState(t, c, helper, wd, step, appliedCfg)
			if err != nil {
				t.Fatal(err)
			}
			continue
		}

		if step.Config != "" {
			err := testStepNewConfig(t, c, wd, step)
			if step.ExpectError != nil {
				if err == nil {
					t.Fatal("Expected an error but got none")
				}
				if !step.ExpectError.MatchString(err.Error()) {
					t.Fatalf("Expected an error with pattern, no match on: %s", err)
				}
			} else {
				if err != nil {
					t.Fatal(err)
				}
			}
			appliedCfg = step.Config
			continue
		}

		t.Fatal("Unsupported test mode")
	}
}

func getState(t testing.T, wd *tftest.WorkingDir) *terraform.State {
	t.Helper()

	jsonState := wd.RequireState(t)
	state, err := shimStateFromJson(jsonState)
	if err != nil {
		t.Fatal(err)
	}
	return state
}

func stateIsEmpty(state *terraform.State) bool {
	return state.Empty() || !state.HasResources()
}

func planIsEmpty(plan *tfjson.Plan) bool {
	for _, rc := range plan.ResourceChanges {
		if rc.Mode == tfjson.DataResourceMode {
			// Skip data sources as the current implementation ignores
			// existing state and they are all re-read every time
			continue
		}

		for _, a := range rc.Change.Actions {
			if a != tfjson.ActionNoop {
				return false
			}
		}
	}
	return true
}

func testIDRefresh(c TestCase, t testing.T, wd *tftest.WorkingDir, step TestStep, r *terraform.ResourceState) error {
	t.Helper()

	spewConf := spew.NewDefaultConfig()
	spewConf.SortKeys = true

	// Build the state. The state is just the resource with an ID. There
	// are no attributes. We only set what is needed to perform a refresh.
	state := terraform.NewState()
	state.RootModule().Resources = make(map[string]*terraform.ResourceState)
	state.RootModule().Resources[c.IDRefreshName] = &terraform.ResourceState{}

	// Temporarily set the config to a minimal provider config for the refresh
	// test. After the refresh we can reset it.
	cfg, err := testProviderConfig(c)
	if err != nil {
		return err
	}
	wd.RequireSetConfig(t, cfg)
	defer wd.RequireSetConfig(t, step.Config)

	// Refresh!
	err = runProviderCommand(t, func() error {
		wd.RequireRefresh(t)
		state = getState(t, wd)
		return nil
	}, wd, c.ProviderFactories)
	if err != nil {
		return err
	}

	// Verify attribute equivalence.
	actualR := state.RootModule().Resources[c.IDRefreshName]
	if actualR == nil {
		return fmt.Errorf("Resource gone!")
	}
	if actualR.Primary == nil {
		return fmt.Errorf("Resource has no primary instance")
	}
	actual := actualR.Primary.Attributes
	expected := r.Primary.Attributes
	// Remove fields we're ignoring
	for _, v := range c.IDRefreshIgnore {
		for k := range actual {
			if strings.HasPrefix(k, v) {
				delete(actual, k)
			}
		}
		for k := range expected {
			if strings.HasPrefix(k, v) {
				delete(expected, k)
			}
		}
	}

	if !reflect.DeepEqual(actual, expected) {
		// Determine only the different attributes
		for k, v := range expected {
			if av, ok := actual[k]; ok && v == av {
				delete(expected, k)
				delete(actual, k)
			}
		}

		spewConf := spew.NewDefaultConfig()
		spewConf.SortKeys = true
		return fmt.Errorf(
			"Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
				"\n\n%s\n\n%s",
			spewConf.Sdump(actual), spewConf.Sdump(expected))
	}

	return nil
}
<commit_msg>Restore step number in err msg<commit_after>package resource

import (
	"fmt"
	"log"
	"reflect"
	"strings"

	"github.com/davecgh/go-spew/spew"
	tfjson "github.com/hashicorp/terraform-json"
	tftest "github.com/hashicorp/terraform-plugin-test/v2"
	testing "github.com/mitchellh/go-testing-interface"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func runPostTestDestroy(t testing.T, c TestCase, wd *tftest.WorkingDir, factories map[string]func() (*schema.Provider, error)) error {
	t.Helper()

	err := runProviderCommand(t, func() error {
		wd.RequireDestroy(t)
		return nil
	}, wd, factories)
	if err != nil {
		return err
	}

	if c.CheckDestroy != nil {
		var statePostDestroy *terraform.State
		err := runProviderCommand(t, func() error {
			statePostDestroy = getState(t, wd)
			return nil
		}, wd, factories)
		if err != nil {
			return err
		}

		if err := c.CheckDestroy(statePostDestroy); err != nil {
			return err
		}
	}

	return nil
}

func runNewTest(t testing.T, c TestCase, helper *tftest.Helper) {
	t.Helper()

	spewConf := spew.NewDefaultConfig()
	spewConf.SortKeys = true
	wd := helper.RequireNewWorkingDir(t)

	defer func() {
		var statePreDestroy *terraform.State
		err := runProviderCommand(t, func() error {
			statePreDestroy = getState(t, wd)
			return nil
		}, wd, c.ProviderFactories)
		if err != nil {
			t.Fatalf("Error retrieving state, there may be dangling resources: %s", err.Error())
			return
		}

		if !stateIsEmpty(statePreDestroy) {
			runPostTestDestroy(t, c, wd, c.ProviderFactories)
		}

		wd.Close()
	}()

	providerCfg, err := testProviderConfig(c)
	if err != nil {
		t.Fatal(err)
	}

	wd.RequireSetConfig(t, providerCfg)
	err = runProviderCommand(t, func() error {
		wd.RequireInit(t)
		return nil
	}, wd, c.ProviderFactories)
	if err != nil {
		t.Fatalf("Error running init: %s", err.Error())
		return
	}

	// use this to track last step succesfully applied
	// acts as default for import tests
	var appliedCfg string

	for i, step := range c.Steps {
		if step.PreConfig != nil {
			step.PreConfig()
		}

		if step.SkipFunc != nil {
			skip, err := step.SkipFunc()
			if err != nil {
				t.Fatal(err)
			}
			if skip {
				log.Printf("[WARN] Skipping step %d", i)
				continue
			}
		}

		if step.ImportState {
			err := testStepNewImportState(t, c, helper, wd, step, appliedCfg)
			if err != nil {
				t.Fatal(err)
			}
			continue
		}

		if step.Config != "" {
			err := testStepNewConfig(t, c, wd, step)
			if step.ExpectError != nil {
				if err == nil {
					t.Fatal("Step %d, expected an error but got none")
				}
				if !step.ExpectError.MatchString(err.Error()) {
					t.Fatalf("Step %d, expected an error with pattern, no match on: %s", err)
				}
			} else {
				if err != nil {
					t.Fatalf("Step %d error: %s", err)
				}
			}
			appliedCfg = step.Config
			continue
		}

		t.Fatal("Unsupported test mode")
	}
}

func getState(t testing.T, wd *tftest.WorkingDir) *terraform.State {
	t.Helper()

	jsonState := wd.RequireState(t)
	state, err := shimStateFromJson(jsonState)
	if err != nil {
		t.Fatal(err)
	}
	return state
}

func stateIsEmpty(state *terraform.State) bool {
	return state.Empty() || !state.HasResources()
}

func planIsEmpty(plan *tfjson.Plan) bool {
	for _, rc := range plan.ResourceChanges {
		if rc.Mode == tfjson.DataResourceMode {
			// Skip data sources as the current implementation ignores
			// existing state and they are all re-read every time
			continue
		}

		for _, a := range rc.Change.Actions {
			if a != tfjson.ActionNoop {
				return false
			}
		}
	}
	return true
}

func testIDRefresh(c TestCase, t testing.T, wd *tftest.WorkingDir, step TestStep, r *terraform.ResourceState) error {
	t.Helper()

	spewConf := spew.NewDefaultConfig()
	spewConf.SortKeys = true

	// Build the state. The state is just the resource with an ID. There
	// are no attributes. We only set what is needed to perform a refresh.
	state := terraform.NewState()
	state.RootModule().Resources = make(map[string]*terraform.ResourceState)
	state.RootModule().Resources[c.IDRefreshName] = &terraform.ResourceState{}

	// Temporarily set the config to a minimal provider config for the refresh
	// test. After the refresh we can reset it.
	cfg, err := testProviderConfig(c)
	if err != nil {
		return err
	}
	wd.RequireSetConfig(t, cfg)
	defer wd.RequireSetConfig(t, step.Config)

	// Refresh!
	err = runProviderCommand(t, func() error {
		wd.RequireRefresh(t)
		state = getState(t, wd)
		return nil
	}, wd, c.ProviderFactories)
	if err != nil {
		return err
	}

	// Verify attribute equivalence.
	actualR := state.RootModule().Resources[c.IDRefreshName]
	if actualR == nil {
		return fmt.Errorf("Resource gone!")
	}
	if actualR.Primary == nil {
		return fmt.Errorf("Resource has no primary instance")
	}
	actual := actualR.Primary.Attributes
	expected := r.Primary.Attributes
	// Remove fields we're ignoring
	for _, v := range c.IDRefreshIgnore {
		for k := range actual {
			if strings.HasPrefix(k, v) {
				delete(actual, k)
			}
		}
		for k := range expected {
			if strings.HasPrefix(k, v) {
				delete(expected, k)
			}
		}
	}

	if !reflect.DeepEqual(actual, expected) {
		// Determine only the different attributes
		for k, v := range expected {
			if av, ok := actual[k]; ok && v == av {
				delete(expected, k)
				delete(actual, k)
			}
		}

		spewConf := spew.NewDefaultConfig()
		spewConf.SortKeys = true
		return fmt.Errorf(
			"Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
				"\n\n%s\n\n%s",
			spewConf.Sdump(actual), spewConf.Sdump(expected))
	}

	return nil
}
<|endoftext|>


<commit_before>package schema

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"sync"

	"github.com/hashicorp/terraform/terraform"
)

// newValueWriter is a minor re-implementation of MapFieldWriter to include
// keys that should be marked as computed, to represent the new part of a
// pseudo-diff.
type newValueWriter struct {
	*MapFieldWriter

	// A list of keys that should be marked as computed.
	computedKeys map[string]bool

	// A lock to prevent races on writes. The underlying writer will have one as
	// well - this is for computed keys.
	lock sync.Mutex
}

// WriteField overrides MapValueWriter's WriteField, adding the ability to flag
// the address as computed.
func (w *newValueWriter) WriteField(address []string, value interface{}, computed bool) error {
	// Fail the write if we have a non-nil value and computed is true.
	// NewComputed values should not have a value when written.
	if value != nil && computed {
		return errors.New("Non-nil value with computed set")
	}

	if err := w.MapFieldWriter.WriteField(address, value); err != nil {
		return err
	}

	w.lock.Lock()
	defer w.lock.Unlock()
	if w.computedKeys == nil {
		w.computedKeys = make(map[string]bool)
	}

	if computed {
		w.computedKeys[strings.Join(address, ".")] = true
	}
	return nil
}

// ComputedKeysMap returns the underlying computed keys map.
func (w *newValueWriter) ComputedKeysMap() map[string]bool {
	w.lock.Lock()
	defer w.lock.Unlock()
	if w.computedKeys == nil {
		w.computedKeys = make(map[string]bool)
	}
	return w.computedKeys
}

// newValueReader is a minor re-implementation of MapFieldReader and is the
// read counterpart to MapValueWriter, allowing the read of keys flagged as
// computed to accommodate the diff override logic in ResourceDiff.
type newValueReader struct {
	*MapFieldReader

	// The list of computed keys from a newValueWriter.
	computedKeys map[string]bool
}

// ReadField reads the values from the underlying writer, returning the
// computed value if it is found as well.
func (r *newValueReader) ReadField(address []string) (FieldReadResult, error) {
	addrKey := strings.Join(address, ".")
	v, err := r.MapFieldReader.ReadField(address)
	if err != nil {
		return FieldReadResult{}, err
	}
	for computedKey := range r.computedKeys {
		if strings.HasPrefix(addrKey, computedKey) {
			if strings.HasSuffix(addrKey, ".#") {
				// This is a count value for a list or set that has been marked as
				// computed, or a sub-list/sub-set of a complex resource that has
				// been marked as computed.  We need to pass through to other readers
				// so that an accurate previous count can be fetched for the diff.
				v.Exists = false
			}
			v.Computed = true
		}
	}

	return v, nil
}

// ResourceDiff is used to query and make custom changes to an in-flight diff.
// It can be used to veto particular changes in the diff, customize the diff
// that has been created, or diff values not controlled by config.
//
// The object functions similar to ResourceData, however most notably lacks
// Set, SetPartial, and Partial, as it should be used to change diff values
// only.  Most other first-class ResourceData functions exist, namely Get,
// GetOk, HasChange, and GetChange exist.
//
// All functions in ResourceDiff, save for ForceNew, can only be used on
// computed fields.
type ResourceDiff struct {
	// The schema for the resource being worked on.
	schema map[string]*Schema

	// The current config for this resource.
	config *terraform.ResourceConfig

	// The state for this resource as it exists post-refresh, after the initial
	// diff.
	state *terraform.InstanceState

	// The diff created by Terraform. This diff is used, along with state,
	// config, and custom-set diff data, to provide a multi-level reader
	// experience similar to ResourceData.
	diff *terraform.InstanceDiff

	// The internal reader structure that contains the state, config, the default
	// diff, and the new diff.
	multiReader *MultiLevelFieldReader

	// A writer that writes overridden new fields.
	newWriter *newValueWriter

	// Tracks which keys have been updated by SetNew, SetNewComputed, and SetDiff
	// to ensure that the diff does not get re-run on keys that were not touched,
	// or diffs that were just removed (re-running on the latter would just roll
	// back the removal).
	updatedKeys map[string]bool
}

// newResourceDiff creates a new ResourceDiff instance.
func newResourceDiff(schema map[string]*Schema, config *terraform.ResourceConfig, state *terraform.InstanceState, diff *terraform.InstanceDiff) *ResourceDiff {
	d := &ResourceDiff{
		config: config,
		state:  state,
		diff:   diff,
		schema: schema,
	}

	d.newWriter = &newValueWriter{
		MapFieldWriter: &MapFieldWriter{Schema: d.schema},
	}
	readers := make(map[string]FieldReader)
	var stateAttributes map[string]string
	if d.state != nil {
		stateAttributes = d.state.Attributes
		readers["state"] = &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(stateAttributes),
		}
	}
	if d.config != nil {
		readers["config"] = &ConfigFieldReader{
			Schema: d.schema,
			Config: d.config,
		}
	}
	if d.diff != nil {
		readers["diff"] = &DiffFieldReader{
			Schema: d.schema,
			Diff:   d.diff,
			Source: &MultiLevelFieldReader{
				Levels:  []string{"state", "config"},
				Readers: readers,
			},
		}
	}
	readers["newDiff"] = &newValueReader{
		MapFieldReader: &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(d.newWriter.Map()),
		},
		computedKeys: d.newWriter.ComputedKeysMap(),
	}
	d.multiReader = &MultiLevelFieldReader{
		Levels: []string{
			"state",
			"config",
			"diff",
			"newDiff",
		},

		Readers: readers,
	}

	d.updatedKeys = make(map[string]bool)

	return d
}

// UpdatedKeys returns the keys that were updated by SetNew, SetNewComputed, or
// SetDiff. These are the only keys that a diff should be re-calculated for.
func (d *ResourceDiff) UpdatedKeys() []string {
	s := make([]string, 0)
	for k := range d.updatedKeys {
		s = append(s, k)
	}
	return s
}

// Clear wipes the diff for a particular key. It is called by SetDiff to remove
// any possibility of conflicts, but can be called on its own to just remove a
// specific key from the diff completely.
//
// Note that this does not wipe an override. This function is only allowed on
// computed keys.
func (d *ResourceDiff) Clear(key string) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("Clear is allowed on computed attributes only - %s is not one", key)
	}

	return d.clear(key)
}

func (d *ResourceDiff) clear(key string) error {
	// Check the schema to make sure that this key exists first.
	if _, ok := d.schema[key]; !ok {
		return fmt.Errorf("%s is not a valid key", key)
	}
	for k := range d.diff.Attributes {
		if strings.HasPrefix(k, key) {
			delete(d.diff.Attributes, k)
		}
	}
	return nil
}

// diffChange helps to implement resourceDiffer and derives its change values
// from ResourceDiff's own change data, in addition to existing diff, config, and state.
func (d *ResourceDiff) diffChange(key string) (interface{}, interface{}, bool, bool) {
	old, new := d.getChange(key)

	if !old.Exists {
		old.Value = nil
	}
	if !new.Exists {
		new.Value = nil
	}

	return old.Value, new.Value, !reflect.DeepEqual(old.Value, new.Value), new.Computed
}

// SetNew is used to set a new diff value for the mentioned key. The value must
// be correct for the attribute's schema (mostly relevant for maps, lists, and
// sets). The original value from the state is used as the old value.
//
// This function is only allowed on computed attributes.
func (d *ResourceDiff) SetNew(key string, value interface{}) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("SetNew only operates on computed keys - %s is not one", key)
	}
	return d.setDiff(key, d.get(strings.Split(key, "."), "state").Value, value, false)
}

// SetNewComputed functions like SetNew, except that it blanks out a new value
// and marks it as computed.
//
// This function is only allowed on computed attributes.
func (d *ResourceDiff) SetNewComputed(key string) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("SetNewComputed only operates on computed keys - %s is not one", key)
	}
	return d.setDiff(key, d.get(strings.Split(key, "."), "state").Value, nil, true)
}

// setDiff performs common diff setting behaviour.
func (d *ResourceDiff) setDiff(key string, old, new interface{}, computed bool) error {
	if err := d.clear(key); err != nil {
		return err
	}

	if err := d.newWriter.WriteField(strings.Split(key, "."), new, computed); err != nil {
		return fmt.Errorf("Cannot set new diff value for key %s: %s", key, err)
	}

	d.updatedKeys[key] = true

	return nil
}

// ForceNew force-flags ForceNew in the schema for a specific key, and
// re-calculates its diff. This function is a no-op/error if there is no diff.
//
// Note that the change to schema is permanent for the lifecycle of this
// specific ResourceDiff instance.
func (d *ResourceDiff) ForceNew(key string) error {
	if !d.HasChange(key) {
		return fmt.Errorf("ResourceDiff.ForceNew: No changes for %s", key)
	}

	old, new := d.GetChange(key)
	d.schema[key].ForceNew = true
	return d.setDiff(key, old, new, false)
}

// Get hands off to ResourceData.Get.
func (d *ResourceDiff) Get(key string) interface{} {
	r, _ := d.GetOk(key)
	return r
}

// GetChange gets the change between the state and diff, checking first to see
// if a overridden diff exists.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) GetChange(key string) (interface{}, interface{}) {
	old, new := d.getChange(key)
	return old.Value, new.Value
}

// GetOk functions the same way as ResourceData.GetOk, but it also checks the
// new diff levels to provide data consistent with the current state of the
// customized diff.
func (d *ResourceDiff) GetOk(key string) (interface{}, bool) {
	r := d.get(strings.Split(key, "."), "newDiff")
	exists := r.Exists && !r.Computed
	if exists {
		// If it exists, we also want to verify it is not the zero-value.
		value := r.Value
		zero := r.Schema.Type.Zero()

		if eq, ok := value.(Equal); ok {
			exists = !eq.Equal(zero)
		} else {
			exists = !reflect.DeepEqual(value, zero)
		}
	}

	return r.Value, exists
}

// HasChange checks to see if there is a change between state and the diff, or
// in the overridden diff.
func (d *ResourceDiff) HasChange(key string) bool {
	old, new := d.GetChange(key)

	// If the type implements the Equal interface, then call that
	// instead of just doing a reflect.DeepEqual. An example where this is
	// needed is *Set
	if eq, ok := old.(Equal); ok {
		return !eq.Equal(new)
	}

	return !reflect.DeepEqual(old, new)
}

// Id returns the ID of this resource.
//
// Note that technically, ID does not change during diffs (it either has
// already changed in the refresh, or will change on update), hence we do not
// support updating the ID or fetching it from anything else other than state.
func (d *ResourceDiff) Id() string {
	var result string

	if d.state != nil {
		result = d.state.ID
	}
	return result
}

// getChange gets values from two different levels, designed for use in
// diffChange, HasChange, and GetChange.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) getChange(key string) (getResult, getResult) {
	old := d.get(strings.Split(key, "."), "state")
	var new getResult
	for p := range d.updatedKeys {
		if childAddrOf(key, p) {
			new = d.getExact(strings.Split(key, "."), "newDiff")
			goto done
		}
	}
	new = d.get(strings.Split(key, "."), "newDiff")
done:
	return old, new
}

// get performs the appropriate multi-level reader logic for ResourceDiff,
// starting at source. Refer to newResourceDiff for the level order.
func (d *ResourceDiff) get(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldMerge(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// getExact gets an attribute from the exact level referenced by source.
func (d *ResourceDiff) getExact(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldExact(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// finalizeResult does some post-processing of the result produced by get and getExact.
func (d *ResourceDiff) finalizeResult(addr []string, result FieldReadResult) getResult {
	// If the result doesn't exist, then we set the value to the zero value
	var schema *Schema
	if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 {
		schema = schemaL[len(schemaL)-1]
	}

	if result.Value == nil && schema != nil {
		result.Value = result.ValueOrZero(schema)
	}

	// Transform the FieldReadResult into a getResult. It might be worth
	// merging these two structures one day.
	return getResult{
		Value:          result.Value,
		ValueProcessed: result.ValueProcessed,
		Computed:       result.Computed,
		Exists:         result.Exists,
		Schema:         schema,
	}
}

// childAddrOf does a comparison of two addresses to see if one is the child of
// the other.
func childAddrOf(child, parent string) bool {
	cs := strings.Split(child, ".")
	ps := strings.Split(parent, ".")
	return reflect.DeepEqual(ps, cs[:len(ps)])
}
<commit_msg>helper/schema: Guard against out of range on childAddrOf<commit_after>package schema

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"sync"

	"github.com/hashicorp/terraform/terraform"
)

// newValueWriter is a minor re-implementation of MapFieldWriter to include
// keys that should be marked as computed, to represent the new part of a
// pseudo-diff.
type newValueWriter struct {
	*MapFieldWriter

	// A list of keys that should be marked as computed.
	computedKeys map[string]bool

	// A lock to prevent races on writes. The underlying writer will have one as
	// well - this is for computed keys.
	lock sync.Mutex
}

// WriteField overrides MapValueWriter's WriteField, adding the ability to flag
// the address as computed.
func (w *newValueWriter) WriteField(address []string, value interface{}, computed bool) error {
	// Fail the write if we have a non-nil value and computed is true.
	// NewComputed values should not have a value when written.
	if value != nil && computed {
		return errors.New("Non-nil value with computed set")
	}

	if err := w.MapFieldWriter.WriteField(address, value); err != nil {
		return err
	}

	w.lock.Lock()
	defer w.lock.Unlock()
	if w.computedKeys == nil {
		w.computedKeys = make(map[string]bool)
	}

	if computed {
		w.computedKeys[strings.Join(address, ".")] = true
	}
	return nil
}

// ComputedKeysMap returns the underlying computed keys map.
func (w *newValueWriter) ComputedKeysMap() map[string]bool {
	w.lock.Lock()
	defer w.lock.Unlock()
	if w.computedKeys == nil {
		w.computedKeys = make(map[string]bool)
	}
	return w.computedKeys
}

// newValueReader is a minor re-implementation of MapFieldReader and is the
// read counterpart to MapValueWriter, allowing the read of keys flagged as
// computed to accommodate the diff override logic in ResourceDiff.
type newValueReader struct {
	*MapFieldReader

	// The list of computed keys from a newValueWriter.
	computedKeys map[string]bool
}

// ReadField reads the values from the underlying writer, returning the
// computed value if it is found as well.
func (r *newValueReader) ReadField(address []string) (FieldReadResult, error) {
	addrKey := strings.Join(address, ".")
	v, err := r.MapFieldReader.ReadField(address)
	if err != nil {
		return FieldReadResult{}, err
	}
	for computedKey := range r.computedKeys {
		if strings.HasPrefix(addrKey, computedKey) {
			if strings.HasSuffix(addrKey, ".#") {
				// This is a count value for a list or set that has been marked as
				// computed, or a sub-list/sub-set of a complex resource that has
				// been marked as computed.  We need to pass through to other readers
				// so that an accurate previous count can be fetched for the diff.
				v.Exists = false
			}
			v.Computed = true
		}
	}

	return v, nil
}

// ResourceDiff is used to query and make custom changes to an in-flight diff.
// It can be used to veto particular changes in the diff, customize the diff
// that has been created, or diff values not controlled by config.
//
// The object functions similar to ResourceData, however most notably lacks
// Set, SetPartial, and Partial, as it should be used to change diff values
// only.  Most other first-class ResourceData functions exist, namely Get,
// GetOk, HasChange, and GetChange exist.
//
// All functions in ResourceDiff, save for ForceNew, can only be used on
// computed fields.
type ResourceDiff struct {
	// The schema for the resource being worked on.
	schema map[string]*Schema

	// The current config for this resource.
	config *terraform.ResourceConfig

	// The state for this resource as it exists post-refresh, after the initial
	// diff.
	state *terraform.InstanceState

	// The diff created by Terraform. This diff is used, along with state,
	// config, and custom-set diff data, to provide a multi-level reader
	// experience similar to ResourceData.
	diff *terraform.InstanceDiff

	// The internal reader structure that contains the state, config, the default
	// diff, and the new diff.
	multiReader *MultiLevelFieldReader

	// A writer that writes overridden new fields.
	newWriter *newValueWriter

	// Tracks which keys have been updated by SetNew, SetNewComputed, and SetDiff
	// to ensure that the diff does not get re-run on keys that were not touched,
	// or diffs that were just removed (re-running on the latter would just roll
	// back the removal).
	updatedKeys map[string]bool
}

// newResourceDiff creates a new ResourceDiff instance.
func newResourceDiff(schema map[string]*Schema, config *terraform.ResourceConfig, state *terraform.InstanceState, diff *terraform.InstanceDiff) *ResourceDiff {
	d := &ResourceDiff{
		config: config,
		state:  state,
		diff:   diff,
		schema: schema,
	}

	d.newWriter = &newValueWriter{
		MapFieldWriter: &MapFieldWriter{Schema: d.schema},
	}
	readers := make(map[string]FieldReader)
	var stateAttributes map[string]string
	if d.state != nil {
		stateAttributes = d.state.Attributes
		readers["state"] = &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(stateAttributes),
		}
	}
	if d.config != nil {
		readers["config"] = &ConfigFieldReader{
			Schema: d.schema,
			Config: d.config,
		}
	}
	if d.diff != nil {
		readers["diff"] = &DiffFieldReader{
			Schema: d.schema,
			Diff:   d.diff,
			Source: &MultiLevelFieldReader{
				Levels:  []string{"state", "config"},
				Readers: readers,
			},
		}
	}
	readers["newDiff"] = &newValueReader{
		MapFieldReader: &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(d.newWriter.Map()),
		},
		computedKeys: d.newWriter.ComputedKeysMap(),
	}
	d.multiReader = &MultiLevelFieldReader{
		Levels: []string{
			"state",
			"config",
			"diff",
			"newDiff",
		},

		Readers: readers,
	}

	d.updatedKeys = make(map[string]bool)

	return d
}

// UpdatedKeys returns the keys that were updated by SetNew, SetNewComputed, or
// SetDiff. These are the only keys that a diff should be re-calculated for.
func (d *ResourceDiff) UpdatedKeys() []string {
	var s []string
	for k := range d.updatedKeys {
		s = append(s, k)
	}
	return s
}

// Clear wipes the diff for a particular key. It is called by SetDiff to remove
// any possibility of conflicts, but can be called on its own to just remove a
// specific key from the diff completely.
//
// Note that this does not wipe an override. This function is only allowed on
// computed keys.
func (d *ResourceDiff) Clear(key string) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("Clear is allowed on computed attributes only - %s is not one", key)
	}

	return d.clear(key)
}

func (d *ResourceDiff) clear(key string) error {
	// Check the schema to make sure that this key exists first.
	if _, ok := d.schema[key]; !ok {
		return fmt.Errorf("%s is not a valid key", key)
	}
	for k := range d.diff.Attributes {
		if strings.HasPrefix(k, key) {
			delete(d.diff.Attributes, k)
		}
	}
	return nil
}

// diffChange helps to implement resourceDiffer and derives its change values
// from ResourceDiff's own change data, in addition to existing diff, config, and state.
func (d *ResourceDiff) diffChange(key string) (interface{}, interface{}, bool, bool) {
	old, new := d.getChange(key)

	if !old.Exists {
		old.Value = nil
	}
	if !new.Exists {
		new.Value = nil
	}

	return old.Value, new.Value, !reflect.DeepEqual(old.Value, new.Value), new.Computed
}

// SetNew is used to set a new diff value for the mentioned key. The value must
// be correct for the attribute's schema (mostly relevant for maps, lists, and
// sets). The original value from the state is used as the old value.
//
// This function is only allowed on computed attributes.
func (d *ResourceDiff) SetNew(key string, value interface{}) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("SetNew only operates on computed keys - %s is not one", key)
	}
	return d.setDiff(key, d.get(strings.Split(key, "."), "state").Value, value, false)
}

// SetNewComputed functions like SetNew, except that it blanks out a new value
// and marks it as computed.
//
// This function is only allowed on computed attributes.
func (d *ResourceDiff) SetNewComputed(key string) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("SetNewComputed only operates on computed keys - %s is not one", key)
	}
	return d.setDiff(key, d.get(strings.Split(key, "."), "state").Value, nil, true)
}

// setDiff performs common diff setting behaviour.
func (d *ResourceDiff) setDiff(key string, old, new interface{}, computed bool) error {
	if err := d.clear(key); err != nil {
		return err
	}

	if err := d.newWriter.WriteField(strings.Split(key, "."), new, computed); err != nil {
		return fmt.Errorf("Cannot set new diff value for key %s: %s", key, err)
	}

	d.updatedKeys[key] = true

	return nil
}

// ForceNew force-flags ForceNew in the schema for a specific key, and
// re-calculates its diff. This function is a no-op/error if there is no diff.
//
// Note that the change to schema is permanent for the lifecycle of this
// specific ResourceDiff instance.
func (d *ResourceDiff) ForceNew(key string) error {
	if !d.HasChange(key) {
		return fmt.Errorf("ResourceDiff.ForceNew: No changes for %s", key)
	}

	old, new := d.GetChange(key)
	d.schema[key].ForceNew = true
	return d.setDiff(key, old, new, false)
}

// Get hands off to ResourceData.Get.
func (d *ResourceDiff) Get(key string) interface{} {
	r, _ := d.GetOk(key)
	return r
}

// GetChange gets the change between the state and diff, checking first to see
// if a overridden diff exists.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) GetChange(key string) (interface{}, interface{}) {
	old, new := d.getChange(key)
	return old.Value, new.Value
}

// GetOk functions the same way as ResourceData.GetOk, but it also checks the
// new diff levels to provide data consistent with the current state of the
// customized diff.
func (d *ResourceDiff) GetOk(key string) (interface{}, bool) {
	r := d.get(strings.Split(key, "."), "newDiff")
	exists := r.Exists && !r.Computed
	if exists {
		// If it exists, we also want to verify it is not the zero-value.
		value := r.Value
		zero := r.Schema.Type.Zero()

		if eq, ok := value.(Equal); ok {
			exists = !eq.Equal(zero)
		} else {
			exists = !reflect.DeepEqual(value, zero)
		}
	}

	return r.Value, exists
}

// HasChange checks to see if there is a change between state and the diff, or
// in the overridden diff.
func (d *ResourceDiff) HasChange(key string) bool {
	old, new := d.GetChange(key)

	// If the type implements the Equal interface, then call that
	// instead of just doing a reflect.DeepEqual. An example where this is
	// needed is *Set
	if eq, ok := old.(Equal); ok {
		return !eq.Equal(new)
	}

	return !reflect.DeepEqual(old, new)
}

// Id returns the ID of this resource.
//
// Note that technically, ID does not change during diffs (it either has
// already changed in the refresh, or will change on update), hence we do not
// support updating the ID or fetching it from anything else other than state.
func (d *ResourceDiff) Id() string {
	var result string

	if d.state != nil {
		result = d.state.ID
	}
	return result
}

// getChange gets values from two different levels, designed for use in
// diffChange, HasChange, and GetChange.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) getChange(key string) (getResult, getResult) {
	old := d.get(strings.Split(key, "."), "state")
	var new getResult
	for p := range d.updatedKeys {
		if childAddrOf(key, p) {
			new = d.getExact(strings.Split(key, "."), "newDiff")
			goto done
		}
	}
	new = d.get(strings.Split(key, "."), "newDiff")
done:
	return old, new
}

// get performs the appropriate multi-level reader logic for ResourceDiff,
// starting at source. Refer to newResourceDiff for the level order.
func (d *ResourceDiff) get(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldMerge(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// getExact gets an attribute from the exact level referenced by source.
func (d *ResourceDiff) getExact(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldExact(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// finalizeResult does some post-processing of the result produced by get and getExact.
func (d *ResourceDiff) finalizeResult(addr []string, result FieldReadResult) getResult {
	// If the result doesn't exist, then we set the value to the zero value
	var schema *Schema
	if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 {
		schema = schemaL[len(schemaL)-1]
	}

	if result.Value == nil && schema != nil {
		result.Value = result.ValueOrZero(schema)
	}

	// Transform the FieldReadResult into a getResult. It might be worth
	// merging these two structures one day.
	return getResult{
		Value:          result.Value,
		ValueProcessed: result.ValueProcessed,
		Computed:       result.Computed,
		Exists:         result.Exists,
		Schema:         schema,
	}
}

// childAddrOf does a comparison of two addresses to see if one is the child of
// the other.
func childAddrOf(child, parent string) bool {
	cs := strings.Split(child, ".")
	ps := strings.Split(parent, ".")
	if len(ps) > len(cs) {
		return false
	}
	return reflect.DeepEqual(ps, cs[:len(ps)])
}
<|endoftext|>