Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
27pchrisl committed Nov 5, 2020
2 parents de8c7a4 + 6c42bc5 commit 73c24ab
Show file tree
Hide file tree
Showing 20 changed files with 399 additions and 85 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,10 +492,10 @@ The relevant parts of the specification used for Lodata are:
Lodata supports many sections of the OData specification, these are the major areas of support:

* Publishing a [service document](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31358840) at the service root
* Adding custom annotations
* Adding custom [annotations](https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#sec_Annotation)
* Strict type model for primitive types, supporting Eloquent casts and getter/setters
* Returning data according to the [OData-JSON](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html) specification
* Streaming JSON support
* [Streaming JSON](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_PayloadOrderingConstraints) support
* Using [server-driven-pagination](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#sec_ServerDrivenPaging) when returning partial results
* The [$expand](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#sec_SystemQueryOptionexpand) system query option
* The [$select](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31358942) system query option
Expand All @@ -504,17 +504,18 @@ Lodata supports many sections of the OData specification, these are the major ar
* The [$skip](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31358954) system query option
* The [$count](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31358955) system query option
* The [$search](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31358956) system query option
* The $value path segment
* The [$value](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31358940) path segment
* The [$filter](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31358948) system query option, with all expressions, functions, operators, and supports query parameter aliases
* [Asynchronous requests](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#sec_AsynchronousRequests) using Laravel jobs, with monitoring, cancellation and callbacks
* Edit links, and POST/PATCH/DELETE requests for new or existing entities
* Composable URLs
* Declared and navigation properties
* Referential constraints
* Entity singletons
* IEEE754 number-as-string support
* Full, minimal and no metadata requests
* Function and Action operations, including bound operations and inline parameters
* Requesting [entity references](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_EntityReference)
* [IEEE754](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_ControllingtheRepresentationofNumber) number-as-string support
* Full, minimal and no [metadata](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#_Toc38457725) requests
* Function and Action [operations](https://docs.oasis-open.org/odata/odata/v4.01/os/part1-protocol/odata-v4.01-os-part1-protocol.html#_Toc31359005), including bound operations and inline parameters
* Automatic discovery of PDO or Eloquent model tables, and relationships between Eloquent models
* All database backends that Laravel supports (MySQL, PostgreSQL, SQLite and Microsoft SQL Server) including all possible $filter expressions
* Automatic discovery of OData feeds by PowerBI (using [PBIDS](https://docs.microsoft.com/en-us/power-bi/connect-data/desktop-data-sources#using-pbids-files-to-get-data)) and Excel (using [ODCFF](https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-odcff/09a237b3-a761-4847-a54c-eb665f5b0a6e))
Expand Down
1 change: 1 addition & 0 deletions src/Controller/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class Transaction implements ArgumentInterface
PathSegment\Value::class,
PathSegment\Count::class,
PathSegment\Filter::class,
PathSegment\Reference::class,
Operation::class,
Singleton::class,
PropertyValue::class,
Expand Down
59 changes: 37 additions & 22 deletions src/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
use Flat3\Lodata\Interfaces\EntityTypeInterface;
use Flat3\Lodata\Interfaces\Operation\ArgumentInterface;
use Flat3\Lodata\Interfaces\PipeInterface;
use Flat3\Lodata\Interfaces\ReferenceInterface;
use Flat3\Lodata\Interfaces\ResourceInterface;
use Flat3\Lodata\Traits\UseReferences;
use Flat3\Lodata\Traits\HasTransaction;
use Flat3\Lodata\Transaction\MetadataContainer;
use Flat3\Lodata\Transaction\NavigationRequest;

class Entity implements ResourceInterface, EntityTypeInterface, ContextInterface, ArrayAccess, EmitInterface, PipeInterface, ArgumentInterface
class Entity implements ResourceInterface, ReferenceInterface, EntityTypeInterface, ContextInterface, ArrayAccess, EmitInterface, PipeInterface, ArgumentInterface
{
use UseReferences;
use HasTransaction;

/** @var ObjectArray $properties */
Expand All @@ -33,7 +37,8 @@ class Entity implements ResourceInterface, EntityTypeInterface, ContextInterface
/** @var EntityType $type */
private $type;

protected $metadata = [];
/** @var MetadataContainer $metadata */
protected $metadata = null;

public function __construct()
{
Expand Down Expand Up @@ -98,25 +103,37 @@ public function emit(Transaction $transaction): void

$transaction->outputJsonObjectStart();

$metadata = $this->metadata;

$metadata = $this->metadata ?: $transaction->getMetadata()->getContainer();
$metadata['type'] = '#'.$this->getType()->getIdentifier();

if ($this->entitySet && $this->getEntityId()) {
$metadata['id'] = $this->getResourceUrl($transaction);

if ($this->usesReferences()) {
$metadata['id'] = sprintf(
"%s(%s)",
$this->entitySet->getName(),
$this->getEntityId()->getValue()->get()
);

$metadata->addRequiredProperty('id');
}

$metadata['readLink'] = $metadata['id'];
}

$metadata = $transaction->getMetadata()->filter($metadata);

$requiresSeparator = false;

if ($metadata) {
$transaction->outputJsonKV($metadata);
if ($metadata->hasMetadata()) {
$transaction->outputJsonKV($metadata->getMetadata());
$requiresSeparator = true;
}

while (true) {
if ($this->usesReferences()) {
break;
}

if (!$this->properties->valid()) {
break;
}
Expand All @@ -136,8 +153,8 @@ public function emit(Transaction $transaction): void
if ($propertyValue->getProperty() instanceof NavigationProperty) {
$propertyMetadata = $this->getExpansionMetadata($transaction, $propertyValue);

if ($propertyMetadata) {
$transaction->outputJsonKV($propertyMetadata);
if ($propertyMetadata->hasMetadata()) {
$transaction->outputJsonKV($propertyMetadata->getMetadata());
$transaction->outputJsonSeparator();
}
}
Expand Down Expand Up @@ -230,11 +247,11 @@ public function offsetUnset($offset)
$this->properties->drop($offset);
}

public function getExpansionMetadata(Transaction $transaction, PropertyValue $propertyValue)
public function getExpansionMetadata(Transaction $transaction, PropertyValue $propertyValue): MetadataContainer
{
$propertyMetadata = [
'navigationLink' => $this->getResourceUrl($transaction).'/'.$propertyValue->getProperty()->getName(),
];
$propertyMetadata = $transaction->getMetadata()->getContainer();
$propertyMetadata->setPrefix($propertyValue->getProperty()->getName());
$propertyMetadata['navigationLink'] = $this->getResourceUrl($transaction).'/'.$propertyValue->getProperty()->getName();

if ($propertyValue->getValue() instanceof EntitySet) {
$set = $propertyValue->getEntitySetValue();
Expand Down Expand Up @@ -267,11 +284,6 @@ public function getExpansionMetadata(Transaction $transaction, PropertyValue $pr
}
}

$propertyMetadata = $transaction->getMetadata()->filter(
$propertyMetadata,
$propertyValue->getProperty()->getName()
);

return $propertyMetadata;
}

Expand All @@ -286,6 +298,10 @@ public static function pipe(

public function getContextUrl(Transaction $transaction): string
{
if ($this->usesReferences()) {
return $transaction->getContextUrl().'#$ref';
}

if ($this->entitySet) {
$url = $this->entitySet->getContextUrl($transaction);

Expand Down Expand Up @@ -334,9 +350,8 @@ public function response(Transaction $transaction, ?ContextInterface $context =

$context = $context ?: $this;

$this->metadata = [
'context' => $context->getContextUrl($transaction),
];
$this->metadata = $transaction->getMetadata()->getContainer();
$this->metadata['context'] = $context->getContextUrl($transaction);

return $transaction->getResponse()->setCallback(function () use ($transaction) {
$this->emit($transaction);
Expand Down
26 changes: 18 additions & 8 deletions src/EntitySet.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,21 @@
use Flat3\Lodata\Interfaces\IdentifierInterface;
use Flat3\Lodata\Interfaces\Operation\ArgumentInterface;
use Flat3\Lodata\Interfaces\PipeInterface;
use Flat3\Lodata\Interfaces\ReferenceInterface;
use Flat3\Lodata\Interfaces\ResourceInterface;
use Flat3\Lodata\Interfaces\ServiceInterface;
use Flat3\Lodata\Traits\HasIdentifier;
use Flat3\Lodata\Traits\UseReferences;
use Flat3\Lodata\Traits\HasTitle;
use Flat3\Lodata\Traits\HasTransaction;
use Flat3\Lodata\Transaction\Option;
use Illuminate\Http\Request;
use Iterator;

abstract class EntitySet implements EntityTypeInterface, IdentifierInterface, ResourceInterface, ServiceInterface, ContextInterface, Iterator, Countable, EmitInterface, PipeInterface, ArgumentInterface
abstract class EntitySet implements EntityTypeInterface, ReferenceInterface, IdentifierInterface, ResourceInterface, ServiceInterface, ContextInterface, Iterator, Countable, EmitInterface, PipeInterface, ArgumentInterface
{
use HasIdentifier;
use UseReferences;
use HasTitle;
use HasTransaction;

Expand Down Expand Up @@ -231,6 +234,11 @@ public function emit(Transaction $transaction): void

while ($this->valid()) {
$entity = $this->current();

if ($this->usesReferences()) {
$entity->useReferences();
}

$entity->emit($transaction);

$this->next();
Expand All @@ -255,9 +263,9 @@ public function response(Transaction $transaction, ?ContextInterface $context =

$setCount = $this->count();

$metadata = [
'context' => $context->getContextUrl($transaction),
];
$metadata = $transaction->getMetadata()->getContainer();

$metadata['context'] = $context->getContextUrl($transaction);

$count = $transaction->getCount();
if (true === $count->getValue()) {
Expand Down Expand Up @@ -292,13 +300,11 @@ public function response(Transaction $transaction, ?ContextInterface $context =
);
}

$metadata = $transaction->getMetadata()->filter($metadata);

return $transaction->getResponse()->setCallback(function () use ($transaction, $metadata) {
$transaction->outputJsonObjectStart();

if ($metadata) {
$transaction->outputJsonKV($metadata);
if ($metadata->hasMetadata()) {
$transaction->outputJsonKV($metadata->getMetadata());
$transaction->outputJsonSeparator();
}

Expand All @@ -310,6 +316,10 @@ public function response(Transaction $transaction, ?ContextInterface $context =

public function getContextUrl(Transaction $transaction): string
{
if ($this->usesReferences()) {
return $transaction->getContextUrl().'#Collection($ref)';
}

$url = $transaction->getContextUrl().'#'.$this->getName();
$properties = $transaction->getContextUrlProperties();

Expand Down
10 changes: 4 additions & 6 deletions src/Helper/PropertyValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,17 +211,15 @@ public function response(Transaction $transaction, ?ContextInterface $context =
return $value->response($transaction, $this);
}

$metadata = [
'context' => $context->getContextUrl($transaction),
];
$metadata = $transaction->getMetadata()->getContainer();

$metadata = $transaction->getMetadata()->filter($metadata);
$metadata['context'] = $context->getContextUrl($transaction);

return $transaction->getResponse()->setCallback(function () use ($transaction, $metadata) {
$transaction->outputJsonObjectStart();

if ($metadata) {
$transaction->outputJsonKV($metadata);
if ($metadata->hasMetadata()) {
$transaction->outputJsonKV($metadata->getMetadata());
$transaction->outputJsonSeparator();
}

Expand Down
10 changes: 10 additions & 0 deletions src/Interfaces/ReferenceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Flat3\Lodata\Interfaces;

interface ReferenceInterface
{
public function useReferences(bool $useReferences = true);

public function usesReferences(): bool;
}
63 changes: 63 additions & 0 deletions src/PathSegment/Reference.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Flat3\Lodata\PathSegment;

use Flat3\Lodata\Controller\Response;
use Flat3\Lodata\Controller\Transaction;
use Flat3\Lodata\Entity;
use Flat3\Lodata\EntitySet;
use Flat3\Lodata\Exception\Internal\PathNotHandledException;
use Flat3\Lodata\Exception\Protocol\BadRequestException;
use Flat3\Lodata\Exception\Protocol\NotFoundException;
use Flat3\Lodata\Interfaces\ContextInterface;
use Flat3\Lodata\Interfaces\EmitInterface;
use Flat3\Lodata\Interfaces\PipeInterface;
use Flat3\Lodata\Interfaces\ReferenceInterface;

class Reference implements EmitInterface, PipeInterface
{
/** @var Entity|EntitySet $referencable */
protected $referencable;

public function __construct($countable)
{
$this->referencable = $countable;
}

public function emit(Transaction $transaction): void
{
$this->referencable->emit($transaction);
}

public function response(Transaction $transaction, ?ContextInterface $context = null): Response
{
return $this->referencable->response($transaction, $context);
}

public static function pipe(
Transaction $transaction,
string $currentSegment,
?string $nextSegment,
?PipeInterface $argument
): ?PipeInterface {
if ($currentSegment !== '$ref') {
throw new PathNotHandledException();
}

if ($nextSegment) {
throw new BadRequestException('no_next_segment', 'Reference request must be the last segment');
}

if (!$argument instanceof Entity && !$argument instanceof EntitySet) {
throw new NotFoundException(
'not_entity_or_entity_set',
'Can only ask for a reference for an entity set or entity'
);
}

/** @var ReferenceInterface $argument */
$argument->useReferences();

return new static($argument);
}
}
11 changes: 4 additions & 7 deletions src/PathSegment/Service.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ public function emit(Transaction $transaction): void
{
$transaction->outputJsonObjectStart();

$metadata = [
'context' => $transaction->getContextUrl(),
];
$metadata = $transaction->getMetadata()->getContainer();
$metadata['context'] = $transaction->getContextUrl();

$metadata = $transaction->getMetadata()->filter($metadata);

if ($metadata) {
$transaction->outputJsonKV($metadata);
if ($metadata->hasMetadata()) {
$transaction->outputJsonKV($metadata->getMetadata());
$transaction->outputJsonSeparator();
}

Expand Down
10 changes: 4 additions & 6 deletions src/Primitive.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,15 @@ public function response(Transaction $transaction, ?ContextInterface $context =

$context = $context ?: $this;

$metadata = [
'context' => $context->getContextUrl($transaction),
];
$metadata = $transaction->getMetadata()->getContainer();

$metadata = $transaction->getMetadata()->filter($metadata);
$metadata['context'] = $context->getContextUrl($transaction);

return $transaction->getResponse()->setCallback(function () use ($transaction, $metadata) {
$transaction->outputJsonObjectStart();

if ($metadata) {
$transaction->outputJsonKV($metadata);
if ($metadata->hasMetadata()) {
$transaction->outputJsonKV($metadata->getMetadata());
$transaction->outputJsonSeparator();
}

Expand Down
Loading

0 comments on commit 73c24ab

Please sign in to comment.