godata

package module
v1.0.10 Latest Latest
Warning

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

Go to latest
Published: Dec 18, 2023 License: MIT Imports: 11 Imported by: 17

README

Go golangci-lint

GoData

This is an implementation of OData in Go. It is capable of parsing an OData request, and exposing it in a standard way so that any provider can consume OData requests and produce a response. Providers can be written for general usage like producing SQL statements for a databases, or very specific uses like connecting to another API.

Most OData server frameworks are C#/.NET or Java. These require using the CLR or JVM, and are overkill for a lot of use cases. By using Go we aim to provide a lightweight, fast, and concurrent OData service. By exposing a generic interface to an OData request, we hope to enable any backend to expose itself with an OData API with as little effort as possible.

Status

This project is not finished yet, and cannot be used in its current state. Progress is underway to make it usable, and eventually fully compatible with the OData V4 specification.

Work in Progress

  • Parse OData URLs
  • Create provider interface for GET requests
  • Parse OData POST and PATCH requests
  • Create provider interface for POST and PATCH requests
  • Parse OData DELETE requests
  • Create provider interface for PATCH requests
  • Allow injecting middleware into the request pipeline to enable such features as caching, authentication, telemetry, etc.
  • Work on fully supporting the OData specification with unit tests

Feel free to contribute with any of these tasks.

High Level Architecture

If you're interesting in helping out, here is a quick introduction to the code to help you understand the process. The code works something like this:

  1. A provider is initialized that defines the object model (i.e., metadata), of the OData service. (See the example directory.)
  2. An HTTP request is received by the request handler in service.go
  3. The URL is parsed into a data structure defined in request_model.go
  4. The request model is semanticized, so each piece of the request is associated with an entity/type/collection/etc. in the provider object model.
  5. The correct method and type of request (entity, collection, $metadata, $ref, property, etc.) is determined from the semantic information.
  6. The request is then delegated to the appropriate method of a GoDataProvider, which will produce a response based on the semantic information, and package it into a response defined in response_model.go.
  7. The response is converted to JSON and sent back to the client.

Documentation

Index

Constants

View Source
const (
	ALLPAGES = "allpages"
	NONE     = "none"
)
View Source
const (
	GoDataString         = "Edm.String"
	GoDataInt16          = "Edm.Int16"
	GoDataInt32          = "Edm.Int32"
	GoDataInt64          = "Edm.Int64"
	GoDataDecimal        = "Edm.Decimal"
	GoDataBinary         = "Edm.Binary"
	GoDataBoolean        = "Edm.Boolean"
	GoDataTimeOfDay      = "Edm.TimeOfDay"
	GoDataDate           = "Edm.Date"
	GoDataDateTimeOffset = "Edm.DateTimeOffset"
)
View Source
const (
	ASC  = "asc"
	DESC = "desc"
)
View Source
const (
	OpAssociationLeft int = iota
	OpAssociationRight
	OpAssociationNone
)
View Source
const (
	TokenListExpr = "list"

	// TokenComma is the default separator for function arguments.
	TokenComma      = ","
	TokenOpenParen  = "("
	TokenCloseParen = ")"
)
View Source
const (
	ODataFieldContext string = "@odata.context"
	ODataFieldCount   string = "@odata.count"
	ODataFieldValue   string = "value"
)

Variables

View Source
var GlobalExpandTokenizer = ExpandTokenizer()
View Source
var GlobalSearchParser = SearchParser()
View Source
var GlobalSearchTokenizer = SearchTokenizer()

Functions

func DefineCustomFunctions added in v1.0.6

func DefineCustomFunctions(functions []CustomFunctionInput) error

DefineCustomFunctions introduces additional function names to be considered as legal function names while parsing. The function names must be different from all canonical functions and operators defined in the odata specification.

See https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_Functions

func ParseExpandOption

func ParseExpandOption(ctx context.Context, queue *tokenQueue, item *ExpandItem) error

func ParseName

func ParseName(segment string) string

func SemanticizeExpandQuery

func SemanticizeExpandQuery(
	expand *GoDataExpandQuery,
	service *GoDataService,
	entity *GoDataEntityType,
) error

func SemanticizeFilterQuery

func SemanticizeFilterQuery(
	filter *GoDataFilterQuery,
	service *GoDataService,
	entity *GoDataEntityType,
) error

func SemanticizeOrderByQuery

func SemanticizeOrderByQuery(orderby *GoDataOrderByQuery, service *GoDataService, entity *GoDataEntityType) error

func SemanticizePathSegment

func SemanticizePathSegment(segment *GoDataSegment, service *GoDataService) error

func SemanticizeSelectQuery

func SemanticizeSelectQuery(sel *GoDataSelectQuery, service *GoDataService, entity *GoDataEntityType) error

func SplitComputeItems added in v1.0.8

func SplitComputeItems(in string) ([]string, error)

SplitComputeItems splits the input string based on the comma delimiter. It does so with awareness as to which commas delimit $compute items and which ones are an inline part of the item, such as a separator for function arguments.

For example the input "someFunc(one,two) as three, 1 add 2 as four" results in the output ["someFunc(one,two) as three", "1 add 2 as four"]

func WithOdataComplianceConfig added in v1.0.5

func WithOdataComplianceConfig(ctx context.Context, cfg OdataComplianceConfig) context.Context

If the lenient mode is set, the 'failOnConfig' bits are used to determine the ODATA compliance. This is mostly for historical reasons because the original parser had compliance issues. If the lenient mode is not set, the parser returns an error.

Types

type ComputeItem added in v1.0.6

type ComputeItem struct {
	Tree  *ParseNode // The compute expression parsed as a tree.
	Field string     // The name of the computed dynamic property.
}

type CustomFunctionInput added in v1.0.6

type CustomFunctionInput struct {
	Name        string // case-insensitive function name
	NumParams   []int  // number of allowed parameters
	ReturnsBool bool   // indicates if the function has a boolean return value
}

CustomFunctionInput serves as input to function DefineCustomFunctions()

type DuplicateQueryParameterError

type DuplicateQueryParameterError struct {
	Parameter string
}

func (*DuplicateQueryParameterError) Error

func (err *DuplicateQueryParameterError) Error() string

type ExpandItem

type ExpandItem struct {
	Path    []*Token
	Filter  *GoDataFilterQuery
	At      *GoDataFilterQuery
	Search  *GoDataSearchQuery
	OrderBy *GoDataOrderByQuery
	Skip    *GoDataSkipQuery
	Top     *GoDataTopQuery
	Select  *GoDataSelectQuery
	Compute *GoDataComputeQuery
	Expand  *GoDataExpandQuery
	Levels  int
}

Represents an item to expand in an OData query. Tracks the path of the entity to expand and also the filter, levels, and reference options, etc.

func ParseExpandItem

func ParseExpandItem(ctx context.Context, input tokenQueue) (*ExpandItem, error)

func (*ExpandItem) AddExpandItem added in v1.0.6

func (o *ExpandItem) AddExpandItem(item *ExpandItem)

AddExpandItem adds an expand clause to 'o' creating a nested expand, ie $expand 'item' nested within $expand 'o'.

func (*ExpandItem) GetApply added in v1.0.6

func (o *ExpandItem) GetApply() *GoDataApplyQuery

func (*ExpandItem) GetAt added in v1.0.6

func (o *ExpandItem) GetAt() *GoDataFilterQuery

func (*ExpandItem) GetCompute added in v1.0.6

func (o *ExpandItem) GetCompute() *GoDataComputeQuery

func (*ExpandItem) GetCount added in v1.0.6

func (o *ExpandItem) GetCount() *GoDataCountQuery

func (*ExpandItem) GetExpand added in v1.0.6

func (o *ExpandItem) GetExpand() *GoDataExpandQuery

func (*ExpandItem) GetFilter added in v1.0.6

func (o *ExpandItem) GetFilter() *GoDataFilterQuery

ExpandItem implementation of GoDataCommonStructure interface

func (*ExpandItem) GetFormat added in v1.0.6

func (o *ExpandItem) GetFormat() *GoDataFormatQuery

func (*ExpandItem) GetInlineCount added in v1.0.6

func (o *ExpandItem) GetInlineCount() *GoDataInlineCountQuery

func (*ExpandItem) GetOrderBy added in v1.0.6

func (o *ExpandItem) GetOrderBy() *GoDataOrderByQuery

func (*ExpandItem) GetSearch added in v1.0.6

func (o *ExpandItem) GetSearch() *GoDataSearchQuery

func (*ExpandItem) GetSelect added in v1.0.6

func (o *ExpandItem) GetSelect() *GoDataSelectQuery

func (*ExpandItem) GetSkip added in v1.0.6

func (o *ExpandItem) GetSkip() *GoDataSkipQuery

func (*ExpandItem) GetTop added in v1.0.6

func (o *ExpandItem) GetTop() *GoDataTopQuery

type ExpandTokenType added in v1.0.5

type ExpandTokenType int
const (
	ExpandTokenOpenParen ExpandTokenType = iota
	ExpandTokenCloseParen
	ExpandTokenNav
	ExpandTokenComma
	ExpandTokenSemicolon
	ExpandTokenEquals
	ExpandTokenLiteral
)

func (ExpandTokenType) Value added in v1.0.5

func (e ExpandTokenType) Value() int

type ExpressionParser added in v1.0.5

type ExpressionParser struct {
	*Parser
	ExpectBoolExpr bool // Request expression to validate it is a boolean expression.
	// contains filtered or unexported fields
}

ExpressionParser is a ODATA expression parser.

var GlobalExpressionParser *ExpressionParser
var GlobalFilterParser *ExpressionParser

func NewExpressionParser added in v1.0.5

func NewExpressionParser() *ExpressionParser

func (*ExpressionParser) ParseExpressionString added in v1.0.5

func (p *ExpressionParser) ParseExpressionString(ctx context.Context, expression string) (*GoDataExpression, error)

ParseExpressionString converts a ODATA expression input string into a parse tree that can be used by providers to create a response. Expressions can be used within $filter and $orderby query options.

func (*ExpressionParser) ParseOrderByString added in v1.0.5

func (p *ExpressionParser) ParseOrderByString(ctx context.Context, orderby string) (*GoDataOrderByQuery, error)

The value of the $orderby System Query option contains a comma-separated list of expressions whose primitive result values are used to sort the items. The service MUST order by the specified property in ascending order. 4.01 services MUST support case-insensitive values for asc and desc.

func (*ExpressionParser) ParseSelectString added in v1.0.10

func (p *ExpressionParser) ParseSelectString(ctx context.Context, sel string) (*GoDataSelectQuery, error)

func (*ExpressionParser) SemanticizeExpression added in v1.0.5

func (p *ExpressionParser) SemanticizeExpression(
	expression *GoDataExpression,
	service *GoDataService,
	entity *GoDataEntityType,
) error

type ExpressionTokenType added in v1.0.5

type ExpressionTokenType int
const (
	ExpressionTokenOpenParen        ExpressionTokenType = iota // Open parenthesis - parenthesis expression, list expression, or path segment selector.
	ExpressionTokenCloseParen                                  // Close parenthesis
	ExpressionTokenWhitespace                                  // white space token
	ExpressionTokenNav                                         // Property navigation
	ExpressionTokenColon                                       // Function arg separator for 'any(v:boolExpr)' and 'all(v:boolExpr)' lambda operators
	ExpressionTokenComma                                       // [5] List delimiter and function argument delimiter.
	ExpressionTokenLogical                                     // eq|ne|gt|ge|lt|le|and|or|not|has|in
	ExpressionTokenOp                                          // add|sub|mul|divby|div|mod
	ExpressionTokenFunc                                        // Function, e.g. contains, substring...
	ExpressionTokenLambdaNav                                   // "/" token when used in lambda expression, e.g. tags/any()
	ExpressionTokenLambda                                      // [10] any(), all() lambda functions
	ExpressionTokenCase                                        // A case() statement. See https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_case
	ExpressionTokenCasePair                                    // A case statement expression pair [ <boolean expression> : <value expression> ]
	ExpressionTokenNull                                        //
	ExpressionTokenIt                                          // The '$it' token
	ExpressionTokenRoot                                        // [15] The '$root' token
	ExpressionTokenFloat                                       // A floating point value.
	ExpressionTokenInteger                                     // An integer value
	ExpressionTokenString                                      // SQUOTE *( SQUOTE-in-string / pchar-no-SQUOTE ) SQUOTE
	ExpressionTokenDate                                        // A date value
	ExpressionTokenTime                                        // [20] A time value
	ExpressionTokenDateTime                                    // A date-time value
	ExpressionTokenBoolean                                     // A literal boolean value
	ExpressionTokenLiteral                                     // A literal non-boolean value
	ExpressionTokenDuration                                    // duration      = [ "duration" ] SQUOTE durationValue SQUOTE
	ExpressionTokenGuid                                        // [25] A 128-bit GUID
	ExpressionTokenAssignement                                 // The '=' assignement for function arguments.
	ExpressionTokenGeographyPolygon                            //
	ExpressionTokenGeometryPolygon                             //

)

func (ExpressionTokenType) String added in v1.0.5

func (e ExpressionTokenType) String() string

func (ExpressionTokenType) Value added in v1.0.5

func (e ExpressionTokenType) Value() int

type Function

type Function struct {
	Token       string // The function token
	Params      []int  // The number of parameters this function accepts
	ReturnsBool bool   // Indicates if the function has a boolean return value
}

type GoDataAction

type GoDataAction struct {
	XMLName       xml.Name `xml:"Action"`
	Name          string   `xml:"Name,attr"`
	IsBound       string   `xml:"IsBound,attr,omitempty"`
	EntitySetPath string   `xml:"EntitySetPath,attr,omitempty"`
	Parameters    []*GoDataParameter
	ReturnType    *GoDataReturnType
}

type GoDataActionImport

type GoDataActionImport struct {
	XMLName   xml.Name `xml:"ActionImport"`
	Name      string   `xml:"Name,attr"`
	Action    string   `xml:"Action,attr"`
	EntitySet string   `xml:"EntitySet,attr,omitempty"`
}

type GoDataAnnotation

type GoDataAnnotation struct {
	XMLName   xml.Name `xml:"Annotation"`
	Term      string   `xml:"Term,attr"`
	Qualifier string   `xml:"Qualifier,attr,omitempty"`
}

type GoDataAnnotations

type GoDataAnnotations struct {
	XMLName     xml.Name `xml:"Annotations"`
	Target      string   `xml:"Target,attr"`
	Qualifier   string   `xml:"Qualifier,attr,omitempty"`
	Annotations []*GoDataAnnotation
}

type GoDataApplyQuery

type GoDataApplyQuery string

func ParseApplyString

func ParseApplyString(ctx context.Context, apply string) (*GoDataApplyQuery, error)

type GoDataCommonStructure added in v1.0.6

type GoDataCommonStructure interface {
	GetFilter() *GoDataFilterQuery
	GetAt() *GoDataFilterQuery
	GetApply() *GoDataApplyQuery
	GetExpand() *GoDataExpandQuery
	GetSelect() *GoDataSelectQuery
	GetOrderBy() *GoDataOrderByQuery
	GetTop() *GoDataTopQuery
	GetSkip() *GoDataSkipQuery
	GetCount() *GoDataCountQuery
	GetInlineCount() *GoDataInlineCountQuery
	GetSearch() *GoDataSearchQuery
	GetCompute() *GoDataComputeQuery
	GetFormat() *GoDataFormatQuery
	// AddExpandItem adds an item to the list of expand clauses in the underlying GoDataQuery/ExpandItem
	// structure.
	// AddExpandItem may be used to add items based on the requirements of the application using godata.
	// For example applications may support the introduction of dynamic navigational fields using $compute.
	// A possible implementation is to parse the request url using godata and then during semantic
	// post-processing identify dynamic navigation properties and call AddExpandItem to add them to the
	// list of expanded fields.
	AddExpandItem(*ExpandItem)
}

GoDataCommonStructure represents either a GoDataQuery or ExpandItem in a uniform manner as a Go interface. This allows the writing of functional logic that can work on either type, such as a provider implementation which starts at the GoDataQuery and walks any nested ExpandItem in an identical manner.

type GoDataComplexType

type GoDataComplexType struct {
	XMLName              xml.Name `xml:"ComplexType"`
	Name                 string   `xml:"Name,attr"`
	BaseType             string   `xml:"BaseType,attr,omitempty"`
	Abstract             string   `xml:"Abstract,attr,omitempty"`
	OpenType             string   `xml:"OpenType,attr,omitempty"`
	Properties           []*GoDataProperty
	NavigationProperties []*GoDataNavigationProperty
}

type GoDataComputeQuery added in v1.0.6

type GoDataComputeQuery struct {
	ComputeItems []*ComputeItem
	// The raw compute string
	RawValue string
}

func ParseComputeString added in v1.0.6

func ParseComputeString(ctx context.Context, compute string) (*GoDataComputeQuery, error)

type GoDataCountQuery

type GoDataCountQuery bool

func ParseCountString

func ParseCountString(ctx context.Context, count string) (*GoDataCountQuery, error)

type GoDataEntityContainer

type GoDataEntityContainer struct {
	XMLName         xml.Name `xml:"EntityContainer"`
	Name            string   `xml:"Name,attr"`
	Extends         string   `xml:"Extends,attr,omitempty"`
	EntitySets      []*GoDataEntitySet
	Singletons      []*GoDataSingleton
	ActionImports   []*GoDataActionImport
	FunctionImports []*GoDataFunctionImport
}

type GoDataEntitySet

type GoDataEntitySet struct {
	XMLName                    xml.Name `xml:"EntitySet"`
	Name                       string   `xml:"Name,attr"`
	EntityType                 string   `xml:"EntityType,attr"`
	IncludeInServiceDocument   string   `xml:"IncludeInServiceDocument,attr,omitempty"`
	NavigationPropertyBindings []*GoDataNavigationPropertyBinding
}

type GoDataEntityType

type GoDataEntityType struct {
	XMLName              xml.Name `xml:"EntityType"`
	Name                 string   `xml:"Name,attr"`
	BaseType             string   `xml:"BaseType,attr,omitempty"`
	Abstract             string   `xml:"Abstract,attr,omitempty"`
	OpenType             string   `xml:"OpenType,attr,omitempty"`
	HasStream            string   `xml:"HasStream,attr,omitempty"`
	Key                  *GoDataKey
	Properties           []*GoDataProperty
	NavigationProperties []*GoDataNavigationProperty
}

type GoDataEnumType

type GoDataEnumType struct {
	XMLName        xml.Name `xml:"EnumType"`
	Name           string   `xml:"Name,attr"`
	UnderlyingType string   `xml:"UnderlyingType,attr,omitempty"`
	IsFlags        string   `xml:"IsFlags,attr,omitempty"`
	Members        []*GoDataMember
}

type GoDataError

type GoDataError struct {
	ResponseCode int
	Message      string
	Cause        error
}

func BadRequestError

func BadRequestError(message string) *GoDataError

func GoneError

func GoneError(message string) *GoDataError

func InternalServerError

func InternalServerError(message string) *GoDataError

func MethodNotAllowedError

func MethodNotAllowedError(message string) *GoDataError

func NotFoundError

func NotFoundError(message string) *GoDataError

func NotImplementedError

func NotImplementedError(message string) *GoDataError

func PreconditionFailedError

func PreconditionFailedError(message string) *GoDataError

func (*GoDataError) Error

func (err *GoDataError) Error() string

func (*GoDataError) SetCause

func (err *GoDataError) SetCause(e error) *GoDataError

func (*GoDataError) Unwrap

func (err *GoDataError) Unwrap() error

type GoDataExpandQuery

type GoDataExpandQuery struct {
	ExpandItems []*ExpandItem
}

func ParseExpandString

func ParseExpandString(ctx context.Context, expand string) (*GoDataExpandQuery, error)

type GoDataExpression added in v1.0.5

type GoDataExpression struct {
	Tree *ParseNode
	// The raw string representing an expression
	RawValue string
}

GoDataExpression encapsulates the tree representation of an expression as defined in the OData ABNF grammar.

type GoDataFilterQuery

type GoDataFilterQuery struct {
	Tree *ParseNode
	// The raw filter string
	RawValue string
}

Stores a parsed version of the filter query string. Can be used by providers to apply the filter based on their own implementation. The filter is stored as a parse tree that can be traversed.

func ParseFilterString

func ParseFilterString(ctx context.Context, filter string) (*GoDataFilterQuery, error)

ParseFilterString converts an input string from the $filter part of the URL into a parse tree that can be used by providers to create a response.

type GoDataFormatQuery

type GoDataFormatQuery struct {
}

type GoDataFunction

type GoDataFunction struct {
	XMLName       xml.Name `xml:"Function"`
	Name          string   `xml:"Name,attr"`
	IsBound       string   `xml:"IsBound,attr,omitempty"`
	IsComposable  string   `xml:"IsComposable,attr,omitempty"`
	EntitySetPath string   `xml:"EntitySetPath,attr,omitempty"`
	Parameters    []*GoDataParameter
	ReturnType    *GoDataReturnType
}

type GoDataFunctionImport

type GoDataFunctionImport struct {
	XMLName                  xml.Name `xml:"FunctionImport"`
	Name                     string   `xml:"Name,attr"`
	Function                 string   `xml:"Function,attr"`
	EntitySet                string   `xml:"EntitySet,attr,omitempty"`
	IncludeInServiceDocument string   `xml:"IncludeInServiceDocument,attr,omitempty"`
}

type GoDataIdentifier

type GoDataIdentifier map[string]string

func ParseIdentifiers

func ParseIdentifiers(segment string) *GoDataIdentifier

func (*GoDataIdentifier) Get

func (id *GoDataIdentifier) Get() string

Return the first key in the map. This is how you should get the identifier for single values, e.g. when the path is Employee(1), etc.

func (*GoDataIdentifier) GetKey

func (id *GoDataIdentifier) GetKey(key string) (string, bool)

Return a specific value for a specific key.

func (*GoDataIdentifier) HasMultiple

func (id *GoDataIdentifier) HasMultiple() bool

Check if this identifier has more than one key/value pair.

type GoDataInclude

type GoDataInclude struct {
	XMLName   xml.Name `xml:"edmx:Include"`
	Namespace string   `xml:"Namespace,attr"`
	Alias     string   `xml:"Alias,attr,omitempty"`
}

type GoDataIncludeAnnotations

type GoDataIncludeAnnotations struct {
	XMLName         xml.Name `xml:"edmx:IncludeAnnotations"`
	TermNamespace   string   `xml:"TermNamespace,attr"`
	Qualifier       string   `xml:"Qualifier,attr,omitempty"`
	TargetNamespace string   `xml:"TargetNamespace,attr,omitempty"`
}

type GoDataInlineCountQuery

type GoDataInlineCountQuery string

func ParseInlineCountString

func ParseInlineCountString(ctx context.Context, inlinecount string) (*GoDataInlineCountQuery, error)

type GoDataKey

type GoDataKey struct {
	XMLName     xml.Name `xml:"Key"`
	PropertyRef *GoDataPropertyRef
}

type GoDataMember

type GoDataMember struct {
	XMLName xml.Name `xml:"Member"`
	Name    string   `xml:"Name,attr"`
	Value   string   `xml:"Value,attr,omitempty"`
}

type GoDataMetadata

type GoDataMetadata struct {
	XMLName      xml.Name `xml:"edmx:Edmx"`
	XMLNamespace string   `xml:"xmlns:edmx,attr"`
	Version      string   `xml:"Version,attr"`
	DataServices *GoDataServices
	References   []*GoDataReference
}

func (*GoDataMetadata) Bytes

func (t *GoDataMetadata) Bytes() ([]byte, error)

func (*GoDataMetadata) String

func (t *GoDataMetadata) String() string

type GoDataNavigationProperty

type GoDataNavigationProperty struct {
	XMLName                xml.Name `xml:"NavigationProperty"`
	Name                   string   `xml:"Name,attr"`
	Type                   string   `xml:"Type,attr"`
	Nullable               string   `xml:"Nullable,attr,omitempty"`
	Partner                string   `xml:"Partner,attr,omitempty"`
	ContainsTarget         string   `xml:"ContainsTarget,attr,omitempty"`
	ReferentialConstraints []*GoDataReferentialConstraint
}

type GoDataNavigationPropertyBinding

type GoDataNavigationPropertyBinding struct {
	XMLName xml.Name `xml:"NavigationPropertyBinding"`
	Path    string   `xml:"Path,attr"`
	Target  string   `xml:"Target,attr"`
}

type GoDataOnDelete

type GoDataOnDelete struct {
	XMLName xml.Name `xml:"OnDelete"`
	Action  string   `xml:"Action,attr"`
}

type GoDataOrderByQuery

type GoDataOrderByQuery struct {
	OrderByItems []*OrderByItem
	// The raw orderby string
	RawValue string
}

func ParseOrderByString

func ParseOrderByString(ctx context.Context, orderby string) (*GoDataOrderByQuery, error)

type GoDataParameter

type GoDataParameter struct {
	XMLName   xml.Name `xml:"Parameter"`
	Name      string   `xml:"Name,attr"`
	Type      string   `xml:"Type,attr"`
	Nullable  string   `xml:"Nullable,attr,omitempty"`
	MaxLength int      `xml:"MaxLength,attr,omitempty"`
	Precision int      `xml:"Precision,attr,omitempty"`
	Scale     int      `xml:"Scale,attr,omitempty"`
	SRID      string   `xml:"SRID,attr,omitempty"`
}

type GoDataProperty

type GoDataProperty struct {
	XMLName      xml.Name `xml:"Property"`
	Name         string   `xml:"Name,attr"`
	Type         string   `xml:"Type,attr"`
	Nullable     string   `xml:"Nullable,attr,omitempty"`
	MaxLength    int      `xml:"MaxLength,attr,omitempty"`
	Precision    int      `xml:"Precision,attr,omitempty"`
	Scale        int      `xml:"Scale,attr,omitempty"`
	Unicode      string   `xml:"Unicode,attr,omitempty"`
	SRID         string   `xml:"SRID,attr,omitempty"`
	DefaultValue string   `xml:"DefaultValue,attr,omitempty"`
}

type GoDataPropertyRef

type GoDataPropertyRef struct {
	XMLName xml.Name `xml:"PropertyRef"`
	Name    string   `xml:"Name,attr"`
}

type GoDataProvider

type GoDataProvider interface {
	// Request a single entity from the provider. Should return a response field
	// that contains the value mapping properties to values for the entity.
	GetEntity(*GoDataRequest) (*GoDataResponseField, error)
	// Request a collection of entities from the provider. Should return a
	// response field that contains the value of a slice of every entity in the
	// collection filtered by the request query parameters.
	GetEntityCollection(*GoDataRequest) (*GoDataResponseField, error)
	// Request the number of entities in a collection, disregarding any filter
	// query parameters.
	GetCount(*GoDataRequest) (int, error)
	// Get the object model representation from the provider.
	GetMetadata() *GoDataMetadata
}

The basic interface for a GoData provider. All providers must implement these functions.

type GoDataQuery

func (*GoDataQuery) AddExpandItem added in v1.0.6

func (o *GoDataQuery) AddExpandItem(item *ExpandItem)

AddExpandItem adds an expand clause to the toplevel odata request structure 'o'.

func (*GoDataQuery) GetApply added in v1.0.6

func (o *GoDataQuery) GetApply() *GoDataApplyQuery

func (*GoDataQuery) GetAt added in v1.0.6

func (o *GoDataQuery) GetAt() *GoDataFilterQuery

func (*GoDataQuery) GetCompute added in v1.0.6

func (o *GoDataQuery) GetCompute() *GoDataComputeQuery

func (*GoDataQuery) GetCount added in v1.0.6

func (o *GoDataQuery) GetCount() *GoDataCountQuery

func (*GoDataQuery) GetExpand added in v1.0.6

func (o *GoDataQuery) GetExpand() *GoDataExpandQuery

func (*GoDataQuery) GetFilter added in v1.0.6

func (o *GoDataQuery) GetFilter() *GoDataFilterQuery

GoDataQuery implementation of GoDataCommonStructure interface

func (*GoDataQuery) GetFormat added in v1.0.6

func (o *GoDataQuery) GetFormat() *GoDataFormatQuery

func (*GoDataQuery) GetInlineCount added in v1.0.6

func (o *GoDataQuery) GetInlineCount() *GoDataInlineCountQuery

func (*GoDataQuery) GetOrderBy added in v1.0.6

func (o *GoDataQuery) GetOrderBy() *GoDataOrderByQuery

func (*GoDataQuery) GetSearch added in v1.0.6

func (o *GoDataQuery) GetSearch() *GoDataSearchQuery

func (*GoDataQuery) GetSelect added in v1.0.6

func (o *GoDataQuery) GetSelect() *GoDataSelectQuery

func (*GoDataQuery) GetSkip added in v1.0.6

func (o *GoDataQuery) GetSkip() *GoDataSkipQuery

func (*GoDataQuery) GetTop added in v1.0.6

func (o *GoDataQuery) GetTop() *GoDataTopQuery

type GoDataReference

type GoDataReference struct {
	XMLName            xml.Name `xml:"edmx:Reference"`
	Uri                string   `xml:"Uri,attr"`
	Includes           []*GoDataInclude
	IncludeAnnotations []*GoDataIncludeAnnotations
}

type GoDataReferentialConstraint

type GoDataReferentialConstraint struct {
	XMLName            xml.Name        `xml:"ReferentialConstraint"`
	Property           string          `xml:"Property,attr"`
	ReferencedProperty string          `xml:"ReferencedProperty,attr"`
	OnDelete           *GoDataOnDelete `xml:"OnDelete,omitempty"`
}

type GoDataRequest

type GoDataRequest struct {
	FirstSegment *GoDataSegment
	LastSegment  *GoDataSegment
	Query        *GoDataQuery
	RequestKind  RequestKind
}

func ParseRequest

func ParseRequest(ctx context.Context, path string, query url.Values) (*GoDataRequest, error)

Parse a request from the HTTP server and format it into a GoDaataRequest type to be passed to a provider to produce a result.

func (*GoDataRequest) ParseUrlPath added in v1.0.5

func (req *GoDataRequest) ParseUrlPath(path string) error

func (*GoDataRequest) ParseUrlQuery added in v1.0.5

func (req *GoDataRequest) ParseUrlQuery(ctx context.Context, query url.Values) error

ParseUrlQuery parses the URL query, applying optional logic specified in the context.

func (*GoDataRequest) SemanticizeRequest added in v1.0.5

func (req *GoDataRequest) SemanticizeRequest(service *GoDataService) error

Compare a request to a given service, and validate the semantics and update the request with semantics included

type GoDataResponse

type GoDataResponse struct {
	Fields map[string]*GoDataResponseField
}

A response is a dictionary of keys to their corresponding fields. This will be converted into a JSON dictionary in the response to the web client.

func (*GoDataResponse) Json

func (r *GoDataResponse) Json() ([]byte, error)

Serialize the result as JSON for sending to the client. If an error occurs during the serialization, it will be returned.

type GoDataResponseField

type GoDataResponseField struct {
	Value interface{}
}

A response that is a primitive JSON type or a list or a dictionary. When writing to JSON, it is automatically mapped from the Go type to a suitable JSON data type. Any type can be used, but if the data type is not supported for serialization, then an error is thrown.

func (*GoDataResponseField) Json

func (f *GoDataResponseField) Json() ([]byte, error)

Convert the response field to a JSON serialized form. If the type is not string, []byte, int, float64, map[string]*GoDataResponseField, or []*GoDataResponseField, then an error will be thrown.

type GoDataReturnType

type GoDataReturnType struct {
	XMLName   xml.Name `xml:"ReturnType"`
	Name      string   `xml:"Name,attr"`
	Type      string   `xml:"Type,attr"`
	Nullable  string   `xml:"Nullable,attr,omitempty"`
	MaxLength int      `xml:"MaxLength,attr,omitempty"`
	Precision int      `xml:"Precision,attr,omitempty"`
	Scale     int      `xml:"Scale,attr,omitempty"`
	SRID      string   `xml:"SRID,attr,omitempty"`
}

type GoDataSchema

type GoDataSchema struct {
	XMLName          xml.Name `xml:"Schema"`
	Namespace        string   `xml:"Namespace,attr"`
	Alias            string   `xml:"Alias,attr,omitempty"`
	Actions          []*GoDataAction
	Annotations      []*GoDataAnnotations
	Annotation       []*GoDataAnnotation
	ComplexTypes     []*GoDataComplexType
	EntityContainers []*GoDataEntityContainer
	EntityTypes      []*GoDataEntityType
	EnumTypes        []*GoDataEnumType
	Functions        []*GoDataFunction
	Terms            []*GoDataTerm
	TypeDefinitions  []*GoDataTypeDefinition
}

type GoDataSearchQuery

type GoDataSearchQuery struct {
	Tree *ParseNode
	// The raw search string
	RawValue string
}

func ParseSearchString

func ParseSearchString(ctx context.Context, filter string) (*GoDataSearchQuery, error)

Convert an input string from the $filter part of the URL into a parse tree that can be used by providers to create a response.

type GoDataSegment

type GoDataSegment struct {
	// The raw segment parsed from the URI
	RawValue string

	// The kind of resource being pointed at by this segment
	SemanticType SemanticType

	// A pointer to the metadata type this object represents, as defined by a
	// particular service
	SemanticReference interface{}

	// The name of the entity, type, collection, etc.
	Name string

	// map[string]string of identifiers passed to this segment. If the identifier
	// is not key/value pair(s), then all values will be nil. If there is no
	// identifier, it will be nil.
	Identifier *GoDataIdentifier

	// The next segment in the path.
	Next *GoDataSegment
	// The previous segment in the path.
	Prev *GoDataSegment
}

Represents a segment (slash-separated) part of the URI path. Each segment has a link to the next segment (the last segment precedes nil).

type GoDataSelectQuery

type GoDataSelectQuery struct {
	SelectItems []*SelectItem
	// The raw select string
	RawValue string
}

func ParseSelectString

func ParseSelectString(ctx context.Context, sel string) (*GoDataSelectQuery, error)

type GoDataService

type GoDataService struct {
	// The base url for the service. Navigating to this URL will display the
	// service document.
	BaseUrl *url.URL
	// The provider for this service that is serving the data to the OData API.
	Provider GoDataProvider
	// Metadata cache taken from the provider.
	Metadata *GoDataMetadata
	// A mapping from schema names to schema references
	SchemaLookup map[string]*GoDataSchema
	// A bottom-up mapping from entity type names to schema namespaces to
	// the entity type reference
	EntityTypeLookup map[string]map[string]*GoDataEntityType
	// A bottom-up mapping from entity container names to schema namespaces to
	// the entity container reference
	EntityContainerLookup map[string]map[string]*GoDataEntityContainer
	// A bottom-up mapping from entity set names to entity collection names to
	// schema namespaces to the entity set reference
	EntitySetLookup map[string]map[string]map[string]*GoDataEntitySet
	// A lookup for entity properties if an entity type is given, lookup
	// properties by name
	PropertyLookup map[*GoDataEntityType]map[string]*GoDataProperty
	// A lookup for navigational properties if an entity type is given,
	// lookup navigational properties by name
	NavigationPropertyLookup map[*GoDataEntityType]map[string]*GoDataNavigationProperty
}

A GoDataService will spawn an HTTP listener, which will connect GoData requests with a backend provider given to it.

func BuildService

func BuildService(provider GoDataProvider, serviceUrl string) (*GoDataService, error)

Create a new service from a given provider. This step builds lookups for all parts of the data model, so constant time lookups can be performed. This step only happens once when the server starts up, so the overall cost is minimal. The given url will be treated as the base URL for all service requests, and used for building context URLs, etc.

func (*GoDataService) GoDataHTTPHandler

func (service *GoDataService) GoDataHTTPHandler(w http.ResponseWriter, r *http.Request)

The default handler for parsing requests as GoDataRequests, passing them to a GoData provider, and then building a response.

func (*GoDataService) ListenAndServe

func (service *GoDataService) ListenAndServe(addr string)

Start the service listening on the given address.

func (*GoDataService) LookupEntitySet

func (service *GoDataService) LookupEntitySet(name string) (*GoDataEntitySet, error)

Lookup an entity set from the service metadata. Accepts a fully qualified name, e.g., ODataService.ContainerName.EntitySetName, ContainerName.EntitySetName or, if unambiguous, accepts a simple identifier, e.g., EntitySetName.

func (*GoDataService) LookupEntityType

func (service *GoDataService) LookupEntityType(name string) (*GoDataEntityType, error)

Lookup an entity type from the service metadata. Accepts a fully qualified name, e.g., ODataService.EntityTypeName or, if unambiguous, accepts a simple identifier, e.g., EntityTypeName.

type GoDataServices

type GoDataServices struct {
	XMLName xml.Name `xml:"edmx:DataServices"`
	Schemas []*GoDataSchema
}

type GoDataSingleton

type GoDataSingleton struct {
	XMLName                    xml.Name `xml:"Singleton"`
	Name                       string   `xml:"Name,attr"`
	Type                       string   `xml:"Type,attr"`
	NavigationPropertyBindings []*GoDataNavigationPropertyBinding
}

type GoDataSkipQuery

type GoDataSkipQuery int

func ParseSkipString

func ParseSkipString(ctx context.Context, skip string) (*GoDataSkipQuery, error)

type GoDataTerm

type GoDataTerm struct {
	XMLName      xml.Name `xml:"Term"`
	Name         string   `xml:"Name,attr"`
	Type         string   `xml:"Type,attr"`
	BaseTerm     string   `xml:"BaseTerm,attr,omitempty"`
	DefaultValue string   `xml:"DefaultValue,attr,omitempty"`
	AppliesTo    string   `xml:"AppliesTo,attr,omitempty"`
}

type GoDataTopQuery

type GoDataTopQuery int

func ParseTopString

func ParseTopString(ctx context.Context, top string) (*GoDataTopQuery, error)

type GoDataTypeDefinition

type GoDataTypeDefinition struct {
	XMLName        xml.Name `xml:"TypeDefinition"`
	Name           string   `xml:"Name,attr"`
	UnderlyingType string   `xml:"UnderlyingTypeattr,omitempty"`
	Annotations    []*GoDataAnnotation
}

type ListExprToken added in v1.0.5

type ListExprToken int
const (
	// TokenTypeListExpr represents a parent node for a variadic listExpr.
	// "list"
	//   "item1"
	//   "item2"
	//   ...
	TokenTypeListExpr ListExprToken = iota
	// TokenTypeArgCount is used to specify the number of arguments of a function or listExpr
	// This is used to handle variadic functions and listExpr.
	TokenTypeArgCount
)

func (ListExprToken) String added in v1.0.5

func (l ListExprToken) String() string

func (ListExprToken) Value added in v1.0.5

func (l ListExprToken) Value() int

type OdataComplianceConfig added in v1.0.5

type OdataComplianceConfig int
const (
	ComplianceStrict OdataComplianceConfig = 0
	// Ignore duplicate ODATA keywords in the URL query.
	ComplianceIgnoreDuplicateKeywords OdataComplianceConfig = 1 << iota
	// Ignore unknown ODATA keywords in the URL query.
	ComplianceIgnoreUnknownKeywords
	// Ignore extraneous comma as the last character in a list of function arguments.
	ComplianceIgnoreInvalidComma
	ComplianceIgnoreAll OdataComplianceConfig = ComplianceIgnoreDuplicateKeywords |
		ComplianceIgnoreUnknownKeywords |
		ComplianceIgnoreInvalidComma
)

type Operator

type Operator struct {
	Token string
	// Whether the operator is left/right/or not associative.
	// Determines how operators of the same precedence are grouped in the absence of parentheses.
	Association int
	// The number of operands this operator operates on
	Operands int
	// Rank of precedence. A higher value indicates higher precedence.
	Precedence int
	// Determine if the operands should be interpreted as a ListExpr or parenExpr according
	// to ODATA ABNF grammar.
	// This is only used when a listExpr has zero or one items.
	// When a listExpr has 2 or more items, there is no ambiguity between listExpr and parenExpr.
	// For example:
	//    2 + (3) ==> the right operand is a parenExpr.
	//    City IN ('Seattle', 'Atlanta') ==> the right operand is unambiguously a listExpr.
	//    City IN ('Seattle') ==> the right operand should be a listExpr.
	PreferListExpr bool
}

func (*Operator) WithListExprPreference added in v1.0.5

func (o *Operator) WithListExprPreference(v bool) *Operator

type OrderByItem

type OrderByItem struct {
	Field *Token            // The raw value of the orderby field or expression.
	Tree  *GoDataExpression // The orderby expression parsed as a tree.
	Order string            // Ascending or descending order.
}

type ParseNode

type ParseNode struct {
	Token    *Token
	Parent   *ParseNode
	Children []*ParseNode
}

func (*ParseNode) String

func (p *ParseNode) String() string

type Parser

type Parser struct {
	// Map from string inputs to operator types
	Operators map[string]*Operator
	// Map from string inputs to function types
	Functions map[string]*Function

	LiteralToken TokenType
}

func EmptyParser

func EmptyParser() *Parser

func SearchParser

func SearchParser() *Parser

func (*Parser) DefineFunction

func (p *Parser) DefineFunction(token string, params []int, returnsBool bool) *Function

DefineFunction adds a function to the language. - params is the number of parameters this function accepts - returnsBool indicates if the function has a boolean return value

func (*Parser) DefineOperator

func (p *Parser) DefineOperator(token string, operands, assoc, precedence int) *Operator

DefineOperator adds an operator to the language. Provide the token, the expected number of arguments, whether the operator is left, right, or not associative, and a precedence.

func (*Parser) InfixToPostfix

func (p *Parser) InfixToPostfix(ctx context.Context, tokens []*Token) (*tokenQueue, error)

InfixToPostfix parses the input string of tokens using the given definitions of operators and functions. Everything else is assumed to be a literal. Uses the Shunting-Yard algorithm.

Infix notation for variadic functions and operators: f ( a, b, c, d ) Postfix notation with wall notation: | a b c d f Postfix notation with count notation: a b c d 4 f

func (*Parser) PostfixToTree

func (p *Parser) PostfixToTree(ctx context.Context, queue *tokenQueue) (*ParseNode, error)

PostfixToTree converts a Postfix token queue to a parse tree

func (*Parser) WithLiteralToken added in v1.0.5

func (p *Parser) WithLiteralToken(token TokenType) *Parser

type RequestKind added in v1.0.5

type RequestKind int
const (
	RequestKindUnknown RequestKind = iota
	RequestKindMetadata
	RequestKindService
	RequestKindEntity
	RequestKindCollection
	RequestKindSingleton
	RequestKindProperty
	RequestKindPropertyValue
	RequestKindRef
	RequestKindCount
)

type SearchTokenType added in v1.0.5

type SearchTokenType int
const (
	SearchTokenLiteral SearchTokenType = iota
	SearchTokenOpenParen
	SearchTokenCloseParen
	SearchTokenOp
	SearchTokenWhitespace
)

func (SearchTokenType) Value added in v1.0.5

func (s SearchTokenType) Value() int

type SelectItem

type SelectItem struct {
	Segments []*Token
}

type SemanticType

type SemanticType int
const (
	SemanticTypeUnknown SemanticType = iota
	SemanticTypeEntity
	SemanticTypeEntitySet
	SemanticTypeDerivedEntity
	SemanticTypeAction
	SemanticTypeFunction
	SemanticTypeProperty
	SemanticTypePropertyValue
	SemanticTypeRef
	SemanticTypeCount
	SemanticTypeMetadata
)

type Token

type Token struct {
	Value string
	Type  TokenType
	// Holds information about the semantic meaning of this token taken from the
	// context of the GoDataService.
	SemanticType      SemanticType
	SemanticReference interface{}
}

type TokenHandler added in v1.0.5

type TokenHandler func(token *Token, stack tokenStack) error

type TokenMatcher

type TokenMatcher struct {
	Pattern         string                 // The regular expression matching a ODATA query token, such as literal value, operator or function
	Re              *regexp.Regexp         // The compiled regex
	Token           TokenType              // The token identifier
	CaseInsensitive bool                   // Regex is case-insensitive
	Subst           func(in string) string // A function that substitutes the raw input token with another representation. By default it is the identity.
}

type TokenType added in v1.0.5

type TokenType interface {
	Value() int
}

TokenType is the interface that must be implemented by all token types.

type Tokenizer

type Tokenizer struct {
	TokenMatchers  []*TokenMatcher
	IgnoreMatchers []*TokenMatcher
}
var GlobalAllTokenParser *Tokenizer

GlobalAllTokenParser is a Tokenizer which matches all tokens and ignores none. It differs from the GlobalExpressionTokenizer which ignores whitespace tokens.

var GlobalExpressionTokenizer *Tokenizer
var GlobalFilterTokenizer *Tokenizer

func ExpandTokenizer

func ExpandTokenizer() *Tokenizer

func NewExpressionTokenizer added in v1.0.5

func NewExpressionTokenizer() *Tokenizer

ExpressionTokenizer creates a tokenizer capable of tokenizing ODATA expressions. 4.01 Services MUST support case-insensitive operator names. See https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc31360955

func SearchTokenizer

func SearchTokenizer() *Tokenizer

Create a tokenizer capable of tokenizing filter statements

func (*Tokenizer) Add

func (t *Tokenizer) Add(pattern string, token TokenType)

func (*Tokenizer) AddWithSubstituteFunc

func (t *Tokenizer) AddWithSubstituteFunc(pattern string, token TokenType, subst func(string) string)

func (*Tokenizer) Ignore

func (t *Tokenizer) Ignore(pattern string, token TokenType)

func (*Tokenizer) Tokenize

func (t *Tokenizer) Tokenize(ctx context.Context, target string) ([]*Token, error)

func (*Tokenizer) TokenizeBytes

func (t *Tokenizer) TokenizeBytes(ctx context.Context, target []byte) ([]*Token, error)

TokenizeBytes takes the input byte array and returns an array of tokens. Return an empty array if there are no tokens.

type UnsupportedQueryParameterError

type UnsupportedQueryParameterError struct {
	Parameter string
}

func (*UnsupportedQueryParameterError) Error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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