diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitTrackChairsApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitTrackChairsApiController.php index 6d6826ea6..580a5d57b 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitTrackChairsApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitTrackChairsApiController.php @@ -16,6 +16,7 @@ use App\Http\Utils\EpochCellFormatter; use App\Models\Foundation\Main\IGroup; use App\Models\Foundation\Summit\Repositories\ISummitTrackChairRepository; +use App\Security\SummitScopes; use App\Services\Model\ITrackChairService; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Request; @@ -26,9 +27,12 @@ use models\summit\Summit; use models\utils\IEntity; use ModelSerializers\SerializerRegistry; +use OpenApi\Attributes as OA; +use Symfony\Component\HttpFoundation\Response; use utils\Filter; use utils\FilterElement; use Exception; + /** * Class OAuth2SummitTrackChairsApiController * @package App\Http\Controllers @@ -69,10 +73,51 @@ public function __construct use ParametrizedGetAll; - /** - * @param $summit_id - * @return \Illuminate\Http\JsonResponse|mixed - */ + #[OA\Get( + path: "/api/v1/summits/{id}/track-chairs", + operationId: "getAllTrackChairs", + summary: "Get all track chairs for a summit", + description: "Returns different data based on user role: Public view for regular users, Admin view for admins/track chairs", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins, + IGroup::TrackChairs + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "page", description: "Page number", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 1)), + new OA\Parameter(name: "per_page", description: "Items per page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 10)), + new OA\Parameter(name: "filter", description: "Filter query (member_first_name=@value, member_last_name=@value, member_email=@value, member_id==value, track_id==value)", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", description: "Order by (+member_first_name, -member_last_name, +member_email, +id, +track_id)", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "expand", description: "Expand relations (categories, member, summit)", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "relations", description: "Include relations (categories)", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: "OK", + content: new OA\JsonContent(ref: "#/components/schemas/PaginatedTrackChairsResponse") + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] public function getAllBySummit($summit_id){ $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->getResourceServerContext())->find($summit_id); if (is_null($summit)) return $this->error404(); @@ -133,10 +178,56 @@ function () { ); } - /** - * @param $summit_id - * @return mixed - */ + #[OA\Get( + path: "/api/v1/summits/{id}/track-chairs/csv", + operationId: "getAllTrackChairsCSV", + summary: "Get all track chairs for a summit in CSV format", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "filter", description: "Filter query", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", description: "Order by", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter( + name: "columns", + description: "Comma-separated list of columns to export. Allowed: created,last_edited,member_first_name,member_last_name,member_email,member_id,categories,summit_id", + in: "query", + required: false, + schema: new OA\Schema(type: "string"), + example: "member_first_name,member_last_name,member_email,categories" + ), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: "OK - CSV file download", + content: new OA\MediaType( + mediaType: "text/csv", + schema: new OA\Schema(type: "string", format: "binary") + ) + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] public function getAllBySummitCSV($summit_id){ $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->getResourceServerContext())->find($summit_id); @@ -294,12 +385,238 @@ protected function getChildFromSummit(Summit $summit, $child_id):?IEntity{ return $summit->getTrackChair(intval($child_id)); } - /** - * @param $summit_id - * @param $track_chair_id - * @param $track_id - * @return \Illuminate\Http\JsonResponse|mixed - */ + #[OA\Post( + path: "/api/v1/summits/{id}/track-chairs", + operationId: "addTrackChair", + summary: "Add a track chair to a summit", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: "#/components/schemas/TrackChairAddRequest") + ), + responses: [ + new OA\Response( + response: Response::HTTP_CREATED, + description: "Created", + content: new OA\JsonContent(ref: "#/components/schemas/AdminSummitTrackChair") + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation Error"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] + public function add($summit_id){ + return $this->processRequest(function() use($summit_id){ + $summit = SummitFinderStrategyFactory::build($this->getSummitRepository(), $this->getResourceServerContext())->find($summit_id); + if (is_null($summit)) return $this->error404(); + return $this->_add($summit, $this->getJsonPayload($this->getAddValidationRules())); + }); + } + + #[OA\Get( + path: "/api/v1/summits/{id}/track-chairs/{track_chair_id}", + operationId: "getTrackChair", + summary: "Get a track chair by id", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins, + IGroup::TrackChairs + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "track_chair_id", description: "Track chair ID", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "expand", description: "Expand relations (categories, member, summit)", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: "OK", + content: new OA\JsonContent( + oneOf: [ + new OA\Schema(ref: "#/components/schemas/SummitTrackChair"), + new OA\Schema(ref: "#/components/schemas/AdminSummitTrackChair") + ] + ) + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] + public function get($summit_id, $track_chair_id){ + return $this->processRequest(function() use($summit_id, $track_chair_id){ + $summit = SummitFinderStrategyFactory::build($this->getSummitRepository(), $this->getResourceServerContext())->find($summit_id); + if (is_null($summit)) return $this->error404(); + return $this->_get($summit, $track_chair_id); + }); + } + + #[OA\Put( + path: "/api/v1/summits/{id}/track-chairs/{track_chair_id}", + operationId: "updateTrackChair", + summary: "Update a track chair's categories", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "track_chair_id", description: "Track chair ID", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: "#/components/schemas/TrackChairUpdateRequest") + ), + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: "OK", + content: new OA\JsonContent(ref: "#/components/schemas/AdminSummitTrackChair") + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation Error"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] + public function update($summit_id, $track_chair_id){ + return $this->processRequest(function() use($summit_id, $track_chair_id){ + $summit = SummitFinderStrategyFactory::build($this->getSummitRepository(), $this->getResourceServerContext())->find($summit_id); + if (is_null($summit)) return $this->error404(); + return $this->_update($summit, $track_chair_id, $this->getJsonPayload($this->getUpdateValidationRules())); + }); + } + + #[OA\Delete( + path: "/api/v1/summits/{id}/track-chairs/{track_chair_id}", + operationId: "deleteTrackChair", + summary: "Delete a track chair", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "track_chair_id", description: "Track chair ID", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: "No Content"), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] + public function delete($summit_id, $track_chair_id){ + return $this->processRequest(function() use($summit_id, $track_chair_id){ + $summit = SummitFinderStrategyFactory::build($this->getSummitRepository(), $this->getResourceServerContext())->find($summit_id); + if (is_null($summit)) return $this->error404(); + return $this->_delete($summit, $track_chair_id); + }); + } + + #[OA\Post( + path: "/api/v1/summits/{id}/track-chairs/{track_chair_id}/categories/{track_id}", + operationId: "addCategoryToTrackChair", + summary: "Add a track/category to a track chair", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "track_chair_id", description: "Track chair ID", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "track_id", description: "Track/Category ID", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: "OK", + content: new OA\JsonContent(ref: "#/components/schemas/AdminSummitTrackChair") + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation Error"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] public function addTrack2TrackChair($summit_id, $track_chair_id, $track_id){ try{ $summit = SummitFinderStrategyFactory::build($this->getSummitRepository(), $this->getResourceServerContext())->find($summit_id); @@ -329,12 +646,45 @@ public function addTrack2TrackChair($summit_id, $track_chair_id, $track_id){ } } - /** - * @param $summit_id - * @param $track_chair_id - * @param $track_id - * @return \Illuminate\Http\JsonResponse|mixed - */ + #[OA\Delete( + path: "/api/v1/summits/{id}/track-chairs/{track_chair_id}/categories/{track_id}", + operationId: "removeCategoryFromTrackChair", + summary: "Remove a track/category from a track chair", + tags: ["Track Chairs"], + security: [ + [ + 'summit_track_chairs_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::TrackChairsAdmins + ] + ], + parameters: [ + new OA\Parameter(name: "id", description: "Summit ID", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "track_chair_id", description: "Track chair ID", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "track_id", description: "Track/Category ID", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: "OK", + content: new OA\JsonContent(ref: "#/components/schemas/AdminSummitTrackChair") + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation Error"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] public function removeFromTrackChair($summit_id, $track_chair_id, $track_id){ try{ $summit = SummitFinderStrategyFactory::build($this->getSummitRepository(), $this->getResourceServerContext())->find($summit_id); @@ -364,4 +714,4 @@ public function removeFromTrackChair($summit_id, $track_chair_id, $track_id){ } } -} \ No newline at end of file +} diff --git a/app/Swagger/Models/AdminMemberSchema.php b/app/Swagger/Models/AdminMemberSchema.php new file mode 100644 index 000000000..363c7878d --- /dev/null +++ b/app/Swagger/Models/AdminMemberSchema.php @@ -0,0 +1,26 @@ + 'Read Summit Data', + SummitScopes::ReadAllSummitData => 'Read All Summit Data', + SummitScopes::WriteSummitData => 'Write Summit Data', + ], + ), + ], + ) +] +class TrackChairsAuthSchema{} diff --git a/app/Swagger/SummitProposedScheduleSchemas.php b/app/Swagger/SummitProposedScheduleSchemas.php index c401815ab..bb632ccee 100644 --- a/app/Swagger/SummitProposedScheduleSchemas.php +++ b/app/Swagger/SummitProposedScheduleSchemas.php @@ -10,16 +10,16 @@ new OA\Property(property: "id", type: "integer", example: 1), new OA\Property(property: "created", type: "integer", description: "Unix timestamp", example: 1640995200), new OA\Property(property: "last_edited", type: "integer", description: "Unix timestamp", example: 1640995200), - new OA\Property(property: "allowed_timeframes", type: "array", items: new OA\Items(type: ["integer", "SummitProposedScheduleAllowedDay"]), description: "Array of allowed timeframe IDs or objects when expanded", nullable: true) - ], - anyOf: [ + new OA\Property(property: "allowed_timeframes", type: "array", items: new OA\Items(oneOf: [ + new OA\Schema(type: 'integer'), + new OA\Schema(ref: '#/components/schemas/SummitProposedScheduleAllowedDay') + ]), description: "Array of allowed timeframe IDs or objects when expand=allowed_timeframes",), new OA\Property(property: "location_id", type: "integer", example: 10, description: "only when not expanded"), - new OA\Property(property: "location", type: "SummitAbstractLocation", description: "only when expanded"), - new OA\Property(property: "track_id", type: "integer", example: 5, description: "only when not expanded"), - new OA\Property(property: "track", type: "PresentationCategory", description: "only when expanded"), + new OA\Property(property: "location", ref: '#/components/schemas/SummitAbstractLocation', description: "only when expand=location"), + new OA\Property(property: "track_id", type: "integer", example: 5, description: "PresentationCategory ID, use expand=track for full object details"), ], )] -class SummitProposedScheduleAllowedLocation {} +class SummitProposedScheduleAllowedLocationSchema {} #[OA\Schema( schema: "SummitProposedScheduleAllowedLocationRequest", @@ -28,7 +28,7 @@ class SummitProposedScheduleAllowedLocation {} new OA\Property(property: "location_id", type: "integer", example: 10) ] )] -class SummitProposedScheduleAllowedLocationRequest {} +class SummitProposedScheduleAllowedLocationRequestSchema {} #[OA\Schema( schema: "SummitProposedScheduleAllowedDay", @@ -42,7 +42,7 @@ class SummitProposedScheduleAllowedLocationRequest {} new OA\Property(property: "closing_hour", type: "integer", description: "Closing hour in HHMM format (0-2359)", example: 1700) ] )] -class SummitProposedScheduleAllowedDay {} +class SummitProposedScheduleAllowedDaySchema {} #[OA\Schema( schema: "SummitProposedScheduleAllowedDayAddRequest", @@ -53,7 +53,7 @@ class SummitProposedScheduleAllowedDay {} new OA\Property(property: "closing_hour", type: "integer", description: "Closing hour in HHMM format (0-2359)", example: 1700) ] )] -class SummitProposedScheduleAllowedDayAddRequest {} +class SummitProposedScheduleAllowedDayAddRequestSchema {} #[OA\Schema( schema: "SummitProposedScheduleAllowedDayUpdateRequest", @@ -63,4 +63,4 @@ class SummitProposedScheduleAllowedDayAddRequest {} new OA\Property(property: "closing_hour", type: "integer", description: "Closing hour in HHMM format (0-2359)", example: 1700) ] )] -class SummitProposedScheduleAllowedDayUpdateRequest {} +class SummitProposedScheduleAllowedDayUpdateRequestSchema {} diff --git a/app/Swagger/SummitTrackChairSchemas.php b/app/Swagger/SummitTrackChairSchemas.php new file mode 100644 index 000000000..61a7e7f81 --- /dev/null +++ b/app/Swagger/SummitTrackChairSchemas.php @@ -0,0 +1,110 @@ +