prop

package
v2.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 25, 2020 License: MIT Imports: 9 Imported by: 1

Documentation

Overview

This package contains SCIM property definitions and its respective implementations defined in the SCIM specification. Various mechanisms to access data structure and react to local data changes are also part of the package.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func SubscriberFactory

func SubscriberFactory() *subscriberFactory

Return the subscriber factory to Register and Create subscribers using annotations.

func Visit

func Visit(property Property, visitor Visitor) error

Visit is the entry point to visit a property in a depth-first-search fashion.

Example
getVisitor := func() Visitor {
	// type ExampleVisitor struct {}
	//
	// func (v *ExampleVisitor) ShouldVisit(_ Property) bool {
	// 	return true
	// }
	//
	// func (v *ExampleVisitor) Visit(property Property) error {
	// 	println(property.Raw())
	// 	return nil
	// }
	//
	// func (v *ExampleVisitor) BeginChildren(container Property) {
	// 	println("entering", container.Attribute().Path())
	// }
	//
	// func (v *ExampleVisitor) EndChildren(container Property) {
	// 	println("exiting", container.Attribute().Path())
	// }
	// assuming it returns the above ExampleVisitor
	return nil
}
v := getVisitor()

getProperty := func() Property {
	// Assuming a property structure of:
	//	{
	//		"id": "foobar",
	//		"name": {
	//			"givenName": "David",
	//			"familyName": "Q"
	//		}
	//	}
	// assuming it returns the above property
	return nil
}
p := getProperty()

_ = Visit(p, v)
// should print:
//
// foobar
// map[string]interface{}{"givenName": "David", "familyName": "Q"}
// entering name
// David
// Q
// exiting name
Output:

Types

type AutoCompactSubscriber

type AutoCompactSubscriber struct{}

AutoCompactSubscriber automatically compacts the multiValued property.

It is mounted by @AutoCompact annotation onto a multiValued property. If the mounted property is not multiValued, this subscriber does nothing.

The subscriber reacts to unassigned events from its elements and invokes the hidden Compact API on the multiValued property.

func (*AutoCompactSubscriber) Notify

func (s *AutoCompactSubscriber) Notify(publisher Property, events *Events) error

type CoCapable

type CoCapable interface {
	// Contains return true if the property's value contains the given value.
	// If the given value is nil, always return false.
	Contains(value string) bool
}

CoCapable defines capability to perform 'co' operations. It should be implemented by capable Property implementations.

type ComplexStateSummarySubscriber

type ComplexStateSummarySubscriber struct {
	// contains filtered or unexported fields
}

ComplexStateSummarySubscriber summarizes the state changes of the sub properties of a complex property and generate new event to describe the inferred state change on the complex property.

It is mounted by @StateSummary annotation onto a complex property. If the mounted property is not complex, this subscriber does nothing.

The subscriber reacts to state change events from its sub properties and computes a new assigned state for the complex property. The new state is compared to an old cached assigned state in order to determine state change for the complex property. It also reacts to direct state changes on the complex property itself, in which case, the subscriber simply updates the state without generating new events.

func (*ComplexStateSummarySubscriber) Notify

func (s *ComplexStateSummarySubscriber) Notify(publisher Property, events *Events) error

type EqCapable

type EqCapable interface {
	// EqualsTo return true if the property's value is equal to the given value.
	// If the given value is nil, always return false.
	EqualsTo(value interface{}) bool
}

EqCapable defines the capability to perform 'eq' operations, and by logic, 'ne' operations. It should be implemented by capable Property implementations.

type Event

type Event struct {
	// contains filtered or unexported fields
}

A single modification event.

func (Event) PreModData

func (e Event) PreModData() interface{}

PreModData optionally returns the Source's data before the modification. Note PreModData is not always available, hence should not be relied upon.

func (Event) Source

func (e Event) Source() Property

Source returns the Property that emits the event

func (*Event) ToEvents

func (e *Event) ToEvents() *Events

ToEvents conveniently creates an Events package that contains this single event.

func (Event) Type

func (e Event) Type() EventType

Type returns the type of the event

type EventType

type EventType int

Type of an event

const (

	// Event that new value has been assigned to the property.
	// The event entails that the property is now in an "assigned" state, such that
	// calling IsUnassigned shall return false. It is only emitted when value introduced
	// to the property is different than the previous value, otherwise, no event would be
	// emitted.
	EventAssigned EventType
	// Event that property value was deleted and now is an "unassigned" state. Calling
	// IsUnassigned shall return true. It is only emitted that value was deleted from property
	// when it was in an "assigned" state, otherwise, no event would be emitted.
	EventUnassigned
)

Type of modification events

func (EventType) NewFrom

func (et EventType) NewFrom(source Property, pre interface{}) *Event

NewFrom constructs an Event of this type.

type Events

type Events struct {
	// contains filtered or unexported fields
}

Events is the package of one or more Event. While a single modification call emits a single Event, the emitted event is wrapped in the Events package and passed along the trace stack for subscriber processing. Subscriber may modify or append to the events in this package, hence the necessity for this structure.

func (*Events) Append

func (e *Events) Append(ev *Event)

Append adds a new Event to the Events package.

func (*Events) Count

func (e *Events) Count() int

Count returns the total number of events.

func (*Events) FindEvent

func (e *Events) FindEvent(criteria func(ev *Event) bool) *Event

FindEvent searches and returns the first Event that matches the criteria, or nil if no such match.

func (*Events) ForEachEvent

func (e *Events) ForEachEvent(callback func(ev *Event) error) error

ForEachEvent invokes callback function on each Event, any error aborts the process and is returned immediately.

type EwCapable

type EwCapable interface {
	// EndsWith return true if the property's value ends with the given value.
	// If the given value is nil, always return false.
	EndsWith(value string) bool
}

EwCapable defines capability to perform 'ew' operations. It should be implemented by capable Property implementations.

type ExclusivePrimarySubscriber

type ExclusivePrimarySubscriber struct{}

ExclusivePrimarySubscriber automatically turns off the true-valued primary sub property when another primary sub property is set to true.

It is mounted by @ExclusivePrimary annotation onto the a multiValued complex property whose sub property contains a boolean property which is annotated @Primary. This boolean property is referred to as the primary property.

The subscriber reacts to assigned events from the primary property. If the event reports a primary property has a new value of true, this subscriber goes through all primary properties and turn off the old true value. The result is that at most one primary property will have the value of true.

func (*ExclusivePrimarySubscriber) Notify

func (s *ExclusivePrimarySubscriber) Notify(publisher Property, events *Events) error

type GeCapable

type GeCapable interface {
	// GreaterThanOrEqualTo return true if the property's value is greater than or equal to the given value.
	// If the given value is nil, always return false.
	GreaterThanOrEqualTo(value interface{}) bool
}

GeCapable defines capability to perform 'ge' operations. It should be implemented by capable Property implementations.

type GtCapable

type GtCapable interface {
	// GreaterThan return true if the property's value is greater than the given value.
	// If the given value is nil, always return false.
	GreaterThan(value interface{}) bool
}

GtCapable defines capability to perform 'gt' operations. It should be implemented by capable Property implementations.

type LeCapable

type LeCapable interface {
	// LessThanOrEqualTo return true if the property's value is greater than or equal to the given value.
	// If the given value is nil, always return false.
	LessThanOrEqualTo(value interface{}) bool
}

LeCapable defines capability to perform 'le' operations. It should be implemented by capable Property implementations.

type LtCapable

type LtCapable interface {
	// LessThan return true if the property's value is greater than the given value.
	// If the given value is nil, always return false.
	LessThan(value interface{}) bool
}

LtCapable defines capability to perform 'lt' operations. It should be implemented by capable Property implementations.

type Navigator interface {
	// Error returns any error occurred during fluent navigation. If any step
	// during the navigation had generated an error, further steps will become
	// no op and the original error is reflected here.
	Error() error
	// HasError returns true when Error is not nil.
	HasError() bool
	// ClearError resets the error in the navigator.
	ClearError()
	// Depth return the number of properties that was focused, including the
	// currently focused. These properties, excluding the current one, can be
	// refocused by calling Retract, one at a time in the reversed order that
	// were focused. The minimum depth is one.
	Depth() int
	// Source returns the property this Navigator is created with.
	Source() Property
	// Current returns the currently focused property on the top of the trace stack.
	Current() Property
	// Retract goes back to the last focused property. The source property that
	// this navigator was created with cannot be retracted
	Retract() Navigator
	// Dot focuses on the sub property that goes by the given name (case insensitive)
	Dot(name string) Navigator
	// At focuses on the element property at given index
	At(index int) Navigator
	// Where focuses on the first child property meeting given criteria
	Where(criteria func(child Property) bool) Navigator
	// Add delegates for Add of the Current property and propagates events to upstream properties.
	Add(value interface{}) Navigator
	// Replace delegates for Replace of the Current property and propagates events to upstream properties.
	Replace(value interface{}) Navigator
	// Delete delegates for Delete of the Current property and propagates events to upstream properties.
	Delete() Navigator
	// ForEachChild iterates each child property of the current property and invokes callback.
	// The method returns any error generated previously or generated by any of the callbacks.
	ForEachChild(callback func(index int, child Property) error) error
}

Navigator is a controlled mechanism to traverse the Resource/Property data structure. It should be used in cases where the caller has knowledge of what to access. For example, when de-serializing JSON into a Resource, caller has knowledge of the JSON structure, therefore knows what to access in the Resource structure.

The Navigator exists for two purposes: First, to maintain a call stack of the traversal trace, enabling modification events to be propagated along the trace, all the way to the root property, and notify subscribers on every level. Without such call stack, such things will have to resort to runtime reflection, which is hard to get right or maintain. Second, as a frequently used feature, the Navigator promotes fluent API by returning the instance for the majority of the accessor methods, making the code easier to read in general.

Because the Navigator does not return error on every error-able operation, implementation will mostly be stateful. Callers should call Error or HasError to check if the Navigator is currently in the error state, after performing one or possibly several chained operations fluently.

The call stack can be advanced by calling Dot, At and Where methods. Which methods to call depends on the context. The call stack can be retracted by calling Retract. The top and the bottom item on the stack can be queried by calling Current and Source. The stack depth is available via Depth.

Example
getResource := func() *Resource {
	return &Resource{}
}
// create navigator
nav := getResource().Navigator()
// traverse resource structure
nav.Dot("emails").At(1).Dot("value")
// check for errors during the chained traversal
if err := nav.Error(); err != nil {
	panic(err)
}
// access the property at the top of the trace stack
println(nav.Current().Raw())
Output:

func Navigate(property Property) Navigator

Navigate returns a navigator that allows caller to freely navigate the property structure and maintains the navigation history to enable retraction at any time. The navigator also exposes delegate methods to modify the property, and propagate modification events to upstream properties.

type PrCapable

type PrCapable interface {
	// Present return true if the property's value is present. Presence is defined to be non-nil and non-empty.
	Present() bool
}

PrCapable defines capability to perform 'pr' operations. It should be implemented by capable Property implementations.

type Property

type Property interface {
	// Attribute always returns a non-nil attribute to describe this property.
	Attribute() *spec.Attribute
	// Raw return the property's value in Golang's native type. The type correspondence are:
	// 	SCIM string <-> Go string
	//	SCIM integer <-> Go int64
	//	SCIM decimal <-> Go float64
	//	SCIM boolean <-> Go bool
	//	SCIM dateTime <-> Go string
	//	SCIM reference <-> Go string
	//	SCIM binary <-> Go string
	//	SCIM complex <-> Go map[string]interface{}
	//	SCIM multiValued <-> Go []interface{}
	// Property implementations are obliged to return in these types, or return a nil when unassigned. However,
	// implementations are not obliged to represent data in these types internally.
	Raw() interface{}
	// IsUnassigned return true if this property is unassigned. Unassigned is defined to be nil for singular non-complex
	// typed properties; empty for multiValued properties; and complex properties are unassigned if and only
	// if all its containing sub properties are unassigned.
	IsUnassigned() bool
	// Dirty returns true if any of Add/Replace/Delete method was ever called.
	// This method is necessary to distinguish between a naturally unassigned state and a deleted unassigned state.
	// When a property is first constructed, it has no value, and hence lies in an unassigned state. However, this
	// shall not be considered the same with a property returning to an unassigned state after having its value deleted.
	// Such difference is important as the SCIM specification mandates that user may override the server generated
	// default value for a readWrite property by explicitly providing "null" or "[]" (in case of multiValued) in the
	// JSON payload. In such situations, value generators should back off when an unassigned property is also dirty.
	Dirty() bool
	// Hash returns the hash value of this property's value. This will be helpful in comparing two properties.
	// Unassigned values shall return a hash of 0 (zero). Although this will create a potential hash
	// collision, we avoid this problem by checking the unassigned case first before comparing hashes.
	Hash() uint64
	// Matches return true if the two properties match each other. Properties match if and only if
	// their attributes match and their values are comparable according to the attribute.
	// 	For properties carrying singular non-complex attributes, attributes and values are compared.
	// 	For complex properties, two complex properties match if and only if all their identity sub properties match.
	// 	For multiValued properties, match only happens when they have the same number of element properties
	//	and the element properties all match correspondingly.
	// Two unassigned properties with the same attribute matches each other.
	Matches(another Property) bool
	// Clone return an exact clone of the property. The cloned property may share the same instance of attribute and
	// subscribers, but must retain individual copies of their values.
	Clone() Property
	// Add a value to the property and emit an event describing the change.
	// If the value already exists, no change will be made and the emitted event is nil. Otherwise, the value will
	// be added to the underlying data structure and mark the value dirty. For simple properties, calling this
	// method equates to calling Replace.
	Add(value interface{}) (*Event, error)
	// Replace value of this property and emit an event describing the change.
	// If the value equals to the current value, no change will be made and the emitted event is nil. Otherwise,
	// the underlying value will be replaced. Providing a nil value equates to calling Delete.
	Replace(value interface{}) (*Event, error)
	// Delete value from this property and emit an event describing the change.
	// If the property is already unassigned, deleting it again has no effect.
	Delete() (*Event, error)
	// Notify all subscribers of this property of the events.
	Notify(events *Events) error
	// CountChildren returns the number of children properties. Children properties are sub properties for complex
	// properties and element properties for multiValued properties. Other properties have no children.
	CountChildren() int
	// ForEachChild iterates all children properties and invoke callback function.
	ForEachChild(callback func(index int, child Property) error) error
	// FindChild returns the first children property that satisfies the criteria, or nil if none satisfies.
	FindChild(criteria func(child Property) bool) Property
	// ChildAtIndex returns the children property at given index. The type of index vary across implementations.
	ChildAtIndex(index interface{}) (Property, error)
}

Property holds a piece of data and is describe by an Attribute. The data requirement of the property is described by an enclosing attribute returned by the Attribute method. The enclosed data may be accessed via Raw and IsUnassigned method, and may be modified by Add, Replace and Delete method.

A Property may enclose other properties. Such property is known to be a container property. Default cases of container property are the singleValued complex property and the multiValued property, as defined in SCIM. A non-container property must return 0 to CountChildren and is generally indifferent to CountChildren, ForEachChild, FindChild and ChildAtIndex methods.

func NewBinary

func NewBinary(attr *spec.Attribute) Property

NewBinary creates a new binary property associated with attribute.

func NewBinaryOf

func NewBinaryOf(attr *spec.Attribute, value string) Property

NewBinaryOf creates a new binary property of given value associated with attribute.

func NewBoolean

func NewBoolean(attr *spec.Attribute) Property

NewBoolean creates a new boolean property associated with attribute.

func NewBooleanOf

func NewBooleanOf(attr *spec.Attribute, value bool) Property

NewBooleanOf creates a new boolean property of given value associated with attribute.

func NewComplex

func NewComplex(attr *spec.Attribute) Property

NewComplex creates a new complex property associated with attribute. All sub attributes are created.

func NewComplexOf

func NewComplexOf(attr *spec.Attribute, value map[string]interface{}) Property

NewComplexOf creates a new complex property of given value associated with attribute.

func NewDateTime

func NewDateTime(attr *spec.Attribute) Property

NewDateTime creates a new dateTime property associated with attribute.

func NewDateTimeOf

func NewDateTimeOf(attr *spec.Attribute, value string) Property

NewDateTimeOf creates a new dateTime property of given value associated with attribute.

func NewDecimal

func NewDecimal(attr *spec.Attribute) Property

NewDecimal creates a new decimal property associated with attribute.

func NewDecimalOf

func NewDecimalOf(attr *spec.Attribute, value float64) Property

NewDecimalOf creates a new decimal property of given value associated with attribute.

func NewInteger

func NewInteger(attr *spec.Attribute) Property

NewInteger creates a new integer property associated with attribute.

func NewIntegerOf

func NewIntegerOf(attr *spec.Attribute, value int64) Property

NewIntegerOf creates a new integer property of given value associated with attribute.

func NewMulti

func NewMulti(attr *spec.Attribute) Property

NewMulti creates a new multiValued property associated with attribute. All sub attributes are created.

func NewMultiOf

func NewMultiOf(attr *spec.Attribute, value []interface{}) Property

NewMultiOf creates a new multiValued property of given value associated with attribute.

func NewProperty

func NewProperty(attr *spec.Attribute) Property

NewProperty creates a new property of any legal SCIM type

func NewReference

func NewReference(attr *spec.Attribute) Property

NewReference creates a new reference property associated with attribute.

func NewReferenceOf

func NewReferenceOf(attr *spec.Attribute, value string) Property

NewReferenceOf creates a new reference property of given value associated with attribute.

func NewString

func NewString(attr *spec.Attribute) Property

NewString creates a new string property associated with attribute.

func NewStringOf

func NewStringOf(attr *spec.Attribute, value string) Property

NewStringOf creates a new string property of given value associated with attribute.

type Resource

type Resource struct {
	// contains filtered or unexported fields
}

Resource represents a SCIM resource. It is a wrapper around the root Property.

func NewResource

func NewResource(resourceType *spec.ResourceType) *Resource

NewResource creates a resource prototype of the attributes defined in the resource type, along with the core SCIM attributes.

func (*Resource) Clone

func (r *Resource) Clone() *Resource

Return a clone of this resource. The clone will contain properties that share the same instance of attribute and subscribers with the original property before the clone, but retain separate instance of values.

func (*Resource) Hash

func (r *Resource) Hash() uint64

Hash returns the hash of this resource, which is same hash of the root property.

func (*Resource) IdOrEmpty

func (r *Resource) IdOrEmpty() string

IdOrEmpty returns the id of the resource, defined in the core schema. If in any case the id is not available, (i.e. unassigned, wrong type), empty string is returned.

func (*Resource) MainSchemaId

func (r *Resource) MainSchemaId() string

MainSchemaId returns the id of the resource type's main schema.

func (*Resource) MetaLocationOrEmpty

func (r *Resource) MetaLocationOrEmpty() string

MetaLocationOrEmpty returns meta.location value of the resource, defined in the core schema. If in any case, the meta.location value is not available (i.e. unassigned, wrong type), empty string is returned.

func (*Resource) MetaVersionOrEmpty

func (r *Resource) MetaVersionOrEmpty() string

MetaVersionOrEmpty returns meta.version value of the resource, defined in the core schema. If in any case, the meta.version value is not available (i.e. unassigned, wrong type), empty string is returned.

func (*Resource) Navigator

func (r *Resource) Navigator() Navigator

Navigator returns a navigator on the root property.

func (*Resource) ResourceType

func (r *Resource) ResourceType() *spec.ResourceType

ResourceType returns the resource type of this resource

func (*Resource) RootAttribute

func (r *Resource) RootAttribute() *spec.Attribute

RootAttribute returns the attribute of the root property

func (*Resource) RootProperty

func (r *Resource) RootProperty() Property

RootProperty returns the root property

func (*Resource) Visit

func (r *Resource) Visit(visitor Visitor) error

Visit starts a DFS visit on the root property of the resource.

type SchemaSyncSubscriber

type SchemaSyncSubscriber struct{}

SchemaSyncSubscriber automatically synchronizes the schema property with respect to data changes in the resource.

It is mounted by @SyncSchema annotation onto the root of the property whose attribute is annotated with @Root. If not mounted onto @Root, this subscriber does nothing.

The subscriber reacts to state change events from the root property of schema extension attributes. In other words, it reacts to events whose source attribute is annotated with @StateSummary and @SchemaExtensionRoot. It adds the schema extension id to schemas property on assigned events; and removes the schema extension id from schemas property on unassigned events.

This subscriber does not attempt to compact the schemas property after element removal. This should be handled by AutoCompactSubscriber.

func (*SchemaSyncSubscriber) Notify

func (s *SchemaSyncSubscriber) Notify(publisher Property, events *Events) error

type Subscriber

type Subscriber interface {
	// Be notified and react to the a series of events. Subscriber is allowed to modify the events list in order to
	// affect downstream subscribers.
	Notify(publisher Property, events *Events) error
}

Subscriber attaches to Property and gets notified various state change events via Notify method.

There is no explicit way of attaching to Property. All Subscriber implementations are automatically loaded onto Property via the annotation mechanism. A Subscriber can Register itself with the SubscriberFactory via an annotation. When a Property is being created, it will check if one or more of its attribute annotations are associated with Subscriber and ask SubscriberFactory to create an instance of Subscriber and attach the instance to itself.

Implementations can choose to be either stateful or stateless. It is more memory efficient to create a single instance of stateless Subscriber. Yet, stateful subscribers might be more powerful as it allows user to specify parameters with the annotation, which gets passed in during initialization in the SubscriberFactory.

type SubscriberFactoryFunc

type SubscriberFactoryFunc func(publisher Property, params map[string]interface{}) Subscriber

Constructor function to initialize a Subscriber instance.

publisher is the property that is creating the Subscriber, and also what the Subscriber will eventually subscribe to.

params is the parameter specified with the annotation that was associated with the Subscriber type, it might be
useful when customizing Subscribers during initialization.

Stateless Subscriber implementation may choose to return the same instance.

type SwCapable

type SwCapable interface {
	// StartsWith return true if the property's value starts with the given value.
	// If the given value is nil, always return false.
	StartsWith(value string) bool
}

SwCapable defines capability to perform 'sw' operations. It should be implemented by capable Property implementations.

type Visitor

type Visitor interface {
	// Returns true if property should be visited; if false, the property will not be visited.
	ShouldVisit(property Property) bool
	// Visit the property, only when ShouldVisit returns true. If this method returns non-nil error,
	// the rest of the traversal will be aborted.
	Visit(property Property) error
	// Invoked when the children properties of a container property is about to be visited. The containing
	// property is supplied as an argument to provide context information. The container property itself,
	// however, has already been invoked on ShouldVisit and/or Visit.
	BeginChildren(container Property)
	// Invoked when the children properties of a container property has finished. The containing property
	// is supplied as a context argument.
	EndChildren(container Property)
}

Visitor defines behaviour for implementations to react to a passive Property structure traversal. It shall be used in cases where caller does not have knowledge of resource structure and has to rely on a spontaneous DFS traversal. By implementing this interface, caller will have some control over whether a property should be visited and be notified for entering and exiting container Property.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL