diff --git a/doc/making-requests/querying-data.md b/doc/making-requests/querying-data.md index 9c48292fa..4a72fb349 100644 --- a/doc/making-requests/querying-data.md +++ b/doc/making-requests/querying-data.md @@ -43,6 +43,33 @@ GET http://localhost:8000/odata/People?$filter=FirstName eq 'Scott' +`$filter` can also be used as a path segment, and used multiple times to sequentially add filter parameters. + + + +```uri +GET http://localhost:8000/odata/People/$filter(@a)/$filter(@b)?@a=DOB gt 2000-01-01&@b=endswith(FirstName, 'tt') +``` + + + +```json +{ + "@context": "http://localhost:8000/odata/$metadata#People", + "value": [ + { + "id": 1, + "FirstName": "Scott", + "LastName": "Bumble", + "Email": "scott.bumble@gmail.com", + "DOB": "2001-01-01" + } + ] +} +``` + + + ## $orderby The `$orderby` system query option allows clients to request resources in either ascending order using asc or descending @@ -457,3 +484,61 @@ GET http://localhost:8000/odata/People?$filter=pets/any(s:endswith(s/Name, 'The ``` + +## $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. + + + +```uri +PATCH http://localhost/odata/People/$filter(@bar)/$each?@bar=Color eq 'beige-brown' +{ + "Color": "taupe" +} +``` + + + +```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" + } + ] +} +``` + + + +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() +``` diff --git a/src/Controller/Transaction.php b/src/Controller/Transaction.php index 6c647379f..9a4e0a723 100644 --- a/src/Controller/Transaction.php +++ b/src/Controller/Transaction.php @@ -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; @@ -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; @@ -227,6 +227,7 @@ class Transaction PathSegment\Filter::class, PathSegment\Query::class, PathSegment\Reference::class, + PathSegment\Each::class, Operation::class, Singleton::class, PropertyValue::class, @@ -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; diff --git a/src/Drivers/EnumerableEntitySet.php b/src/Drivers/EnumerableEntitySet.php index b044da5c6..a55811d96 100644 --- a/src/Drivers/EnumerableEntitySet.php +++ b/src/Drivers/EnumerableEntitySet.php @@ -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; @@ -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 diff --git a/src/Drivers/SQL/SQLSchema.php b/src/Drivers/SQL/SQLSchema.php index 351b46b3a..ee2bcc557 100644 --- a/src/Drivers/SQL/SQLSchema.php +++ b/src/Drivers/SQL/SQLSchema.php @@ -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()) { diff --git a/src/Drivers/StaticEntitySet.php b/src/Drivers/StaticEntitySet.php deleted file mode 100644 index ddaf2d2a7..000000000 --- a/src/Drivers/StaticEntitySet.php +++ /dev/null @@ -1,105 +0,0 @@ -applyQueryOptions = false; - } - - /** - * Return all results - */ - public function query(): Generator - { - foreach ($this->results as $result) { - yield $result; - } - } - - public function offsetExists($offset): bool - { - return array_key_exists($offset, $this->results); - } - - public function offsetGet($offset): ?Entity - { - return $this->results[$offset]; - } - - public function offsetSet($offset, $value): void - { - if ($offset) { - $this->results[$offset] = $value; - } else { - $this->results[] = $value; - } - } - - public function offsetUnset($offset): void - { - unset ($this->results[$offset]); - } - - public function rewind() - { - reset($this->results); - } - - /** - * Filter the objects in the array - * @param callable $callback - * @return $this - */ - public function filter(callable $callback): self - { - $this->results = array_filter($this->results, $callback); - - return $this; - } - - /** - * Sort the objects in the array - * @param callable $callback - * @return $this - */ - public function sort(callable $callback): self - { - uasort($this->results, $callback); - - return $this; - } - - /** - * Count the objects in the array - * @return int - */ - public function count(): int - { - return count($this->results); - } -} \ No newline at end of file diff --git a/src/Entity.php b/src/Entity.php index 7b3bd3745..a1c5b54e7 100644 --- a/src/Entity.php +++ b/src/Entity.php @@ -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; @@ -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'); } /** @@ -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); diff --git a/src/Operation.php b/src/Operation.php index b2f17f706..3b5b45c81 100644 --- a/src/Operation.php +++ b/src/Operation.php @@ -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; @@ -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; @@ -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; @@ -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); @@ -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) { diff --git a/src/PathSegment/Each.php b/src/PathSegment/Each.php new file mode 100644 index 000000000..6db1b0c85 --- /dev/null +++ b/src/PathSegment/Each.php @@ -0,0 +1,70 @@ +argument = $argument; + } + + public static function pipe( + Transaction $transaction, + string $currentSegment, + ?string $nextSegment, + ?PipeInterface $argument + ): PipeInterface { + if ($currentSegment !== '$each') { + throw new PathNotHandledException(); + } + + if (!$argument instanceof QueryInterface || !$argument instanceof EntitySet) { + throw new PathNotHandledException(); + } + + if ($argument instanceof DeleteInterface && $transaction->getMethod() === Request::METHOD_DELETE) { + foreach ($argument->query() as $entity) { + $entity->delete($transaction); + } + + throw new NoContentException(); + } + + if ($argument instanceof UpdateInterface && $transaction->getMethod() === Request::METHOD_PATCH) { + foreach ($argument->query() as $entity) { + $propertyValues = $argument->arrayToPropertyValues($transaction->getBodyAsArray()); + $argument->update($entity->getEntityId(), $propertyValues); + } + + $argument->getTransaction()->getRequest()->setMethod(Request::METHOD_GET); + + return $argument; + } + + return new self($argument); + } + + public function getArgument(): PipeInterface + { + return $this->argument; + } +} diff --git a/src/PathSegment/Filter.php b/src/PathSegment/Filter.php index 2853a3e27..664e16a47 100644 --- a/src/PathSegment/Filter.php +++ b/src/PathSegment/Filter.php @@ -9,6 +9,7 @@ use Flat3\Lodata\Exception\Internal\PathNotHandledException; use Flat3\Lodata\Exception\Protocol\BadRequestException; use Flat3\Lodata\Expression\Lexer; +use Flat3\Lodata\Interfaces\EntitySet\FilterInterface; use Flat3\Lodata\Interfaces\PipeInterface; /** @@ -38,6 +39,13 @@ public static function pipe( ); } + if (!$argument instanceof FilterInterface) { + throw new BadRequestException( + 'entityset_cannot_filter', + 'The requested entity set does not support filter', + ); + } + $filter = $lexer->matchingParenthesis(); $transaction->getFilter()->addExpression($filter); diff --git a/src/Type/Collection.php b/src/Type/Collection.php index a89a317f5..02383777d 100644 --- a/src/Type/Collection.php +++ b/src/Type/Collection.php @@ -22,14 +22,15 @@ */ class Collection extends Primitive implements ArrayAccess { - /** @var Primitive[] $value */ - protected $value = []; + /** @var Primitive[]|\Illuminate\Support\Collection $value */ + protected $value = null; /** @var CollectionType $type */ protected $type; public function __construct($value = null) { + $this->value = collect(); $this->setCollectionType(new CollectionType); parent::__construct($value); } @@ -48,9 +49,9 @@ public function toJson() public function toMixed(): ?array { - return array_map(function (SerializeInterface $value) { + return $this->value->map(function (SerializeInterface $value) { return $value->toMixed(); - }, $this->value); + })->toArray(); } /** @@ -170,14 +171,14 @@ public function emitJson(Transaction $transaction): void { $transaction->outputJsonArrayStart(); - reset($this->value); + $iterator = $this->value->getIterator(); - while (current($this->value)) { - $value = current($this->value); + while ($iterator->current()) { + $value = $iterator->current(); $value->emitJson($transaction); - next($this->value); + $iterator->next(); - if (current($this->value)) { + if ($iterator->current()) { $transaction->outputJsonSeparator(); } } diff --git a/tests/Entity/EntityTest.php b/tests/Entity/EntityTest.php index 216131bff..31293bbce 100644 --- a/tests/Entity/EntityTest.php +++ b/tests/Entity/EntityTest.php @@ -9,7 +9,6 @@ use Flat3\Lodata\GeneratedProperty; use Flat3\Lodata\Tests\Helpers\Request; use Flat3\Lodata\Tests\TestCase; -use Flat3\Lodata\Transaction\MediaType; use Flat3\Lodata\Transaction\MetadataType; use Flat3\Lodata\Type; use Flat3\Lodata\Type\Int32; diff --git a/tests/EntityEach/DatabaseTest.php b/tests/EntityEach/DatabaseTest.php new file mode 100644 index 000000000..635b494c2 --- /dev/null +++ b/tests/EntityEach/DatabaseTest.php @@ -0,0 +1,9 @@ +assertNoContent( + (new Request) + ->path($this->entitySetPath.'/$filter(age ge 3)/$each') + ->delete() + ); + } + + public function test_update_each() + { + $this->assertJsonResponseSnapshot( + (new Request) + ->path($this->entitySetPath.'/$filter(@bar)/$each') + ->query('@bar', 'age ge 3') + ->patch() + ->body(['name' => 'Oop']) + ); + } + + public function test_action_each() + { + $action = new Action('testaction'); + $action->setCallable(function (Entity $entity): String_ { + return $entity['name']->getPrimitive(); + }); + + $action->setBindingParameterName('entity'); + Lodata::add($action); + + $this->assertJsonResponseSnapshot( + (new Request) + ->path($this->entitySetPath.'/$filter(@bar)/$each/testaction()') + ->query('@bar', 'age ge 3') + ->post() + ); + } + + public function test_action_each_arg() + { + $action = new Action('testaction'); + $action->setCallable(function (Entity $entity, string $pfx): string { + return $pfx.' '.$entity['name']->getPrimitive()->toMixed(); + }); + + $action->setBindingParameterName('entity'); + Lodata::add($action); + + $this->assertJsonResponseSnapshot( + (new Request) + ->path($this->entitySetPath.'/$filter(@bar)/$each/testaction()') + ->query('@bar', 'age ge 3') + ->body([ + 'pfx' => 'Hello', + ]) + ->post() + ); + } +} \ No newline at end of file diff --git a/tests/EntityEach/KeyedCollectionTest.php b/tests/EntityEach/KeyedCollectionTest.php new file mode 100644 index 000000000..754ed23ce --- /dev/null +++ b/tests/EntityEach/KeyedCollectionTest.php @@ -0,0 +1,12 @@ +setCallable(function (String_ $field, EntitySet $passengers): EntitySet { - $result = new StaticEntitySet($passengers->getType()); - $result->setIdentifier($passengers->getIdentifier()); + $sorter->setCallable(function (String_ $field, EntitySet $passengers): Collection { + $result = new Collection(); + $result->setUnderlyingType($passengers->getType()); foreach ($passengers->query() as $airport) { $result[] = $airport; } - $result->sort(function (Entity $a1, Entity $a2) use ($field) { + $result->get()->sort(function (Entity $a1, Entity $a2) use ($field) { return $a1[$field->get()]->getPrimitiveValue() <=> $a2[$field->get()]->getPrimitiveValue(); }); diff --git a/tests/Queries/AllTest.php b/tests/Queries/AllTest.php index 6cbbd58d9..3d211bf89 100644 --- a/tests/Queries/AllTest.php +++ b/tests/Queries/AllTest.php @@ -2,7 +2,7 @@ namespace Flat3\Lodata\Tests\Queries; -use Flat3\Lodata\Drivers\StaticEntitySet; +use Flat3\Lodata\Drivers\CollectionEntitySet; use Flat3\Lodata\EntityType; use Flat3\Lodata\Facades\Lodata; use Flat3\Lodata\Tests\Drivers\WithNumericCollectionDriver; @@ -90,7 +90,7 @@ public function test_bad_type() public function test_read_empty() { - Lodata::add(new StaticEntitySet(new EntityType('basic'))); + Lodata::add(new CollectionEntitySet(new EntityType('basic'))); $this->assertJsonResponseSnapshot( (new Request) diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each__1.json new file mode 100644 index 000000000..2e6cad344 --- /dev/null +++ b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Alpha", + "Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each_arg__1.json new file mode 100644 index 000000000..e440f8802 --- /dev/null +++ b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each_arg__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Hello Alpha", + "Hello Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/EloquentTest__test_delete_each__1.txt new file mode 100644 index 000000000..ff97eafd4 --- /dev/null +++ b/tests/__snapshots__/EntityEach/EloquentTest__test_delete_each__1.txt @@ -0,0 +1,38 @@ +@@ @@ + ], + "passengers": [ + { +- "id": 1, +- "flight_id": 1, +- "name": "Alpha", +- "dob": "2000-01-01 04:04:04", +- "age": 4, +- "chips": true, +- "dq": "2000-01-01", +- "in_role": 86400, +- "open_time": "05:05:05", +- "colour": 2, +- "sock_colours": 6, +- "emails": [ +- "alpha@example.com", +- "alpha@beta.com" +- ] +- }, +- { +- "id": 2, +- "flight_id": null, +- "name": "Beta", +- "dob": "2001-02-02 05:05:05", +- "age": 3, +- "chips": false, +- "dq": "2001-02-02", +- "in_role": 191105.3, +- "open_time": null, +- "colour": null, +- "sock_colours": null, +- "emails": null +- }, +- { + "id": 3, + "flight_id": 1, + "name": "Gamma", diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__1.json new file mode 100644 index 000000000..0acfd8425 --- /dev/null +++ b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__1.json @@ -0,0 +1,36 @@ +{ + "@context": "http://localhost/odata/$metadata#Passengers", + "value": [ + { + "id": 1, + "flight_id": 1, + "name": "Oop", + "dob": "2000-01-01T04:04:04+00:00", + "age": 4, + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": 2, + "flight_id": null, + "name": "Oop", + "dob": "2001-02-02T05:05:05+00:00", + "age": 3, + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "open_time": null, + "colour": null, + "sock_colours": null, + "emails": [] + } + ] +} diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__2.txt new file mode 100644 index 000000000..b9249df3a --- /dev/null +++ b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__2.txt @@ -0,0 +1,18 @@ +@@ @@ + { + "id": 1, + "flight_id": 1, +- "name": "Alpha", ++ "name": "Oop", + "dob": "2000-01-01 04:04:04", + "age": 4, + "chips": true, +@@ @@ + { + "id": 2, + "flight_id": null, +- "name": "Beta", ++ "name": "Oop", + "dob": "2001-02-02 05:05:05", + "age": 3, + "chips": false, diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each__1.json new file mode 100644 index 000000000..2e6cad344 --- /dev/null +++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Alpha", + "Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each_arg__1.json new file mode 100644 index 000000000..e440f8802 --- /dev/null +++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each_arg__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Hello Alpha", + "Hello Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_delete_each__1.txt new file mode 100644 index 000000000..fa514f075 --- /dev/null +++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_delete_each__1.txt @@ -0,0 +1,31 @@ +@@ @@ + { +- "alpha": { +- "name": "Alpha", +- "age": 4, +- "dob": "2000-01-01 04:04:04", +- "chips": true, +- "dq": "2000-01-01", +- "in_role": 86400, +- "open_time": "05:05:05", +- "flight_id": 1, +- "colour": 2, +- "sock_colours": 6, +- "emails": [ +- "alpha@example.com", +- "alpha@beta.com" +- ] +- }, +- "beta": { +- "name": "Beta", +- "age": 3, +- "dob": "2001-02-02 05:05:05", +- "chips": false, +- "dq": "2001-02-02", +- "in_role": 191105.3, +- "colour": null, +- "sock_colours": null +- }, + "gamma": { + "name": "Gamma", + "age": 2, diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__1.json new file mode 100644 index 000000000..540bc602c --- /dev/null +++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__1.json @@ -0,0 +1,36 @@ +{ + "@context": "http://localhost/odata/$metadata#passengers", + "value": [ + { + "id": "alpha", + "name": "Oop", + "age": 4, + "dob": "2000-01-01T04:04:04+00:00", + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "flight_id": 1, + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": "beta", + "name": "Oop", + "age": 3, + "dob": "2001-02-02T05:05:05+00:00", + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "colour": null, + "sock_colours": null, + "open_time": null, + "flight_id": null, + "emails": [] + } + ] +} diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__2.txt new file mode 100644 index 000000000..1cfce203b --- /dev/null +++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__2.txt @@ -0,0 +1,29 @@ +@@ @@ + { + "alpha": { +- "name": "Alpha", ++ "name": "Oop", + "age": 4, + "dob": "2000-01-01 04:04:04", + "chips": true, +@@ @@ + ] + }, + "beta": { +- "name": "Beta", ++ "name": "Oop", + "age": 3, + "dob": "2001-02-02 05:05:05", + "chips": false, + "dq": "2001-02-02", + "in_role": 191105.3, +- "colour": null, +- "sock_colours": null ++ "colour": 0, ++ "sock_colours": 0, ++ "open_time": null, ++ "flight_id": null, ++ "emails": [] + }, + "gamma": { + "name": "Gamma", diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each__1.json new file mode 100644 index 000000000..2e6cad344 --- /dev/null +++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Alpha", + "Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each_arg__1.json new file mode 100644 index 000000000..e440f8802 --- /dev/null +++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each_arg__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Hello Alpha", + "Hello Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_delete_each__1.txt new file mode 100644 index 000000000..c89b3a48f --- /dev/null +++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_delete_each__1.txt @@ -0,0 +1,22 @@ +@@ @@ + [ + { +- "name": "Alpha", +- "age": 4, +- "dob": "2000-01-01 04:04:04", +- "chips": true, +- "dq": "2000-01-01", +- "in_role": 86400, +- "open_time": "05:05:05", +- "flight_id": 1, +- "colour": 2, +- "sock_colours": 6, +- "emails": [ +- "alpha@example.com", +- "alpha@beta.com" +- ] +- }, +- { + "name": "Beta", + "age": 3, + "dob": "2001-02-02 05:05:05", diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__1.json new file mode 100644 index 000000000..2b94266bf --- /dev/null +++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__1.json @@ -0,0 +1,36 @@ +{ + "@context": "http://localhost/odata/$metadata#passengers", + "value": [ + { + "id": 0, + "name": "Oop", + "age": 4, + "dob": "2000-01-01T04:04:04+00:00", + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "flight_id": 1, + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": 1, + "name": "Oop", + "age": 3, + "dob": "2001-02-02T05:05:05+00:00", + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "colour": null, + "sock_colours": null, + "open_time": null, + "flight_id": null, + "emails": [] + } + ] +} diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__2.txt new file mode 100644 index 000000000..aacd84068 --- /dev/null +++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__2.txt @@ -0,0 +1,29 @@ +@@ @@ + [ + { +- "name": "Alpha", ++ "name": "Oop", + "age": 4, + "dob": "2000-01-01 04:04:04", + "chips": true, +@@ @@ + ] + }, + { +- "name": "Beta", ++ "name": "Oop", + "age": 3, + "dob": "2001-02-02 05:05:05", + "chips": false, + "dq": "2001-02-02", + "in_role": 191105.3, +- "colour": null, +- "sock_colours": null ++ "colour": 0, ++ "sock_colours": 0, ++ "open_time": null, ++ "flight_id": null, ++ "emails": [] + }, + { + "name": "Gamma", diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/SQLTest__test_action_each__1.json new file mode 100644 index 000000000..2e6cad344 --- /dev/null +++ b/tests/__snapshots__/EntityEach/SQLTest__test_action_each__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Alpha", + "Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/SQLTest__test_action_each_arg__1.json new file mode 100644 index 000000000..e440f8802 --- /dev/null +++ b/tests/__snapshots__/EntityEach/SQLTest__test_action_each_arg__1.json @@ -0,0 +1,7 @@ +{ + "@context": "http://localhost/odata/$metadata#Collection(Edm.String)", + "value": [ + "Hello Alpha", + "Hello Beta" + ] +} diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/SQLTest__test_delete_each__1.txt new file mode 100644 index 000000000..ff97eafd4 --- /dev/null +++ b/tests/__snapshots__/EntityEach/SQLTest__test_delete_each__1.txt @@ -0,0 +1,38 @@ +@@ @@ + ], + "passengers": [ + { +- "id": 1, +- "flight_id": 1, +- "name": "Alpha", +- "dob": "2000-01-01 04:04:04", +- "age": 4, +- "chips": true, +- "dq": "2000-01-01", +- "in_role": 86400, +- "open_time": "05:05:05", +- "colour": 2, +- "sock_colours": 6, +- "emails": [ +- "alpha@example.com", +- "alpha@beta.com" +- ] +- }, +- { +- "id": 2, +- "flight_id": null, +- "name": "Beta", +- "dob": "2001-02-02 05:05:05", +- "age": 3, +- "chips": false, +- "dq": "2001-02-02", +- "in_role": 191105.3, +- "open_time": null, +- "colour": null, +- "sock_colours": null, +- "emails": null +- }, +- { + "id": 3, + "flight_id": 1, + "name": "Gamma", diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__1.json new file mode 100644 index 000000000..2c756d5f5 --- /dev/null +++ b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__1.json @@ -0,0 +1,36 @@ +{ + "@context": "http://localhost/odata/$metadata#passengers", + "value": [ + { + "id": 1, + "name": "Oop", + "age": 4, + "dob": "2000-01-01T04:04:04+00:00", + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "flight_id": 1, + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": 2, + "name": "Oop", + "age": 3, + "dob": "2001-02-02T05:05:05+00:00", + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "open_time": null, + "flight_id": null, + "colour": null, + "sock_colours": null, + "emails": [] + } + ] +} diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__2.txt new file mode 100644 index 000000000..b9249df3a --- /dev/null +++ b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__2.txt @@ -0,0 +1,18 @@ +@@ @@ + { + "id": 1, + "flight_id": 1, +- "name": "Alpha", ++ "name": "Oop", + "dob": "2000-01-01 04:04:04", + "age": 4, + "chips": true, +@@ @@ + { + "id": 2, + "flight_id": null, +- "name": "Beta", ++ "name": "Oop", + "dob": "2001-02-02 05:05:05", + "age": 3, + "chips": false, diff --git a/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json b/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json index 55d05ef13..00d851c13 100644 --- a/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json +++ b/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json @@ -1,5 +1,5 @@ { - "@context": "http://localhost/odata/$metadata#passengers", + "@context": "http://localhost/odata/$metadata#Collection(com.example.odata.passenger)", "value": [ { "id": 0,