Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
27pchrisl committed Mar 19, 2022
1 parent 733068e commit b63cac1
Show file tree
Hide file tree
Showing 40 changed files with 801 additions and 135 deletions.
85 changes: 85 additions & 0 deletions doc/making-requests/querying-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,33 @@ GET http://localhost:8000/odata/People?$filter=FirstName eq 'Scott'
</code-block>
</code-group>

`$filter` can also be used as a path segment, and used multiple times to sequentially add filter parameters.

<code-group>
<code-block title="Request">
```uri
GET http://localhost:8000/odata/People/$filter(@a)/$filter(@b)?@a=DOB gt 2000-01-01&@b=endswith(FirstName, 'tt')
```
</code-block>

<code-block title="Response">
```json
{
"@context": "http://localhost:8000/odata/$metadata#People",
"value": [
{
"id": 1,
"FirstName": "Scott",
"LastName": "Bumble",
"Email": "scott.bumble@gmail.com",
"DOB": "2001-01-01"
}
]
}
```
</code-block>
</code-group>

## $orderby

The `$orderby` system query option allows clients to request resources in either ascending order using asc or descending
Expand Down Expand Up @@ -457,3 +484,61 @@ GET http://localhost:8000/odata/People?$filter=pets/any(s:endswith(s/Name, 'The
```
</code-block>
</code-group>

## $each

The `$each` path segment enables actions and odata operations such as deletes and updates to be run on sequences of
entities server-side. To filter the sequence the `$filter` path segment must be used.

Members of a collection can be updated by submitting a PATCH request to the URL constructed by appending `/$each` to the
resource path of the collection. The additional path segment expresses that the request body describes an update to
each member of the collection, not an update to the collection itself.

<code-group>
<code-block title="Request">
```uri
PATCH http://localhost/odata/People/$filter(@bar)/$each?@bar=Color eq 'beige-brown'
{
"Color": "taupe"
}
```
</code-block>

<code-block title="Response">
```json
{
"@context": "http://localhost:8000/odata/$metadata#People",
"value": [
{
"id": 1,
"FirstName": "Scott",
"LastName": "Bumble",
"Color": "taupe"
},
{
"id": 3,
"FirstName": "Michael",
"LastName": "Scott",
"Color": "taupe"
}
]
}
```
</code-block>
</code-group>

Members of a collection can be deleted by submitting a DELETE request to the URL constructed by appending `/$each`
to the resource path of the collection. The additional path segment expresses that the collection itself is not
deleted.

```uri
DELETE http://localhost/odata/People/$filter(@bar)/$each?@bar=Color eq 'beige-brown'
```

A bound operation with a single-valued binding parameter can be applied to each member of a collection by appending
the path segment `$each` to the resource path of the collection, followed by a forward slash and the namespace- or
alias-qualified name of the bound operation.

```uri
GET http://localhost/odata/People/$each/SampleModel.MostRecentOrder()
```
6 changes: 4 additions & 2 deletions src/Controller/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Flat3\Lodata\Controller;

use Exception;
use Flat3\Lodata\Drivers\StaticEntitySet;
use Flat3\Lodata\Entity;
use Flat3\Lodata\EntitySet;
use Flat3\Lodata\EntityType;
Expand Down Expand Up @@ -59,6 +58,7 @@
use Flat3\Lodata\Transaction\Parameter;
use Flat3\Lodata\Transaction\ParameterList;
use Flat3\Lodata\Transaction\Version;
use Flat3\Lodata\Type\Collection;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
Expand Down Expand Up @@ -227,6 +227,7 @@ class Transaction
PathSegment\Filter::class,
PathSegment\Query::class,
PathSegment\Reference::class,
PathSegment\Each::class,
Operation::class,
Singleton::class,
PropertyValue::class,
Expand Down Expand Up @@ -1433,7 +1434,8 @@ public function processDeltaPayloads(Entity $parentEntity): void
/** @var NavigationProperty $navigationProperty */
foreach ($navigationProperties as $navigationProperty) {
$deltaPayloads = $body[$navigationProperty->getName()] ?? [];
$deltaResponseSet = new StaticEntitySet($navigationProperty->getEntityType());
$deltaResponseSet = new Collection();
$deltaResponseSet->setUnderlyingType($navigationProperty->getEntityType());

foreach ($deltaPayloads as $deltaPayload) {
$entity = null;
Expand Down
8 changes: 8 additions & 0 deletions src/Drivers/EnumerableEntitySet.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Flat3\Lodata\Entity;
use Flat3\Lodata\EntitySet;
use Flat3\Lodata\EntityType;
use Flat3\Lodata\Exception\Protocol\NotFoundException;
use Flat3\Lodata\Expression\Parser\Common;
use Flat3\Lodata\Expression\Parser\Search;
Expand All @@ -29,6 +30,13 @@ abstract class EnumerableEntitySet extends EntitySet implements ReadInterface, Q
/** @var Enumerable|Collection|LazyCollection $enumerable */
protected $enumerable;

public function __construct(string $identifier, ?EntityType $entityType = null)
{
parent::__construct($identifier, $entityType);

$this->enumerable = new Collection();
}

/**
* Query this entity set
* @return Generator
Expand Down
5 changes: 4 additions & 1 deletion src/Drivers/SQL/SQLSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ function () {
$key = $this->columnToDeclaredProperty($column);

if (null === $key) {
throw new ConfigurationException('missing_key', sprintf('The table %s had no resolvable key', $this->getTable()));
throw new ConfigurationException(
'missing_key',
sprintf('The table %s had no resolvable key', $this->getTable())
);
}

if ($column->getAutoincrement()) {
Expand Down
105 changes: 0 additions & 105 deletions src/Drivers/StaticEntitySet.php

This file was deleted.

8 changes: 3 additions & 5 deletions src/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,8 @@ public function getResourceUrl(Transaction $transaction): string
* Delete this entity
* @param Transaction $transaction Related transaction
* @param ContextInterface|null $context Current context
* @return Response Client response
*/
public function delete(Transaction $transaction, ?ContextInterface $context = null): Response
public function delete(Transaction $transaction, ?ContextInterface $context = null): void
{
$entitySet = $this->entitySet;

Expand All @@ -255,8 +254,6 @@ public function delete(Transaction $transaction, ?ContextInterface $context = nu
$transaction->assertIfMatchHeader($this->getETag());

$entitySet->delete($this->getEntityId());

throw new NoContentException('deleted', 'Content was deleted');
}

/**
Expand Down Expand Up @@ -331,7 +328,8 @@ public function response(Transaction $transaction, ?ContextInterface $context =
return $this->patch($transaction, $context);

case Request::METHOD_DELETE:
return $this->delete($transaction, $context);
$this->delete($transaction, $context);
throw new NoContentException('deleted', 'Content was deleted');

case Request::METHOD_GET:
return $this->get($transaction, $context);
Expand Down
53 changes: 48 additions & 5 deletions src/Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Flat3\Lodata\Exception\Protocol\ConfigurationException;
use Flat3\Lodata\Exception\Protocol\InternalServerErrorException;
use Flat3\Lodata\Exception\Protocol\NoContentException;
use Flat3\Lodata\Exception\Protocol\NotImplementedException;
use Flat3\Lodata\Expression\Lexer;
use Flat3\Lodata\Facades\Lodata;
use Flat3\Lodata\Helper\Arguments;
Expand All @@ -22,6 +23,7 @@
use Flat3\Lodata\Helper\JSON;
use Flat3\Lodata\Helper\PropertyValue;
use Flat3\Lodata\Interfaces\AnnotationInterface;
use Flat3\Lodata\Interfaces\EntitySet\QueryInterface;
use Flat3\Lodata\Interfaces\IdentifierInterface;
use Flat3\Lodata\Interfaces\PipeInterface;
use Flat3\Lodata\Interfaces\RepositoryInterface;
Expand All @@ -36,6 +38,7 @@
use Flat3\Lodata\Operation\Repository;
use Flat3\Lodata\Operation\TransactionArgument;
use Flat3\Lodata\Operation\ValueArgument;
use Flat3\Lodata\PathSegment\Each;
use Flat3\Lodata\Traits\HasAnnotations;
use Flat3\Lodata\Traits\HasIdentifier;
use Flat3\Lodata\Traits\HasTitle;
Expand Down Expand Up @@ -637,6 +640,41 @@ public static function pipe(
);
}

if ($argument instanceof Each) {
$entitySet = $argument->getArgument();

if (!$entitySet instanceof QueryInterface) {
throw new NotImplementedException(
'entityset_cannot_query',
'This entity set cannot be queried',
);
}

$result = new Collection();
$result->setUnderlyingType($operation->getReturnType());

foreach ($entitySet->query() as $entity) {
$operationInstance = clone $operation;
$operationTransaction = new Transaction();
$request = Request::create(
'',
Request::METHOD_POST,
[],
[],
[],
[],
$transaction->getRequest()->getContent()
);
$request->headers->replace($transaction->getRequestHeaders());
$operationTransaction->initialize(new Controller\Request($request));
$operationInstance->setTransaction($operationTransaction);
$operationInstance->setBoundParameter($entity);
$result[] = $operationInstance->executeAction();
}

return $result;
}

$operation = clone $operation;
$operation->setTransaction($transaction);
$operation->setBoundParameter($argument);
Expand Down Expand Up @@ -734,11 +772,16 @@ public function ensureResult($result): ?PipeInterface
);
}

if ($returnType instanceof EntityType && !$result->getType() instanceof $returnType) {
throw new InternalServerErrorException(
'invalid_entity_type_returned',
'The operation returned an entity type that did not match its defined type',
);
if ($returnType instanceof EntityType) {
if (
($result instanceof Collection && !$result->getUnderlyingType() instanceof $returnType) ||
($result instanceof ComplexValue && !$result->getType() instanceof $returnType)
) {
throw new InternalServerErrorException(
'invalid_entity_type_returned',
'The operation returned an entity type that did not match its defined type',
);
}
}

if ($returnType instanceof PrimitiveType && !$result instanceof Primitive) {
Expand Down
Loading

0 comments on commit b63cac1

Please sign in to comment.