Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php namespace App\Http\Controllers;
/*
* Copyright 2026 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

use App\Http\ValidationRulesFactories\AbstractValidationRulesFactory;

/**
* Class SummitSponsorshipAddOnTypeValidationRulesFactory
* @package App\Http\Controllers
*/
final class SummitSponsorshipAddOnTypeValidationRulesFactory extends AbstractValidationRulesFactory
{
public static function buildForAdd(array $payload = []): array
{
return [
'name' => 'required|string|max:255',
];
}

public static function buildForUpdate(array $payload = []): array
{
return [
'name' => 'sometimes|string|max:255',
];
Comment on lines +30 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject empty name values on update.

Line 33 allows name: "" (empty string) during updates, which can persist blank lookup names.

Suggested fix
-            'name' => 'sometimes|string|max:255',
+            'name' => 'sometimes|required|string|max:255',
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 30-30: Avoid unused parameters such as '$payload'. (undefined)

(UnusedFormalParameter)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/Http/Controllers/Apis/Protected/Summit/Factories/SummitSponsorshipAddOnTypeValidationRulesFactory.php`
around lines 30 - 34, The validation rule for the 'name' field in the
buildForUpdate method of SummitSponsorshipAddOnTypeValidationRulesFactory
currently permits empty strings because it only validates that the field is a
string when present. Add the 'filled' validation rule to the 'name' field
validation chain to ensure that when the name is provided, it must not be empty,
preventing blank lookup names from being persisted during updates.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* limitations under the License.
**/
use App\Http\ValidationRulesFactories\AbstractValidationRulesFactory;
use models\summit\SummitSponsorshipAddOn;

/**
* Class SummitSponsorshipAddOnsValidationRulesFactory
Expand All @@ -24,16 +23,18 @@ final class SummitSponsorshipAddOnsValidationRulesFactory extends AbstractValida
public static function buildForAdd(array $payload = []): array
{
return [
'name' => 'required|string|max:255',
'type' => 'required|string|in:'.join(',', SummitSponsorshipAddOn::ValidTypes),
'name' => 'required|string|max:255',
'type_id' => 'required_without:type|integer|prohibited_with:type',
'type' => 'required_without:type_id|string|max:255|prohibited_with:type_id',
];
}

public static function buildForUpdate(array $payload = []): array
{
return [
'name' => 'sometimes|string|max:255',
'type' => 'sometimes|string|required_with:name|in:'.join(',', SummitSponsorshipAddOn::ValidTypes),
'name' => 'sometimes|string|max:255',
'type_id' => 'sometimes|integer|prohibited_with:type',
'type' => 'sometimes|string|max:255|prohibited_with:type_id',
];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
<?php namespace App\Http\Controllers;
/*
* Copyright 2026 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

use App\Models\Foundation\Main\IGroup;
use App\Models\Foundation\Summit\Repositories\ISummitSponsorshipAddOnTypeRepository;
use App\ModelSerializers\SerializerUtils;
use App\Security\SummitScopes;
use App\Services\Model\ISummitSponsorshipAddOnTypeService;
use models\oauth2\IResourceServerContext;
use models\summit\ISummitRepository;
use ModelSerializers\SerializerRegistry;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpFoundation\Response;

/**
* Class OAuth2SummitSponsorshipAddOnTypesApiController
* @package App\Http\Controllers
*/
final class OAuth2SummitSponsorshipAddOnTypesApiController extends OAuth2ProtectedController
{
use RequestProcessor;
use GetAll;
use GetAndValidateJsonPayload;

/**
* @var ISummitSponsorshipAddOnTypeService
*/
private $service;

/**
* @param ISummitSponsorshipAddOnTypeRepository $repository
* @param ISummitRepository $summit_repository
* @param ISummitSponsorshipAddOnTypeService $service
* @param IResourceServerContext $resource_server_context
*/
public function __construct(
ISummitSponsorshipAddOnTypeRepository $repository,
ISummitRepository $summit_repository,
ISummitSponsorshipAddOnTypeService $service,
IResourceServerContext $resource_server_context
)
{
parent::__construct($resource_server_context);
$this->repository = $repository;
$this->summit_repository = $summit_repository;
$this->service = $service;
}

/**
* @return array
*/
protected function getFilterRules(): array
{
return [
'name' => ['==', '=@'],
];
}

/**
* @return array
*/
protected function getFilterValidatorRules(): array
{
return [
'name' => 'sometimes|required|string',
];
}

/**
* @return array
*/
protected function getOrderRules(): array
{
return [
'id',
'name',
];
}


/**
* @return ISummitRepository
*/
protected function getSummitRepository(): ISummitRepository
{
return $this->summit_repository;
}

#[OA\Get(
path: "/api/v1/summits/all/add-on-types",
summary: "Get all sponsorship add-on types",
operationId: 'getAddOnTypes',
x: [
'required-groups' => [
IGroup::SuperAdmins,
IGroup::Administrators,
IGroup::SummitAdministrators,
]
],
security: [['summit_sponsorship_oauth2' => [
SummitScopes::ReadSummitData,
SummitScopes::ReadAllSummitData,
]]],
tags: ["Sponsorship Add-On Types"],
parameters: [
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 (name==value, name=@value)", in: "query", required: false, schema: new OA\Schema(type: "string")),
new OA\Parameter(name: "order", description: "Order by (+id, -name)", in: "query", required: false, schema: new OA\Schema(type: "string")),
],
responses: [
new OA\Response(response: Response::HTTP_OK, description: "OK"),
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"),
new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"),
new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"),
]
)]

#[OA\Post(
path: "/api/v1/summits/all/add-on-types",
summary: "Add a new sponsorship add-on type",
operationId: 'addAddOnType',
x: [
'required-groups' => [
IGroup::SuperAdmins,
IGroup::Administrators,
IGroup::SummitAdministrators,
]
],
security: [['summit_sponsorship_oauth2' => [
SummitScopes::WriteSummitData,
]]],
tags: ["Sponsorship Add-On Types"],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
required: ["name"],
properties: [
new OA\Property(property: "name", type: "string"),
]
)
),
responses: [
new OA\Response(response: Response::HTTP_CREATED, description: "Created"),
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"),
new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"),
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()
{
return $this->processRequest(function () {
$payload = $this->getJsonPayload(
SummitSponsorshipAddOnTypeValidationRulesFactory::buildForAdd(),
true
);

$type = $this->service->add($payload);

return $this->created(SerializerRegistry::getInstance()->getSerializer($type)->serialize(
SerializerUtils::getExpand(),
SerializerUtils::getFields(),
SerializerUtils::getRelations()
));
});
}

#[OA\Get(
path: "/api/v1/summits/all/add-on-types/{id}",
summary: "Get a sponsorship add-on type by id",
operationId: 'getAddOnType',
security: [['summit_sponsorship_oauth2' => [
SummitScopes::ReadSummitData,
SummitScopes::ReadAllSummitData,
]]],
tags: ["Sponsorship Add-On Types"],
parameters: [
new OA\Parameter(name: "id", description: "Add-on Type ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
],
responses: [
new OA\Response(response: Response::HTTP_OK, description: "OK"),
new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"),
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"),
new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"),
new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"),
]
)]
public function get($id)
{
return $this->processRequest(function () use ($id) {
$type = $this->repository->getById(intval($id));
if (is_null($type))
return $this->error404();

return $this->ok(SerializerRegistry::getInstance()->getSerializer($type)->serialize(
SerializerUtils::getExpand(),
SerializerUtils::getFields(),
SerializerUtils::getRelations()
));
});
}

#[OA\Put(
path: "/api/v1/summits/all/add-on-types/{id}",
summary: "Update a sponsorship add-on type",
operationId: 'updateAddOnType',
x: [
'required-groups' => [
IGroup::SuperAdmins,
IGroup::Administrators,
IGroup::SummitAdministrators,
]
],
security: [['summit_sponsorship_oauth2' => [
SummitScopes::WriteSummitData,
]]],
tags: ["Sponsorship Add-On Types"],
parameters: [
new OA\Parameter(name: "id", description: "Add-on Type ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
properties: [
new OA\Property(property: "name", type: "string"),
]
)
),
responses: [
new OA\Response(response: Response::HTTP_OK, description: "OK"),
new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not Found"),
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"),
new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"),
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($id)
{
return $this->processRequest(function () use ($id) {
$payload = $this->getJsonPayload(
SummitSponsorshipAddOnTypeValidationRulesFactory::buildForUpdate(),
true
);

$type = $this->service->update(intval($id), $payload);

return $this->updated(SerializerRegistry::getInstance()->getSerializer($type)->serialize(
SerializerUtils::getExpand(),
SerializerUtils::getFields(),
SerializerUtils::getRelations()
));
});
}

#[OA\Delete(
path: "/api/v1/summits/all/add-on-types/{id}",
summary: "Delete a sponsorship add-on type",
operationId: 'deleteAddOnType',
x: [
'required-groups' => [
IGroup::SuperAdmins,
IGroup::Administrators,
IGroup::SummitAdministrators,
]
],
security: [['summit_sponsorship_oauth2' => [
SummitScopes::WriteSummitData,
]]],
tags: ["Sponsorship Add-On Types"],
parameters: [
new OA\Parameter(name: "id", description: "Add-on Type 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_NOT_FOUND, description: "Not Found"),
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"),
new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"),
new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"),
]
)]
public function delete($id)
{
return $this->processRequest(function () use ($id) {
$this->service->delete(intval($id));
return $this->deleted();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -409,21 +409,22 @@ public function getAllAddOns($summit_id, $sponsor_id, $sponsorship_id): mixed
return $this->_getAll(
function () {
return [
'name' => Filter::buildStringDefaultOperators(),
'type' => Filter::buildStringDefaultOperators(),
'name' => Filter::buildStringDefaultOperators(),
'type' => ['==', '=@'],
'type_id' => ['=='],
];
},
function () {
return [
'name' => 'sometimes|string',
'type' => 'sometimes|string',
'name' => 'sometimes|string',
'type' => 'sometimes|string',
'type_id' => 'sometimes|integer',
];
},
function () {
return [
'id',
'name',
'type',
];
},
function ($filter) use ($summit, $sponsor, $sponsorship) {
Expand Down
Loading
Loading