Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ad23675
Embedded Collections camps/periods/days
pmattmann Jul 18, 2021
97f7408
Merge remote-tracking branch 'ecamp/devel' into cosinus/api-validation
pmattmann Jul 18, 2021
7d76aa2
Add Camp:write
pmattmann Jul 28, 2021
761acd0
selectively embed referenced entitiy
pmattmann Jul 29, 2021
e5d69f7
excample fixed
pmattmann Jul 29, 2021
a2f3007
fix post on camp
pmattmann Jul 29, 2021
90f03ee
code-review changes
pmattmann Jul 30, 2021
23c83ee
Sort named arguments
carlobeltrame Aug 4, 2021
7205e60
Make the camp name writable again
carlobeltrame Aug 4, 2021
3ff4f41
Make dayResponsibles readable again
carlobeltrame Aug 4, 2021
e7c3516
Revert readability of camp on day
carlobeltrame Aug 4, 2021
c94ed0d
Fix return type declarations to make examples in swagger work automat…
carlobeltrame Aug 4, 2021
5847e8c
getExamplePayload needs to take into account different schema for dif…
carlobeltrame Aug 5, 2021
8153a0a
Always add some defaults to the context
carlobeltrame Aug 6, 2021
f6d07a2
Add missing normalization groups so the tests pass again
carlobeltrame Aug 6, 2021
25bd154
Work around broken circular reference detection in HAL format
carlobeltrame Aug 6, 2021
eea56b9
Ignore the embedded getters when trying to read the Doctrine metadata
carlobeltrame Aug 6, 2021
c98a70a
Consistently use our group name system for all entities
carlobeltrame Aug 9, 2021
5b029cd
Temporarily replace API platform's eager loading extension
carlobeltrame Aug 9, 2021
4895495
Merge branch 'devel' into feature/embedded-entities
carlobeltrame Aug 9, 2021
4e08a3e
Const for NormalizationCtx; use SwaggerDefName
pmattmann Aug 17, 2021
c65be86
Merge remote-tracking branch 'ecamp/devel' into feature/embedded-enti…
pmattmann Aug 30, 2021
b0cb899
prevent adding same alias twice; doctrine does not like it
pmattmann Aug 30, 2021
e69b947
use self for ITEM_NORMALIZATION_CONTEXT
pmattmann Aug 30, 2021
1e74ed3
Merge remote-tracking branch 'origin/devel' into feature/embedded-ent…
carlobeltrame Sep 4, 2021
476731b
refactor: Extract method for preventing duplicate selects
carlobeltrame Sep 4, 2021
a62928f
Comment out unused code to prevent lowering the code coverage too much
carlobeltrame Sep 7, 2021
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
19 changes: 19 additions & 0 deletions api/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,30 @@ services:
- '@api_platform.filter_locator'
- '@serializer.name_converter.metadata_aware'

App\Serializer\Normalizer\CircularReferenceDetectingHalItemNormalizer:
decorates: 'api_platform.hal.normalizer.item'

App\Serializer\Normalizer\CollectionItemsNormalizer:
decorates: 'api_platform.hal.normalizer.collection'

App\Serializer\SerializerContextBuilder:
decorates: 'api_platform.serializer.context_builder'

App\Serializer\PreventAutomaticEmbeddingPropertyMetadataFactory:
decorates: 'api_platform.metadata.property.metadata_factory'
# Priority should be 1 lower than the one of SerializerPropertyMetadataFactory, see
# https://github.com/api-platform/core/blob/main/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml#L65
decoration_priority: 29

App\OpenApi\JwtDecorator:
decorates: 'api_platform.openapi.factory'

ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\EagerLoadingExtension:
class: App\Doctrine\EagerLoadingExtension
api_platform.doctrine.orm.query_extension.eager_loading:
class: App\Doctrine\EagerLoadingExtension
App\Doctrine\EagerLoadingExtension:
public: false

App\Service\MailService:
public: true
328 changes: 328 additions & 0 deletions api/src/Doctrine/EagerLoadingExtension.php

Large diffs are not rendered by default.

25 changes: 23 additions & 2 deletions api/src/Entity/Activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

/**
Expand All @@ -22,10 +23,12 @@
itemOperations: [
'get',
'patch' => [
'validation_groups' => ['Default', 'activity:update'],
'validation_groups' => ['Default', 'update'],
],
'delete',
],
denormalizationContext: ['groups' => ['write']],
normalizationContext: ['groups' => ['read']],
)]
#[ApiFilter(SearchFilter::class, properties: ['camp'])]
class Activity extends AbstractContentNodeOwner implements BelongsToCampInterface {
Expand All @@ -35,6 +38,7 @@ class Activity extends AbstractContentNodeOwner implements BelongsToCampInterfac
* @ORM\OneToMany(targetEntity="ScheduleEntry", mappedBy="activity", orphanRemoval=true)
*/
#[ApiProperty(writable: false, example: '["/schedule_entries/1a2b3c4d"]')]
#[Groups(['read'])]
public Collection $scheduleEntries;

/**
Expand All @@ -53,6 +57,7 @@ class Activity extends AbstractContentNodeOwner implements BelongsToCampInterfac
*/
#[Assert\DisableAutoMapping] // camp is set in the DataPersister
#[ApiProperty(writable: false, example: '/camps/1a2b3c4d')]
#[Groups(['read'])]
public ?Camp $camp = null;

/**
Expand All @@ -63,7 +68,8 @@ class Activity extends AbstractContentNodeOwner implements BelongsToCampInterfac
* @ORM\JoinColumn(nullable=false)
*/
#[ApiProperty(example: '/categories/1a2b3c4d')]
#[AssertBelongsToSameCamp(groups: ['activity:update'])]
#[AssertBelongsToSameCamp(groups: ['update'])]
#[Groups(['read', 'write'])]
public ?Category $category = null;

/**
Expand All @@ -72,6 +78,7 @@ class Activity extends AbstractContentNodeOwner implements BelongsToCampInterfac
* @ORM\Column(type="text")
*/
#[ApiProperty(example: 'Sportolympiade')]
#[Groups(['read', 'write'])]
public ?string $title = null;

/**
Expand All @@ -80,6 +87,7 @@ class Activity extends AbstractContentNodeOwner implements BelongsToCampInterfac
* @ORM\Column(type="text")
*/
#[ApiProperty(example: 'Spielwiese')]
#[Groups(['read', 'write'])]
public string $location = '';

public function __construct() {
Expand All @@ -98,11 +106,24 @@ public function setRootContentNode(?ContentNode $rootContentNode) {
}

#[Assert\DisableAutoMapping]
#[Groups(['read'])]
public function getRootContentNode(): ?ContentNode {
// Getter is here to add annotations to parent class property
return $this->rootContentNode;
}

/**
* Overridden in order to add annotations.
*
* {@inheritdoc}
*
* @return ContentNode[]
*/
#[Groups(['read'])]
public function getContentNodes(): array {
return parent::getContentNodes();
}

/**
* The list of people that are responsible for planning or carrying out this activity.
*
Expand Down
5 changes: 5 additions & 0 deletions api/src/Entity/ActivityResponsible.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Validator\AssertBelongsToSameCamp;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

/**
Expand All @@ -20,6 +21,8 @@
#[ApiResource(
collectionOperations: ['get', 'post'],
itemOperations: ['get', 'delete'],
denormalizationContext: ['groups' => ['write']],
normalizationContext: ['groups' => ['read']],
)]
#[UniqueEntity(fields: ['activity', 'campCollaboration'])]
class ActivityResponsible extends BaseEntity implements BelongsToCampInterface {
Expand All @@ -31,6 +34,7 @@ class ActivityResponsible extends BaseEntity implements BelongsToCampInterface {
*/
#[Assert\NotNull]
#[ApiProperty(example: '/activities/1a2b3c4d')]
#[Groups(['read', 'write'])]
public ?Activity $activity = null;

/**
Expand All @@ -41,6 +45,7 @@ class ActivityResponsible extends BaseEntity implements BelongsToCampInterface {
*/
#[ApiProperty(example: '/camp_collaborations/1a2b3c4d')]
#[AssertBelongsToSameCamp]
#[Groups(['read', 'write'])]
public ?CampCollaboration $campCollaboration = null;

#[ApiProperty(readable: false)]
Expand Down
2 changes: 2 additions & 0 deletions api/src/Entity/BaseEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Serializer\Annotation\Groups;

/**
* @ORM\MappedSuperclass
Expand All @@ -25,6 +26,7 @@ abstract class BaseEntity {
* @ORM\CustomIdGenerator(class=IdGenerator::class)
*/
#[ApiProperty(writable: false, example: '1a2b3c4d')]
#[Groups(['read'])]
protected string $id;

/**
Expand Down
65 changes: 47 additions & 18 deletions api/src/Entity/Camp.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,37 @@
'post' => [
'security' => 'is_fully_authenticated()',
'input_formats' => ['jsonld', 'jsonapi', 'json'],
'validation_groups' => ['Default', 'camp:create'],
'validation_groups' => ['Default', 'create'],
'denormalization_context' => ['groups' => ['write', 'create']],
'normalization_context' => self::ITEM_NORMALIZATION_CONTEXT,
],
],
itemOperations: [
'get' => ['security' => 'object.owner == user or is_granted("ROLE_ADMIN")'],
'get' => [
'security' => 'object.owner == user or is_granted("ROLE_ADMIN")',
'normalization_context' => self::ITEM_NORMALIZATION_CONTEXT,
],
'patch' => [
'security' => 'object.owner == user or is_granted("ROLE_ADMIN")',
'denormalization_context' => [
'groups' => ['camp:update'],
'allow_extra_attributes' => false,
],
'denormalization_context' => ['groups' => ['write', 'update']],
'normalization_context' => self::ITEM_NORMALIZATION_CONTEXT,
],
'delete' => ['security' => 'object.owner == user or is_granted("ROLE_ADMIN")'],
]
],
denormalizationContext: ['groups' => ['write']],
normalizationContext: ['groups' => ['read']],
)]
class Camp extends BaseEntity implements BelongsToCampInterface {
public const ITEM_NORMALIZATION_CONTEXT = [
'groups' => ['read', 'Camp:Periods', 'Period:Days'],
'swagger_definition_name' => 'read',
];

/**
* @ORM\OneToMany(targetEntity="CampCollaboration", mappedBy="camp", orphanRemoval=true)
*/
#[SerializedName('campCollaborations')]
#[Groups(['read'])]
public Collection $collaborations;

/**
Expand All @@ -56,9 +67,12 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
* @ORM\OrderBy({"start": "ASC"})
*/
#[Assert\Valid]
#[Assert\Count(min: 1, groups: ['camp:create'])]
#[ApiProperty(writableLink: true, example: '[{ "description": "Hauptlager", "start": "2022-01-01", "end": "2022-01-08" }]')]
#[Groups(['Default'])]
#[Assert\Count(min: 1, groups: ['create'])]
#[ApiProperty(
writableLink: true,
example: '[{ "description": "Hauptlager", "start": "2022-01-01", "end": "2022-01-08" }]',
)]
#[Groups(['read', 'create'])]
public Collection $periods;

/**
Expand All @@ -67,6 +81,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
* @ORM\OneToMany(targetEntity="Category", mappedBy="camp", orphanRemoval=true)
*/
#[ApiProperty(writable: false, example: '["/categories/1a2b3c4d"]')]
#[Groups(['read'])]
public Collection $categories;

/**
Expand All @@ -76,6 +91,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
* @ORM\OneToMany(targetEntity="Activity", mappedBy="camp", orphanRemoval=true)
*/
#[ApiProperty(writable: false, example: '["/activities/1a2b3c4d"]')]
#[Groups(['read'])]
public Collection $activities;

/**
Expand All @@ -85,6 +101,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
* @ORM\OneToMany(targetEntity="MaterialList", mappedBy="camp", orphanRemoval=true)
*/
#[ApiProperty(writable: false, example: '["/material_lists/1a2b3c4d"]')]
#[Groups(['read'])]
public Collection $materialLists;

/**
Expand All @@ -104,7 +121,8 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
*/
#[Assert\Type('bool')]
#[Assert\DisableAutoMapping]
#[ApiProperty(example: false, writable: false)]
#[ApiProperty(example: true, writable: false)]
#[Groups(['read'])]
public bool $isPrototype = false;

/**
Expand All @@ -116,7 +134,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
#[InputFilter\CleanHTML]
#[Assert\NotBlank]
#[ApiProperty(example: 'SoLa 2022')]
#[Groups(['Default', 'camp:update'])]
#[Groups(['read', 'write'])]
public string $name;

/**
Expand All @@ -129,7 +147,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
#[Assert\NotBlank]
#[Assert\Length(max: 32)]
#[ApiProperty(example: 'Abteilungs-Sommerlager 2022')]
#[Groups(['Default', 'camp:update'])]
#[Groups(['read', 'write'])]
public string $title;

/**
Expand All @@ -141,7 +159,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
#[InputFilter\CleanHTML]
#[Assert\Length(max: 128)]
#[ApiProperty(example: 'Piraten')]
#[Groups(['Default', 'camp:update'])]
#[Groups(['read', 'write'])]
public ?string $motto = null;

/**
Expand All @@ -153,7 +171,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
#[InputFilter\CleanHTML]
#[Assert\Length(max: 128)]
#[ApiProperty(example: 'Wiese hinter der alten Mühle')]
#[Groups(['Default', 'camp:update'])]
#[Groups(['read', 'write'])]
public ?string $addressName = null;

/**
Expand All @@ -165,7 +183,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
#[InputFilter\CleanHTML]
#[Assert\Length(max: 128)]
#[ApiProperty(example: 'Schönriedweg 23')]
#[Groups(['Default', 'camp:update'])]
#[Groups(['read', 'write'])]
public ?string $addressStreet = null;

/**
Expand All @@ -177,7 +195,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
#[InputFilter\CleanHTML]
#[Assert\Length(max: 128)]
#[ApiProperty(example: '1234')]
#[Groups(['Default', 'camp:update'])]
#[Groups(['read', 'write'])]
public ?string $addressZipcode = null;

/**
Expand All @@ -189,7 +207,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
#[InputFilter\CleanHTML]
#[Assert\Length(max: 128)]
#[ApiProperty(example: 'Hintertüpfingen')]
#[Groups(['Default', 'camp:update'])]
#[Groups(['read', 'write'])]
public ?string $addressCity = null;

/**
Expand All @@ -201,6 +219,7 @@ class Camp extends BaseEntity implements BelongsToCampInterface {
*/
#[Assert\DisableAutoMapping]
#[ApiProperty(writable: false)]
#[Groups(['read'])]
public ?User $creator = null;

/**
Expand All @@ -222,6 +241,16 @@ public function __construct() {
$this->materialLists = new ArrayCollection();
}

/**
* @return Period[]
*/
#[ApiProperty(readableLink: true)]
#[SerializedName('periods')]
#[Groups(['Camp:Periods'])]
public function getEmbeddedPeriods(): array {
return $this->periods->getValues();
}

#[ApiProperty(readable: false)]
public function getCamp(): ?Camp {
return $this;
Expand Down
Loading