diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c809d84bb..3f699e616 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -139,7 +139,7 @@ jobs: # verbose: true - name: Upload logs as artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: mautic-logs path: var/logs/ diff --git a/Config/config.php b/Config/config.php index 5e1ff6a39..c638a8866 100644 --- a/Config/config.php +++ b/Config/config.php @@ -185,6 +185,25 @@ 'method' => 'GET|POST', ], ], + 'api' => [ + 'mautic_api_custom_item_unlink' => [ + 'path' => '/custom/item/{itemId}/unlink/{contactId}', + 'controller' => 'CustomObjectsBundle:CustomItem\Api\Custom:unlink', + 'method' => 'DELETE', + 'requirements' => [ + 'itemId' => '\d+', + 'contactId' => '\d+', + ], + ], + 'mautic_api_custom_item_delete' => [ + 'path' => '/custom/item/{itemId}/delete', + 'controller' => 'CustomObjectsBundle:CustomItem\Api\Custom:delete', + 'method' => 'DELETE', + 'requirements' => [ + 'itemId' => '\d+', + ], + ], + ], ], 'services' => [ @@ -408,6 +427,20 @@ ], ], + 'custom_item.api_custom_controller' => [ + 'class' => \MauticPlugin\CustomObjectsBundle\Controller\CustomItem\Api\CustomController::class, + 'arguments' => [ + 'mautic.custom.model.item', + 'custom_item.permission.provider', + 'translator', + ], + 'methodCalls' => [ + 'setContainer' => [ + '@service_container', + ], + ], + ], + // Custom Objects 'custom_object.list_controller' => [ 'class' => \MauticPlugin\CustomObjectsBundle\Controller\CustomObject\ListController::class, diff --git a/Controller/CustomItem/Api/CustomController.php b/Controller/CustomItem/Api/CustomController.php new file mode 100644 index 000000000..446331849 --- /dev/null +++ b/Controller/CustomItem/Api/CustomController.php @@ -0,0 +1,75 @@ +customItemModel = $customItemModel; + $this->permissionProvider = $permissionProvider; + } + + public function unlinkAction(int $itemId, int $contactId): JsonResponse + { + try { + $customItem = $this->customItemModel->fetchEntity($itemId); + + $this->permissionProvider->canEdit($customItem); + + if ($customItem->getCustomObject()->getRelationshipObject()) { + try { + $childCustomItem = $customItem->findChildCustomItem(); + } catch (NotFoundException $e) { + } + + if (isset($childCustomItem)) { + $this->customItemModel->delete($childCustomItem); + } + } + + $this->customItemModel->unlinkEntity($customItem, 'contact', $contactId); + + return new JsonResponse(['success' => true]); + + } catch (ForbiddenException|NotFoundException|\UnexpectedValueException $e) { + return new JsonResponse([ + 'message' => $e->getMessage(), + 'success' => false, + ], $e->getCode()); + } + } + + public function deleteAction(int $itemId): Response + { + try { + $customItem = $this->customItemModel->fetchEntity($itemId); + $this->permissionProvider->canDelete($customItem); + } catch (NotFoundException|ForbiddenException $e) { + return new JsonResponse([ + 'message' => $e->getMessage(), + 'success' => false, + ], Response::HTTP_NOT_FOUND); + } + + $this->customItemModel->delete($customItem); + + return new JsonResponse(['success' => true]); + } +} \ No newline at end of file diff --git a/Tests/Functional/CustomApiControllerTest.php b/Tests/Functional/CustomApiControllerTest.php new file mode 100644 index 000000000..558dc7394 --- /dev/null +++ b/Tests/Functional/CustomApiControllerTest.php @@ -0,0 +1,111 @@ +configParams['custom_objects_enabled'] = true; + + parent::setUp(); + + $this->customItemRepository = self::$container->get('custom_item.repository'); + $this->customItemXrefContactRepository = self::$container->get('custom_item.xref.contact.repository'); + + // Create a Contact + $this->contact = $this->createContact(); + // Create a Custom Object + $customObject = $this->createCustomObject('Product', 'Products'); + // Create a Custom Item + $this->customItem = $this->createCustomItem($customObject, 'Product 1'); + // Relate CI with Contact + $this->createCustomItemXrefContact($this->customItem, $this->contact); + } + + public function testDetachCustomItemFomContact(): void + { + $items = $this->customItemRepository->count([]); + $links = $this->customItemXrefContactRepository->count([]); + $this->assertEquals(1, $items); + $this->assertEquals(1, $links); + + $this->client->request('DELETE', sprintf('/api/custom/item/%s/unlink/%s', $this->customItem->getId(), $this->contact->getId())); + $this->assertTrue($this->client->getResponse()->isOk()); + + $items = $this->customItemRepository->count([]); + $links = $this->customItemXrefContactRepository->count([]); + $this->assertEquals(1, $items); + $this->assertEquals(0, $links); + } + + public function testDeleteCustomItem(): void + { + $items = $this->customItemRepository->count([]); + $links = $this->customItemXrefContactRepository->count([]); + $this->assertEquals(1, $items); + $this->assertEquals(1, $links); + + $this->client->request('DELETE', sprintf('/api/custom/item/%s/delete', $this->customItem->getId())); + $this->assertTrue($this->client->getResponse()->isOk()); + + $items = $this->customItemRepository->count([]); + $links = $this->customItemXrefContactRepository->count([]); + $this->assertEquals(0, $items); + $this->assertEquals(0, $links); + } + + private function createCustomObject(string $singular, string $plural): CustomObject + { + $customObject = new CustomObject(); + $customObject->setNameSingular($singular); + $customObject->setNamePlural($plural); + $customObject->setAlias(mb_strtolower($plural)); + $this->em->persist($customObject); + $this->em->flush(); + + return $customObject; + } + + private function createContact(): Lead + { + $lead = new Lead(); + $this->em->persist($lead); + $this->em->flush(); + + return $lead; + } + + private function createCustomItem(CustomObject $customObject, string $name): CustomItem + { + $customItem = new CustomItem($customObject); + $customItem->setName($name); + $this->em->persist($customItem); + $this->em->flush(); + + return $customItem; + } + + private function createCustomItemXrefContact(CustomItem $customItem, Lead $contact): void + { + $customItemXrefContact = new CustomItemXrefContact($customItem, $contact); + + $this->em->persist($customItemXrefContact); + $this->em->flush(); + } +}