API endpoints to delete customer/project/activity (#5181)
* added service methods with events to delete customer, project, activity * added API endpoints to delete customer, project, activity * added tests for new API endpoints
This commit is contained in:
@@ -22,6 +22,8 @@ use App\Repository\ActivityRepository;
|
||||
use App\Repository\Query\VisibilityInterface;
|
||||
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -449,4 +451,62 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
$activity = $em->getRepository(Activity::class)->find(1);
|
||||
$this->assertEquals('another,testing,bar', $activity->getMetaField('metatestmock')->getValue());
|
||||
}
|
||||
|
||||
// ------------------------------- [DELETE] -------------------------------
|
||||
|
||||
public function testDeleteIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/activities/1', Request::METHOD_DELETE);
|
||||
}
|
||||
|
||||
public function testDeleteActionWithUnknownTimesheet(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/activities/' . PHP_INT_MAX);
|
||||
}
|
||||
|
||||
public function testDeleteEntityIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/activities/1', Request::METHOD_DELETE);
|
||||
}
|
||||
|
||||
public function testDeleteActionWithoutAuthorization(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$imports = $this->loadActivityTestData();
|
||||
|
||||
$this->request($client, '/api/activities/' . $imports[2]->getId(), Request::METHOD_DELETE);
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testDeleteAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$imports = $this->loadActivityTestData();
|
||||
$getUrl = '/api/activities/' . $imports[2]->getId();
|
||||
$this->assertAccessIsGranted($client, $getUrl);
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertIsString($content);
|
||||
$result = json_decode($content, true);
|
||||
|
||||
self::assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('ActivityEntity', $result);
|
||||
self::assertNotEmpty($result['id']);
|
||||
self::assertIsNumeric($result['id']);
|
||||
$id = $result['id'];
|
||||
|
||||
$this->request($client, '/api/activities/' . $id, Request::METHOD_DELETE);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
self::assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
|
||||
$this->assertEmpty($client->getResponse()->getContent());
|
||||
|
||||
$this->request($client, $getUrl);
|
||||
$this->assertApiException($client->getResponse(), [
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ use App\Repository\CustomerRateRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -88,6 +90,45 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
return [$rate1, $rate2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: Customer, 1: Customer}
|
||||
*/
|
||||
private function loadCustomerData(): array
|
||||
{
|
||||
/** @var CustomerRateRepository $rateRepository */
|
||||
$rateRepository = $this->getEntityManager()->getRepository(CustomerRate::class);
|
||||
/** @var CustomerRepository $repository */
|
||||
$repository = $this->getEntityManager()->getRepository(Customer::class);
|
||||
|
||||
$customer1 = new Customer('foooo');
|
||||
$customer1->setCountry('DE');
|
||||
$customer1->setTimezone('Europe/Paris');
|
||||
$repository->saveCustomer($customer1);
|
||||
|
||||
$customer2 = new Customer('baaaaar');
|
||||
$customer2->setCountry('RU');
|
||||
$customer2->setTimezone('Europe/Moscow');
|
||||
$repository->saveCustomer($customer2);
|
||||
|
||||
$rate1 = new CustomerRate();
|
||||
$rate1->setCustomer($customer1);
|
||||
$rate1->setRate(17.45);
|
||||
$rate1->setIsFixed(false);
|
||||
|
||||
$rateRepository->saveRate($rate1);
|
||||
|
||||
$rate2 = new CustomerRate();
|
||||
$rate2->setCustomer($customer1);
|
||||
$rate2->setRate(99);
|
||||
$rate2->setInternalRate(9);
|
||||
$rate2->setIsFixed(true);
|
||||
$rate2->setUser($this->getUserByName(UserFixtures::USERNAME_USER));
|
||||
|
||||
$rateRepository->saveRate($rate2);
|
||||
|
||||
return [$customer1, $customer2];
|
||||
}
|
||||
|
||||
public function testIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/customers');
|
||||
@@ -392,4 +433,62 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
$customer = $em->getRepository(Customer::class)->find(1);
|
||||
$this->assertEquals('another,testing,bar', $customer->getMetaField('metatestmock')->getValue());
|
||||
}
|
||||
|
||||
// ------------------------------- [DELETE] -------------------------------
|
||||
|
||||
public function testDeleteIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/customers/1', Request::METHOD_DELETE);
|
||||
}
|
||||
|
||||
public function testDeleteActionWithUnknownTimesheet(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/customers/' . PHP_INT_MAX);
|
||||
}
|
||||
|
||||
public function testDeleteEntityIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/customers/1', Request::METHOD_DELETE);
|
||||
}
|
||||
|
||||
public function testDeleteActionWithoutAuthorization(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$imports = $this->loadCustomerData();
|
||||
|
||||
$this->request($client, '/api/customers/' . $imports[1]->getId(), Request::METHOD_DELETE);
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testDeleteAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$imports = $this->loadCustomerData();
|
||||
$getUrl = '/api/customers/' . $imports[0]->getId();
|
||||
$this->assertAccessIsGranted($client, $getUrl);
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertIsString($content);
|
||||
$result = json_decode($content, true);
|
||||
|
||||
self::assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('CustomerEntity', $result);
|
||||
self::assertNotEmpty($result['id']);
|
||||
self::assertIsNumeric($result['id']);
|
||||
$id = $result['id'];
|
||||
|
||||
$this->request($client, '/api/customers/' . $id, Request::METHOD_DELETE);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
self::assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
|
||||
$this->assertEmpty($client->getResponse()->getContent());
|
||||
|
||||
$this->request($client, $getUrl);
|
||||
$this->assertApiException($client->getResponse(), [
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\VisibilityInterface;
|
||||
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -110,10 +111,14 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
self::assertApiResponseTypeStructure('ProjectCollection', $result[0]);
|
||||
}
|
||||
|
||||
protected function loadProjectTestData(HttpKernelBrowser $client)
|
||||
/**
|
||||
* @return array{0: Project, 1: Project, 2: Project, 3: Project, 4: Project}
|
||||
*/
|
||||
protected function loadProjectTestData(): array
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
/** @var Customer $customer */
|
||||
$customer = $em->getRepository(Customer::class)->find(1);
|
||||
|
||||
$customer2 = new Customer('first one');
|
||||
@@ -127,49 +132,49 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$customer3->setTimezone('Europe/Vienna');
|
||||
$em->persist($customer3);
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('first');
|
||||
$project->setVisible(false);
|
||||
$project->setCustomer($customer2);
|
||||
$em->persist($project);
|
||||
$project1 = new Project();
|
||||
$project1->setName('first');
|
||||
$project1->setVisible(false);
|
||||
$project1->setCustomer($customer2);
|
||||
$em->persist($project1);
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('second');
|
||||
$project->setVisible(false);
|
||||
$project->setCustomer($customer);
|
||||
$em->persist($project);
|
||||
$project2 = new Project();
|
||||
$project2->setName('second');
|
||||
$project2->setVisible(false);
|
||||
$project2->setCustomer($customer);
|
||||
$em->persist($project2);
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('third');
|
||||
$project->setVisible(true);
|
||||
$project->setCustomer($customer2);
|
||||
$em->persist($project);
|
||||
$project3 = new Project();
|
||||
$project3->setName('third');
|
||||
$project3->setVisible(true);
|
||||
$project3->setCustomer($customer2);
|
||||
$em->persist($project3);
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('fourth');
|
||||
$project->setVisible(true);
|
||||
$project->setCustomer($customer3);
|
||||
$em->persist($project);
|
||||
$project4 = new Project();
|
||||
$project4->setName('fourth');
|
||||
$project4->setVisible(true);
|
||||
$project4->setCustomer($customer3);
|
||||
$em->persist($project4);
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('fifth');
|
||||
$project->setVisible(true);
|
||||
$project->setCustomer($customer);
|
||||
$project5 = new Project();
|
||||
$project5->setName('fifth');
|
||||
$project5->setVisible(true);
|
||||
$project5->setCustomer($customer);
|
||||
|
||||
// add meta fields
|
||||
$meta = new ProjectMeta();
|
||||
$meta->setName('bar')->setValue('foo')->setIsVisible(false);
|
||||
$project->setMetaField($meta);
|
||||
$project5->setMetaField($meta);
|
||||
$meta = new ProjectMeta();
|
||||
$meta->setName('foo')->setValue('bar')->setIsVisible(true);
|
||||
$project->setMetaField($meta);
|
||||
$em->persist($project);
|
||||
$project5->setMetaField($meta);
|
||||
$em->persist($project5);
|
||||
|
||||
// and a team
|
||||
$team = new Team('Testing project team');
|
||||
$team->addTeamlead($this->getUserByRole(User::ROLE_USER));
|
||||
$team->addCustomer($customer);
|
||||
$team->addProject($project);
|
||||
$team->addProject($project5);
|
||||
$team->addUser($this->getUserByRole(User::ROLE_TEAMLEAD));
|
||||
$em->persist($team);
|
||||
|
||||
@@ -178,18 +183,24 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
|
||||
$em->flush();
|
||||
|
||||
return [$customer, $customer2, $customer3];
|
||||
return [
|
||||
$project1,
|
||||
$project2,
|
||||
$project3,
|
||||
$project4,
|
||||
$project5,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getCollectionTestData
|
||||
*/
|
||||
public function testGetCollectionWithParams($url, $customer, $parameters, $expected): void
|
||||
public function testGetCollectionWithParams(string $url, ?int $project, array $parameters, array $expected): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$imports = $this->loadProjectTestData($client);
|
||||
$imports = $this->loadProjectTestData();
|
||||
|
||||
$customerId = $customer !== null ? $imports[$customer]->getId() : null;
|
||||
$customerId = $project !== null ? $imports[$project]->getCustomer()?->getId() : null;
|
||||
|
||||
if ($customerId !== null) {
|
||||
if (\array_key_exists('customer', $parameters)) {
|
||||
@@ -236,19 +247,19 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
// if you wonder why: case-sensitive ordering feels strange ... "Title" > "fifth”
|
||||
yield ['/api/projects', null, [], [[true, 1], [false, 1], [false, 3]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1'], [[true, 1], [false, 1]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_VISIBLE], [[true, 1], [false, 1]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_HIDDEN], [[false, 1]]];
|
||||
yield ['/api/projects', 1, ['customer' => '1'], [[true, 1], [false, 1]]];
|
||||
yield ['/api/projects', 1, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_VISIBLE], [[true, 1], [false, 1]]];
|
||||
yield ['/api/projects', 1, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
|
||||
yield ['/api/projects', 1, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_HIDDEN], [[false, 1]]];
|
||||
// customer is invisible => query only returns results for VisibilityInterface::SHOW_BOTH
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_VISIBLE], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'customers' => ['2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 1, ['customers' => ['2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 1, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 0, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_VISIBLE], []];
|
||||
yield ['/api/projects', 0, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 0, ['customer' => '2', 'customers' => ['2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 0, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 0, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN], []];
|
||||
yield ['/api/projects', 0, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 0, ['customers' => ['2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 0, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
}
|
||||
|
||||
public function testGetEntityIsSecure(): void
|
||||
@@ -599,4 +610,62 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$project = $em->getRepository(Project::class)->find(1);
|
||||
$this->assertEquals('another,testing,bar', $project->getMetaField('metatestmock')->getValue());
|
||||
}
|
||||
|
||||
// ------------------------------- [DELETE] -------------------------------
|
||||
|
||||
public function testDeleteIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/projects/1', Request::METHOD_DELETE);
|
||||
}
|
||||
|
||||
public function testDeleteActionWithUnknownTimesheet(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/projects/' . PHP_INT_MAX);
|
||||
}
|
||||
|
||||
public function testDeleteEntityIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/projects/1', Request::METHOD_DELETE);
|
||||
}
|
||||
|
||||
public function testDeleteActionWithoutAuthorization(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$imports = $this->loadProjectTestData();
|
||||
|
||||
$this->request($client, '/api/projects/' . $imports[2]->getId(), Request::METHOD_DELETE);
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testDeleteAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$imports = $this->loadProjectTestData();
|
||||
$getUrl = '/api/projects/' . $imports[2]->getId();
|
||||
$this->assertAccessIsGranted($client, $getUrl);
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertIsString($content);
|
||||
$result = json_decode($content, true);
|
||||
|
||||
self::assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('ProjectEntity', $result);
|
||||
self::assertNotEmpty($result['id']);
|
||||
self::assertIsNumeric($result['id']);
|
||||
$id = $result['id'];
|
||||
|
||||
$this->request($client, '/api/projects/' . $id, Request::METHOD_DELETE);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
self::assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
|
||||
$this->assertEmpty($client->getResponse()->getContent());
|
||||
|
||||
$this->request($client, $getUrl);
|
||||
$this->assertApiException($client->getResponse(), [
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,36 +335,6 @@ parameters:
|
||||
count: 1
|
||||
path: API/ProjectControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:loadProjectTestData\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: API/ProjectControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:testGetCollectionWithParams\\(\\) has parameter \\$customer with no type specified\\.$#"
|
||||
count: 1
|
||||
path: API/ProjectControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:testGetCollectionWithParams\\(\\) has parameter \\$expected with no type specified\\.$#"
|
||||
count: 1
|
||||
path: API/ProjectControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:testGetCollectionWithParams\\(\\) has parameter \\$parameters with no type specified\\.$#"
|
||||
count: 1
|
||||
path: API/ProjectControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:testGetCollectionWithParams\\(\\) has parameter \\$url with no type specified\\.$#"
|
||||
count: 1
|
||||
path: API/ProjectControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$customer of method App\\\\Entity\\\\Team\\:\\:addCustomer\\(\\) expects App\\\\Entity\\\\Customer, App\\\\Entity\\\\Customer\\|null given\\.$#"
|
||||
count: 1
|
||||
path: API/ProjectControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#5 \\$content of method App\\\\Tests\\\\API\\\\APIControllerBaseTest\\:\\:request\\(\\) expects string\\|null, string\\|false given\\.$#"
|
||||
count: 15
|
||||
|
||||
Reference in New Issue
Block a user