Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API] an api to send messages to arbitrary phone numbers #672

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
94 changes: 94 additions & 0 deletions symfony/src/Controller/Api/MessageController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace App\Controller\Api;

use App\Base\BaseController;
use App\Entity\Structure;
use App\Facade\Trigger\SimpleMessageRequestFacade;
use App\Facade\Trigger\SimpleMessageResponseFacade;
use App\Form\Model\Campaign;
use App\Form\Model\SmsTrigger;
use App\Form\Type\AudienceType;
use App\Manager\CampaignManager;
use App\Manager\PlatformConfigManager;
use App\Manager\VolunteerManager;
use Bundles\ApiBundle\Annotation\Endpoint;
use Bundles\ApiBundle\Annotation\Facade;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\Routing\Annotation\Route;

/**
* Send a message
*
* These endpoints are used to send messages to a given phone number.
*
* @Route("/api/message", name="api_admin_message_")
* @IsGranted("ROLE_ADMIN")
*/
class MessageController extends BaseController
{
/**
* @var VolunteerManager
*/
private $volunteerManager;

/**
* @var PlatformConfigManager
*/
private $platformManager;

/**
* @var CampaignManager
*/
private $campaignManager;

public function __construct(VolunteerManager $volunteerManager,
PlatformConfigManager $platformManager,
CampaignManager $campaignManager)
{
$this->volunteerManager = $volunteerManager;
$this->platformManager = $platformManager;
$this->campaignManager = $campaignManager;
}

/**
* Send an SMS to a given structure and its sub structures.
*
* @Endpoint(
* priority = 700,
* request = @Facade(class = SimpleMessageRequestFacade::class),
* response = @Facade(class = SimpleMessageResponseFacade::class)
* )
*
* @Route("/{structureExternalId}/simple-sms", methods={"POST"})
* @Entity("structure", expr="repository.findOneByExternalIdAndCurrentPlatform(structureExternalId)")
* @IsGranted("STRUCTURE", subject="structure")
*/
public function simpleSms(Structure $structure, SimpleMessageRequestFacade $request)
{
// User is trying to set trigger's ownership to a volunteer that is out of his/her scope
$volunteer = $this->volunteerManager->findOneByInternalEmail($request->getSenderInternalEmail());
if (!$this->isGranted('VOLUNTEER', $volunteer)) {
throw $this->createNotFoundException();
}

$trigger = new SmsTrigger();
$trigger->setLanguage($this->platformManager->getPlaform($this->getPlatform())->getDefaultLanguage()->getLocale());
$trigger->setAudience(AudienceType::createEmptyData([
'structures_global' => [$structure->getId()],
'badges_all' => true,
]));
$trigger->setMessage($request->getMessage());

$campaign = new Campaign($trigger);
$campaign->label = sprintf('SimpleSms API (%s)', $this->getUser()->getUserIdentifier());

$entity = $this->campaignManager->launchNewCampaign($campaign, null, $volunteer);

return new SimpleMessageResponseFacade(
count(($entity->getCommunications()[0] ?? [])->getMessages()),
sprintf('%s%s', getenv('WEBSITE_URL'), $this->generateUrl('communication_index', ['id' => $entity->getId()]))
);
}
}
151 changes: 151 additions & 0 deletions symfony/src/Entity/MessageToGuest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace App\Entity;

use App\Repository\MessageToGuestRepository;
use Doctrine\ORM\Mapping as ORM;

/**
* Sometimes, we need to send messages to people that are not volunteers.
* Right now, the data structure doesn't allow it.
*
* This entity provides a way to store main information related to a message
* without needing a volunteer, a campaign, a communication and so on, but
* keeps billing information.
*
* @ORM\Entity(repositoryClass=MessageToGuestRepository::class)
*/
class MessageToGuest
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;

/**
* @ORM\Column(type="string", length=80)
*/
private $type;

/**
* @ORM\Column(type="string", length=255)
*/
private $destination;

/**
* @ORM\Column(type="text")
*/
private $body;

/**
* @ORM\Column(type="string", length=64, nullable=true)
*/
private $messageId;

/**
* @ORM\Column(type="string", length=16)
*/
private $cost;

/**
* @ORM\Column(type="string", length=3, nullable=true)
*/
private $currency;

/**
* @ORM\ManyToOne(targetEntity=Structure::class)
* @ORM\JoinColumn(nullable=false)
*/
private $structure;

public function getId(): ?int
{
return $this->id;
}

public function getType(): ?string
{
return $this->type;
}

public function setType(string $type): self
{
$this->type = $type;

return $this;
}

public function getDestination(): ?string
{
return $this->destination;
}

public function setDestination(string $destination): self
{
$this->destination = $destination;

return $this;
}

public function getBody(): ?string
{
return $this->body;
}

public function setBody(string $body): self
{
$this->body = $body;

return $this;
}

public function getMessageId(): ?string
{
return $this->messageId;
}

public function setMessageId(?string $messageId): self
{
$this->messageId = $messageId;

return $this;
}

public function getCost(): ?string
{
return $this->cost;
}

public function setCost(string $cost): self
{
$this->cost = $cost;

return $this;
}

public function getCurrency(): ?string
{
return $this->currency;
}

public function setCurrency(?string $currency): self
{
$this->currency = $currency;

return $this;
}

public function getStructure(): ?Structure
{
return $this->structure;
}

public function setStructure(?Structure $structure): self
{
$this->structure = $structure;

return $this;
}
}
76 changes: 76 additions & 0 deletions symfony/src/Repository/MessageToGuestRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace App\Repository;

use App\Entity\MessageToGuest;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\Persistence\ManagerRegistry;

/**
* @method MessageToGuest|null find($id, $lockMode = null, $lockVersion = null)
* @method MessageToGuest|null findOneBy(array $criteria, array $orderBy = null)
* @method MessageToGuest[] findAll()
* @method MessageToGuest[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class MessageToGuestRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, MessageToGuest::class);
}

/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function add(MessageToGuest $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}

/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function remove(MessageToGuest $entity, bool $flush = true): void
{
$this->_em->remove($entity);
if ($flush) {
$this->_em->flush();
}
}

// /**
// * @return MessageToGuest[] Returns an array of MessageToGuest objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('m')
->andWhere('m.exampleField = :val')
->setParameter('val', $value)
->orderBy('m.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/

/*
public function findOneBySomeField($value): ?MessageToGuest
{
return $this->createQueryBuilder('m')
->andWhere('m.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
2 changes: 2 additions & 0 deletions symfony/templates/new_communication/form_email.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

{{ form_row(form.multipleAnswer) }}

<br/>

<div class="h3">{{ 'form.communication.fields.preview'|trans }}</div>

{{ include('new_communication/preview.html.twig') }}
Expand Down
2 changes: 2 additions & 0 deletions symfony/templates/new_communication/form_sms.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

{{ form_row(form.multipleAnswer) }}

<br/>

<div class="h3">{{ 'form.communication.fields.preview'|trans }}</div>

{{ include('new_communication/preview.html.twig') }}
Expand Down