Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update behaviour for immutable and computed properties #837

Merged
merged 17 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9f758de
Strip computed properties from payload and update OpenAPI documentati…
yvo-niedrich Jul 17, 2024
8cf08df
code style: make boolean condition easier to read and understand
yvo-niedrich Jul 18, 2024
f5740f7
OpenAPI: Display `Computed` properties with the entity schema and `Co…
yvo-niedrich Jul 23, 2024
aa10b9f
update phpunit snapshots
yvo-niedrich Jul 23, 2024
165bb17
Bugfix: Initialise `$cookies` when creating a Request object from `Il…
yvo-niedrich Jul 23, 2024
e7dfb5c
Improvement: Utilizing the method-chaining nature of `Model.setAttrib…
yvo-niedrich Jul 23, 2024
cef62a4
PHPUnit: Update snapshots for `mongo` test suite (computed properties…
yvo-niedrich Jul 24, 2024
3c00ff6
Bugfix: Add `description` to Enum type to address invalid order of ar…
yvo-niedrich Jul 25, 2024
c363ad8
EloquentEntitySet::create - use the property source name for the attr…
sean-james-eco Jul 31, 2024
e63ff1f
Merge pull request #1 from yvo-niedrich/5.x-prop-source
yvo-niedrich Jul 31, 2024
3bc5b54
`Property.IsComputed` no longer matches on ComputedDefault annotations.
yvo-niedrich Aug 7, 2024
ebc5db6
CR Feedback: Update entity auto-detected key property annotation from…
yvo-niedrich Aug 7, 2024
43b73e5
Update tests
yvo-niedrich Aug 7, 2024
c7fd254
EPC-11157: validate guids property values
sean-james-eco Aug 5, 2024
f64d6de
Remove "modern" PHP syntax
yvo-niedrich Aug 7, 2024
940dc98
Merge pull request #2 from EcoOnline/ESR/EPC-11157-uuid-500
yvo-niedrich Aug 7, 2024
a1f1c5a
Prevent `Str::startsWith` being called with `null` on `Transaction:ge…
yvo-niedrich Aug 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/Attributes/LodataEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
class LodataEnum extends LodataProperty
{
protected string $enum;

public function __construct(string $name, string $enum, ?string $source = null)
{
parent::__construct($name, $source);
/** @var string */
protected $enum;

public function __construct(
string $name,
string $enum,
?string $description = null,
?string $source = null,
?bool $nullable = true,
?bool $immutable = false
) {
parent::__construct($name, $description, $source);
$this->nullable = $nullable;
$this->immutable = $immutable;
$this->enum = $enum;
}

Expand All @@ -33,4 +42,4 @@ public function getType(): Type

return Lodata::getEnumerationType($this->enum);
}
}
}
23 changes: 15 additions & 8 deletions src/ComplexType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use ArrayAccess;
use Flat3\Lodata\Annotation\Core\V1\Computed;
use Flat3\Lodata\Annotation\Core\V1\ComputedDefaultValue;
use Flat3\Lodata\Annotation\Core\V1\Immutable;
use Flat3\Lodata\Controller\Transaction;
use Flat3\Lodata\Exception\Protocol\NotAcceptableException;
use Flat3\Lodata\Helper\Constants;
Expand Down Expand Up @@ -243,9 +245,11 @@ public function getOpenAPISchema(): array
return [
'type' => Constants::oapiObject,
'title' => $this->getName(),
'properties' => (object) $this->getDeclaredProperties()->map(function (DeclaredProperty $property) {
return $property->getOpenAPISchema();
})
'properties' => (object) $this->getProperties()
->sliceByClass([DeclaredProperty::class, GeneratedProperty::class])
->map(function (Property $property) {
return $property->getOpenAPISchema();
})
];
}

Expand All @@ -259,7 +263,8 @@ public function getOpenAPICreateSchema(): array
'type' => Constants::oapiObject,
'title' => __('lodata:::name (Create schema)', ['name' => $this->getName()]),
'properties' => (object) $this->getDeclaredProperties()->filter(function (DeclaredProperty $property) {
return $property->getAnnotations()->sliceByClass([Computed::class])->isEmpty();
return $property->getAnnotations()->sliceByClass([Computed::class])->isEmpty()
|| $property->getAnnotations()->sliceByClass([ComputedDefaultValue::class])->hasEntries();
})->map(function (DeclaredProperty $property) {
return $property->getOpenAPISchema();
})
Expand All @@ -276,7 +281,7 @@ public function getOpenAPIUpdateSchema(): array
'type' => Constants::oapiObject,
'title' => __('lodata:::name (Update schema)', ['name' => $this->getName()]),
'properties' => (object) $this->getDeclaredProperties()->filter(function (DeclaredProperty $property) {
return $property->getAnnotations()->sliceByClass([Computed::class])->isEmpty();
return $property->getAnnotations()->sliceByClass([Computed::class, Immutable::class])->isEmpty();
})->map(function (DeclaredProperty $property) {
return $property->getOpenAPISchema();
})
Expand Down Expand Up @@ -322,7 +327,7 @@ public function assertPropertyValues(PropertyValues $propertyValues): PropertyVa
public function assertUpdateProperties(PropertyValues $propertyValues): PropertyValues
{
return $this->assertPropertyValues($propertyValues)->filter(function (PropertyValue $propertyValue) {
return !$propertyValue->getProperty()->isImmutable();
return !($propertyValue->getProperty()->isImmutable() || $propertyValue->getProperty()->isComputed());
});
}

Expand Down Expand Up @@ -353,7 +358,7 @@ public function assertCreateProperties(
continue;
}

if ($declaredProperty->isNullable() || $declaredProperty->isComputed()) {
if ($declaredProperty->isNullable() || $declaredProperty->isComputed() || $declaredProperty->isComputedDefault()) {
continue;
}

Expand All @@ -371,6 +376,8 @@ public function assertCreateProperties(
$declaredProperty->assertAllowsValue(null);
}

return $propertyValues;
return $propertyValues->filter(function (PropertyValue $propertyValue) {
return !$propertyValue->getProperty()->isComputed();
});
}
}
1 change: 1 addition & 0 deletions src/Controller/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public function __construct(IlluminateRequest $request)
{
$this->method = $request->getRealMethod();
$this->headers = $request->headers;
$this->cookies = $request->cookies;
$this->query = $request->query;
$this->content = $request->content;
$this->server = $request->server;
Expand Down
2 changes: 1 addition & 1 deletion src/Controller/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ public function getAcceptedContentTypes(): MediaTypes
{
$formatQueryOption = $this->getFormat()->getValue();

if (Str::startsWith($formatQueryOption, ['json', 'xml'])) {
if ($formatQueryOption && Str::startsWith($formatQueryOption, ['json', 'xml'])) {
if (!in_array($formatQueryOption, ['json', 'xml'])) {
throw new BadRequestException(
'invalid_short_format',
Expand Down
6 changes: 3 additions & 3 deletions src/Drivers/CSVEntityType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Flat3\Lodata\Drivers;

use Flat3\Lodata\Annotation\Core\V1\ComputedDefaultValue;
use Flat3\Lodata\Annotation\Core\V1\Computed;
use Flat3\Lodata\DeclaredProperty;
use Flat3\Lodata\EntityType;
use Flat3\Lodata\Type;
Expand All @@ -18,6 +18,6 @@ class CSVEntityType extends EntityType
public function __construct($identifier)
{
parent::__construct($identifier);
$this->setKey((new DeclaredProperty('offset', Type::int64()))->addAnnotation(new ComputedDefaultValue));
$this->setKey((new DeclaredProperty('offset', Type::int64()))->addAnnotation(new Computed));
}
}
}
18 changes: 8 additions & 10 deletions src/Drivers/EloquentEntitySet.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Doctrine\DBAL\Schema\Column;
use Exception;
use Flat3\Lodata\Annotation\Capabilities\V1\DeepInsertSupport;
use Flat3\Lodata\Annotation\Core\V1\ComputedDefaultValue;
use Flat3\Lodata\Annotation\Core\V1\Computed;
use Flat3\Lodata\Annotation\Core\V1\Description;
use Flat3\Lodata\Attributes\LodataIdentifier;
use Flat3\Lodata\Attributes\LodataProperty;
Expand Down Expand Up @@ -187,14 +187,12 @@ public function read(PropertyValue $key): Entity
*/
protected function setModelAttributes(Model $model, PropertyValues $propertyValues): Model
{
foreach ($propertyValues->getDeclaredPropertyValues() as $propertyValue) {
$model->setAttribute(
$this->getPropertySourceName($propertyValue->getProperty()),
$propertyValue->getPrimitive()->toMixed()
return $propertyValues->getDeclaredPropertyValues()->reduce(function(Model $model, PropertyValue $value) {
return $model->setAttribute(
$this->getPropertySourceName($value->getProperty()),
$value->getPrimitive()->toMixed(),
);
}

return $model;
}, $model);
}

/**
Expand Down Expand Up @@ -229,7 +227,7 @@ public function create(PropertyValues $propertyValues): Entity
foreach ($navigationProperty->getConstraints() as $constraint) {
$referencedProperty = $constraint->getReferencedProperty();
$model->setAttribute(
$referencedProperty->getName(),
$this->getPropertySourceName($referencedProperty),
$this->navigationSource->getParent()->getEntityId()->getPrimitive()->toMixed()
);
}
Expand Down Expand Up @@ -677,7 +675,7 @@ public function columnToDeclaredProperty(Column $column): ?DeclaredProperty

$defaultValue = $model->getAttributeValue($column->getName());
if ($defaultValue) {
$property->addAnnotation(new ComputedDefaultValue);
$property->addAnnotation(new Computed);
$property->setDefaultValue($defaultValue);
}

Expand Down
4 changes: 3 additions & 1 deletion src/EntityType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Flat3\Lodata;

use Flat3\Lodata\Annotation\Core\V1\Computed;
use Flat3\Lodata\Annotation\Core\V1\Immutable;
use Flat3\Lodata\Controller\Transaction;
use Flat3\Lodata\Exception\Internal\PathNotHandledException;
use Flat3\Lodata\Facades\Lodata;
Expand Down Expand Up @@ -98,7 +99,8 @@ public function getOpenAPIUpdateSchema(): array
'type' => Constants::oapiObject,
'title' => __('lodata:::name (Update schema)', ['name' => $this->getName()]),
'properties' => (object) $this->getDeclaredProperties()->filter(function (DeclaredProperty $property) {
return $property->getAnnotations()->sliceByClass([Computed::class])->isEmpty() && $property !== $this->getKey();
return $property->getAnnotations()->sliceByClass([Computed::class, Immutable::class])->isEmpty()
&& $property !== $this->getKey();
})->map(function (DeclaredProperty $property) {
return $property->getOpenAPISchema();
})
Expand Down
5 changes: 5 additions & 0 deletions src/GeneratedProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
*/
abstract class GeneratedProperty extends Property
{
public function __construct($name, ?Type $type = null)
{
parent::__construct($name, $type !== null ? $type : Type::untyped());
}

/**
* Generate the property value for this property on the provided entity
* @param ComplexValue $value Entity this property is generated on
Expand Down
13 changes: 13 additions & 0 deletions src/Helper/ObjectArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,19 @@ public function map(callable $callback)
return array_map($callback, $this->array);
}

/**
* @param callable(mixed $initial, mixed $value, mixed $key): mixed $callback
* @param $initial
* @return mixed|null
*/
public function reduce(callable $callback, $initial = null)
{
foreach ($this->array as $key => $value) {
$initial = $callback($initial, $value, $key);
}
return $initial;
}

/**
* Sort the objects in the array
* @param callable $callback
Expand Down
5 changes: 5 additions & 0 deletions src/Primitive.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public function __construct($value = null)
* @return Primitive
*/
abstract public function set($value);

public function allows($value): bool
{
return true;
}

/**
* Get the internal representation of the value
Expand Down
7 changes: 6 additions & 1 deletion src/PrimitiveType.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,9 @@ public function getOpenAPISchema(): array
{
return $this->instance()->getOpenAPISchema();
}
}

public function allowsValue($value): bool
{
return $this->instance()->allows($value);
}
}
25 changes: 22 additions & 3 deletions src/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,11 @@ public function isSearchable(): bool
*/
public function assertAllowsValue($value)
{
if (!$this->isNullable() && $value === null) {
if ($value === null) {
if ($this->isNullable()) {
return;
}

throw new BadRequestException(
'property_not_nullable',
sprintf("The property '%s' cannot be set to null", $this->getName())
Expand All @@ -383,6 +387,13 @@ public function assertAllowsValue($value)
sprintf("The value property '%s' exceeds the maximum length", $this->getName())
);
}

if (!$this->type->allowsValue($value)) {
throw new BadRequestException(
'property_value_invalid',
sprintf("The value property '%s' is not valid", $this->getName()),
);
}
}

/**
Expand All @@ -409,8 +420,16 @@ public function getOpenAPISchema(): array
public function isComputed(): bool
{
return $this instanceof ComputedProperty ||
$this->hasAnnotation(new Computed, Boolean::true()) ||
$this->hasAnnotation(new ComputedDefaultValue, Boolean::true());
$this->hasAnnotation(new Computed, Boolean::true());
}

/**
* Determine whether this property is computed on create operations
* @return bool
*/
public function isComputedDefault(): bool
{
return $this->hasAnnotation(new ComputedDefaultValue, Boolean::true());
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,9 @@ public function is(string $class): bool
{
return is_a($this->factory, $class, true);
}

public function allowsValue($value): bool
{
return true;
}
}
5 changes: 5 additions & 0 deletions src/Type/Guid.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,9 @@ public function getOpenAPISchema(?Property $property = null): array
'pattern' => '^'.Lexer::guid.'$',
]);
}

public function allows($value): bool
{
return Lexer::patternCheck(Lexer::guid, (string) $value);
}
}
6 changes: 3 additions & 3 deletions tests/Drivers/WithNumericCollectionDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Flat3\Lodata\Tests\Drivers;

use Flat3\Lodata\Annotation\Core\V1\ComputedDefaultValue;
use Flat3\Lodata\Annotation\Core\V1\Computed;
use Flat3\Lodata\DeclaredProperty;
use Flat3\Lodata\Drivers\CollectionEntitySet;
use Flat3\Lodata\EntityType;
Expand All @@ -22,7 +22,7 @@ protected function setUpDriver(): void

$entityType = new EntityType('passenger');
$entityType->setOpen();
$entityType->setKey((new DeclaredProperty('id', Type::int64()))->addAnnotation(new ComputedDefaultValue));
$entityType->setKey((new DeclaredProperty('id', Type::int64()))->addAnnotation(new Computed));
$this->addPassengerProperties($entityType);
$entityType->getDeclaredProperty('name')->setSearchable();
$entitySet = new CollectionEntitySet($this->entitySet, $entityType);
Expand All @@ -42,4 +42,4 @@ protected function captureDriverState(): array
{
return array_values(Lodata::getEntitySet($this->entitySet)->getCollection()->toArray());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
<PropertyRef Name="offset"/>
</Key>
<Property Name="offset" Type="Edm.Int64" Nullable="false">
<Annotation Term="Org.OData.Core.V1.ComputedDefaultValue" Bool="true"/>
<Annotation Term="Org.OData.Core.V1.Computed" Bool="true"/>
</Property>
<Property Name="name" Type="Edm.String" Nullable="false" MaxLength="255"/>
<Property Name="age" Type="Edm.Double" Nullable="true"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"offset": {
"$Type": "Edm.Int64",
"$Nullable": false,
"@Org.OData.Core.V1.ComputedDefaultValue": true
"@Org.OData.Core.V1.Computed": true
},
"name": {
"$Type": "Edm.String",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,13 @@
"type": "string",
"nullable": true
}
},
"cp": {
"type": "integer",
"format": "int32",
"minimum": -2147483648,
"maximum": 2147483647,
"nullable": true
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3972,6 +3972,13 @@
"type": "string",
"nullable": true
}
},
"cp": {
"type": "integer",
"format": "int32",
"minimum": -2147483648,
"maximum": 2147483647,
"nullable": true
}
}
},
Expand Down
Loading
Loading