Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 6 additions & 2 deletions api/src/Entity/Camp.php
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,13 @@ public function getCamp(): ?Camp {
*
* @return CampCollaboration[]
*/
#[ApiProperty(writable: false, example: '["/camp_collaborations/1a2b3c4d"]')]
#[ApiProperty(
writable: false,
uriTemplate: CampCollaboration::CAMP_SUBRESOURCE_URI_TEMPLATE,
example: '["/camps/1a2b3c4d/camp_collaborations"]'
)]
public function getCampCollaborations(): array {
return $this->collaborations->getValues();
return [];
}

/**
Expand Down
15 changes: 15 additions & 0 deletions api/src/Entity/CampCollaboration.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
Expand Down Expand Up @@ -60,6 +61,18 @@
security: 'is_fully_authenticated()',
normalizationContext: self::COLLECTION_NORMALIZATION_CONTEXT
),
new GetCollection(
uriTemplate: self::CAMP_SUBRESOURCE_URI_TEMPLATE,
uriVariables: [
'campId' => new Link(
toProperty: 'camp',
fromClass: Camp::class,
security: 'is_granted("CAMP_COLLABORATOR", camp) or is_granted("CAMP_IS_PROTOTYPE", camp)'
),
],
security: 'is_fully_authenticated()',
normalizationContext: self::COLLECTION_NORMALIZATION_CONTEXT
),
new Post(
processor: CampCollaborationCreateProcessor::class,
denormalizationContext: ['groups' => ['write', 'create']],
Expand Down Expand Up @@ -88,6 +101,8 @@
#[ORM\UniqueConstraint(name: 'inviteEmail_camp_unique', fields: ['inviteEmail', 'camp'])]
#[ORM\Index(columns: ['status'])]
class CampCollaboration extends BaseEntity implements BelongsToCampInterface {
public const CAMP_SUBRESOURCE_URI_TEMPLATE = '/camps/{campId}/camp_collaborations{._format}';

public const ITEM_NORMALIZATION_CONTEXT = [
'groups' => ['read', 'CampCollaboration:User'],
'swagger_definition_name' => 'read',
Expand Down
41 changes: 41 additions & 0 deletions api/tests/Api/CampCollaborations/ListCampCollaborationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ public function testListCampCollaborationsFilteredByCampIsAllowedForCollaborator
], $response->toArray()['_links']['items']);
}

public function testListCampCollaborationsAsCampSubResourceIsAllowedForCollaborator() {
$camp = static::getFixture('camp1');
$response = static::createClientWithCredentials()->request('GET', "/camps/{$camp->getId()}/camp_collaborations");
$this->assertResponseStatusCodeSame(200);
$this->assertJsonContains([
'totalItems' => 6,
'_links' => [
'items' => [],
],
'_embedded' => [
'items' => [],
],
]);
$this->assertEqualsCanonicalizing([
['href' => $this->getIriFor('campCollaboration1manager')],
['href' => $this->getIriFor('campCollaboration2member')],
['href' => $this->getIriFor('campCollaboration3guest')],
['href' => $this->getIriFor('campCollaboration4invited')],
['href' => $this->getIriFor('campCollaboration5inactive')],
['href' => $this->getIriFor('campCollaboration6manager')],
], $response->toArray()['_links']['items']);
}

public function testListCampCollaborationsFilteredByCampIsDeniedForUnrelatedUser() {
$camp = static::getFixture('camp1');
$response = static::createClientWithCredentials(['email' => static::$fixtures['user4unrelated']->getEmail()])
Expand All @@ -81,6 +104,15 @@ public function testListCampCollaborationsFilteredByCampIsDeniedForUnrelatedUser
$this->assertArrayNotHasKey('items', $response->toArray()['_links']);
}

public function testListCampCollaborationsAsCampSubResourceIsDeniedForUnrelatedUser() {
$camp = static::getFixture('camp1');
static::createClientWithCredentials(['email' => static::$fixtures['user4unrelated']->getEmail()])
->request('GET', "/camps/{$camp->getId()}/camp_collaborations")
;

$this->assertResponseStatusCodeSame(404);
}

public function testListCampCollaborationsFilteredByCampIsDeniedForInactiveCollaborator() {
$camp = static::getFixture('camp1');
$response = static::createClientWithCredentials(['email' => static::$fixtures['user5inactive']->getEmail()])
Expand All @@ -91,6 +123,15 @@ public function testListCampCollaborationsFilteredByCampIsDeniedForInactiveColla
$this->assertArrayNotHasKey('items', $response->toArray()['_links']);
}

public function testListCampCollaborationsAsCampSubResourceIsDeniedForInactiveCollaborator() {
$camp = static::getFixture('camp1');
static::createClientWithCredentials(['email' => static::$fixtures['user5inactive']->getEmail()])
->request('GET', "/camps/{$camp->getId()}/camp_collaborations")
;

$this->assertResponseStatusCodeSame(404);
}

public function testListCampCollaborationsFilteredByCampPrototypeIsAllowedForUnrelatedUser() {
$camp = static::getFixture('campPrototype');
$response = static::createClientWithCredentials()->request('GET', '/camp_collaborations?camp=%2Fcamps%2F'.$camp->getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24484,6 +24484,132 @@ paths:
summary: 'Retrieves the collection of ActivityProgressLabel resources.'
tags:
- ActivityProgressLabel
'/camps/{campId}/camp_collaborations':
get:
deprecated: false
description: 'Retrieves the collection of CampCollaboration resources.'
operationId: api_camps_campIdcamp_collaborations_get_collection
parameters:
-
allowEmptyValue: false
allowReserved: false
deprecated: false
description: ''
explode: false
in: query
name: activityResponsibles.activity
required: false
schema:
type: string
style: form
-
allowEmptyValue: false
allowReserved: false
deprecated: false
description: ''
explode: false
in: query
name: camp
required: false
schema:
type: string
style: form
-
allowEmptyValue: false
allowReserved: false
deprecated: false
description: ''
explode: true
in: query
name: 'activityResponsibles.activity[]'
required: false
schema:
items:
type: string
type: array
style: form
-
allowEmptyValue: false
allowReserved: false
deprecated: false
description: ''
explode: true
in: query
name: 'camp[]'
required: false
schema:
items:
type: string
type: array
style: form
-
allowEmptyValue: false
allowReserved: false
deprecated: false
description: 'CampCollaboration identifier'
explode: false
in: path
name: campId
required: true
schema:
type: string
style: simple
responses:
200:
content:
application/hal+json:
schema:
properties:
_embedded: { anyOf: [{ properties: { item: { items: { $ref: '#/components/schemas/CampCollaboration.jsonhal-read_CampCollaboration.Camp_CampCollaboration.User' }, type: array } }, type: object }, { type: object }] }
_links: { properties: { first: { properties: { href: { format: iri-reference, type: string } }, type: object }, last: { properties: { href: { format: iri-reference, type: string } }, type: object }, next: { properties: { href: { format: iri-reference, type: string } }, type: object }, previous: { properties: { href: { format: iri-reference, type: string } }, type: object }, self: { properties: { href: { format: iri-reference, type: string } }, type: object } }, type: object }
itemsPerPage: { minimum: 0, type: integer }
totalItems: { minimum: 0, type: integer }
required:
- _embedded
- _links
type: object
application/json:
schema:
items:
$ref: '#/components/schemas/CampCollaboration-read_CampCollaboration.Camp_CampCollaboration.User'
type: array
application/ld+json:
schema:
properties:
member: { items: { $ref: '#/components/schemas/CampCollaboration.jsonld-read_CampCollaboration.Camp_CampCollaboration.User' }, type: array }
search: { properties: { '@type': { type: string }, mapping: { items: { properties: { '@type': { type: string }, property: { type: ['null', string] }, required: { type: boolean }, variable: { type: string } }, type: object }, type: array }, template: { type: string }, variableRepresentation: { type: string } }, type: object }
totalItems: { minimum: 0, type: integer }
view: { example: { '@id': string, first: string, last: string, next: string, previous: string, type: string }, properties: { '@id': { format: iri-reference, type: string }, '@type': { type: string }, first: { format: iri-reference, type: string }, last: { format: iri-reference, type: string }, next: { format: iri-reference, type: string }, previous: { format: iri-reference, type: string } }, type: object }
required:
- member
type: object
application/vnd.api+json:
schema:
items:
$ref: '#/components/schemas/CampCollaboration.jsonapi'
type: array
text/html:
schema:
items:
$ref: '#/components/schemas/CampCollaboration-read_CampCollaboration.Camp_CampCollaboration.User'
type: array
description: 'CampCollaboration collection'
403:
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
application/ld+json:
schema:
$ref: '#/components/schemas/Error.jsonld'
application/problem+json:
schema:
$ref: '#/components/schemas/Error'
description: Forbidden
links: []
summary: 'Retrieves the collection of CampCollaboration resources.'
tags:
- CampCollaboration
'/camps/{campId}/categories':
get:
deprecated: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/activity_progress_labels/item: 7
/activity_responsibles: 6
/activity_responsibles/item: 8
/camps: 20
/camps: 15
/camps/item: 19
/camp_collaborations: 16
/camp_collaborations/item: 7
Expand All @@ -25,7 +25,7 @@
/material_lists: 6
/material_lists/item: 7
/periods: 6
/periods/item: 15
/periods/item: 12
/profiles: 6
/profiles/item: 6
/schedule_entries: 23
Expand All @@ -35,7 +35,7 @@
'/activity_progress_labels?camp=': 6
'/activity_responsibles?activity.camp=': 6
'/camp_collaborations?camp=': 10
'/camp_collaborations?activityResponsibles.activity=': 12
'/camp_collaborations?activityResponsibles.activity=': 9
'/categories?camp=': 9
'/content_types?categories=': 6
'/day_responsibles?day.period=': 6
Expand Down