smarcet 39e3c8e333 * Summit Registration Model/Endpoints
Doc

https://docs.google.com/document/d/1gOAceevwutF3QHYWD0_6aESQMZEiIz3YVBVXlyUuRy8

* Summit Registration External Feed

https://docs.google.com/document/d/1_2QZUK--A350jxh2USKBtVtisKrrcUcVjdKnbcZ7-4M/edit#

* Multi Stripe Config

* Added stripe payment configuration per summit
* Added webhook automatic creation
* Added CRUD endpoints for payment profiles per summit

GET api/v1/summits/{id}/payment-gateway-profiles

filters

active ['==']
application_type ['=@', '==']

scopes

summits/read/all
summits/payment-gateway-profiles/read

POST  api/v1/summits/{id}/payment-gateway-profiles

payload

'active'               => 'sometimes|boolean',
'application_type'     => 'sometimes|string|in:Registration,BookableRooms'
'provider'             => 'required|string|in:Stripe',
'test_mode_enabled'    => 'required|boolean',
'live_secret_key'      => 'sometimes|string',
'live_publishable_key' => 'required_with:live_secret_key|string',
'test_secret_key'      => 'sometimes|string',
'test_publishable_key' => 'required_with:test_secret_key|string'

scopes

summits/write
summits/payment-gateway-profiles/write

GET  api/v1/summits/{id}/payment-gateway-profiles/{payment_profile_id}

scopes

summits/read/all
summits/payment-gateway-profiles/read

PUT api/v1/summits/{id}/payment-gateway-profiles/{payment_profile_id}

payload

'active'               => 'sometimes|boolean',
'application_type'     => 'sometimes|string|in:Registration,BookableRooms'
'provider'             => 'required|string|in:Stripe',
'test_mode_enabled'    => 'required|boolean',
'live_secret_key'      => 'sometimes|string',
'live_publishable_key' => 'required_with:live_secret_key|string',
'test_secret_key'      => 'sometimes|string',
'test_publishable_key' => 'required_with:test_secret_key|string'

scopes

summits/payment-gateway-profiles/write
summits/write

DELETE api/v1/summits/{id}/payment-gateway-profiles/{payment_profile_id}

scopes

summits/payment-gateway-profiles/write
summits/write

Stripe Webhooks

default webhook

(SECRET set per application under .env, should be created previously on stripe default account)

POST api/public/v1/summits/all/payments/{application_name}/confirm

where application_name could be Registration or BookableRooms

otherwise if a stripe account is set per summit, when the profile gets activated
a webhook will be automatically be created using following url

POST api/public/v1/summits/{id}/payments/{application_name}/confirm

* Email API Integration

* moved all blade templates to mail api ( DB Seeding)
* defined email events flows per summmit
* created endpoint to set up templates per email events on each flow

PUT /api/v1/summits/{id}/email-flows-events/{event_id}

payload

email_template_identifier: string

* added endpoint to get all email events per summit

GET /api/v1/summits/{id}/email-flows-events

* added endpoint to get email event per summit/id

GET /api/v1/summits/{id}/email-flows-events/{event_id}

Summit Documents endpoints

add summit document

POST /api/v1/summits/{id}/summit-documents

payload

file (mandatory|file stream)
label ( mandatory|string)
name ( mandatory|string)
description (optional|string)

get all summit documents

GET /api/v1/summits/{id}/summit-documents

get by id

GET /api/v1/summits/{id}/summit-documents/{document_id}

update

PUT GET /api/v1/summits/{id}/summit-documents/{document_id}

file (optional|file stream)
label (optional|string)
name (optional|string)
description (optional|string)

delete

DELETE GET /api/v1/summits/{id}/summit-documents/{document_id}

add event type to doc

PUT /api/v1/summits/{id}/summit-documents/{document_id}/event-types/{event_type_id}

delete event type from doc

DELETE /api/v1/summits/{id}/summit-documents/{document_id}/event-types/{event_type_id}

add document to event type

PUT /api/v1/summits/{id}/event-types/{event_type_id}/summit-documents/{document_id}

delete document from event type

DELETE /api/v1/summits/{id}/event-types/{event_type_id}/summit-documents/{document_id}

Updated Summit events to support streaming url, meeting_url and etherpad link

* Summit Registration - Invite only

Summit Administrator Permission Groups

GET /api/v1/summit-administrator-groups

filter

* title
* member_first_name
* member_last_name
* member_full_name
* member_email
* summit_id
* member_id

ordering

id
title

scopes

%s/summit-administrator-groups/read

/api/v1/summit-administrator-groups/{group_id}

scopes

%s/summit-administrator-groups/read

POST /api/v1/summit-administrator-groups

title
summits
members

scopes

%s/summit-administrator-groups/write

PUT /api/v1/summit-administrator-groups/{group_id}

title
summits
members

scopes

%s/summit-administrator-groups/write

DELETE /api/v1/summit-administrator-groups/{group_id}

scopes

%s/summit-administrator-groups/write

PUT /api/v1/summit-administrator-groups/{group_id}/members/{member_id}

DELETE /api/v1/summit-administrator-groups/{group_id}/members/{member_id}

PUT /api/v1/summit-administrator-groups/{group_id}/summits/{summit_id}

DELETE /api/v1/summit-administrator-groups/{group_id}/summits/{summit_id}

Room Metrics

PUT /api/v1/summits/{id}/members/{member_id}/schedule/{event_id}/enter

Scopes

%s/me/summits/events/enter

PUT /api/v1/summits/{id}/members/{member_id}/schedule/{event_id}/leave

%s/me/summits/events/leave

update summit_event serializer

new fields

total_attendance_count
current_attendance_count
attendance
current_attendance

Added new Speakers Endpoints

GET /api/v1/summits/{id}/speakers/on-schedule

retrieves all speakers on summit schedule

filters

'first_name' => ['=@', '=='],
'last_name'  => ['=@', '=='],
'email'      => ['=@', '=='],
'id'         => ['=='],
'full_name'  => ['=@', '=='],
'start_date' => ['>', '<', '<=', '>=', '=='],
'end_date'   => ['>', '<', '<=', '>=', '=='],

Summit Events Image

POST /api/v1/summits/{id}/events/{event_id}/image
DELETE /api/v1/summits/{id}/events/{event_id}/image

Speakers Image Big Pic

POST /api/v1/speakers/{speaker_id}/big-photo
DELETE /api/v1/speakers/{speaker_id}/big-photo

CFP - multiple summits

Presentation Materials - Media Uploads

New Endpoints

GET api/v1/summit-media-file-types

filter
'name' => ['=@', '==']

order

id, name

POST api/v1/summit-media-file-types

payload

'name'  => 'required|string|max:255',
'description'  => 'sometimes|string|max:255',
'allowed_extensions' => 'required|string_array',

GET api/v1/summit-media-file-types/{id}

PUT api/v1/summit-media-file-types/{id}

payload

'name'  => 'sometimes|string|max:255',
'description'  => 'sometimes|string|max:255',
'allowed_extensions' => 'required|string_array',

DELETE api/v1/summit-media-file-types/{id}

GET api/v1/summits/{id}/media-upload-types

POST

payload

'name' => 'required|string|max:255',
'description' => 'sometimes|string|max:255',
'is_mandatory' => 'required|boolean',
'max_size' => 'required|int|megabyte_aligned',
'private_storage_type' => 'required|string|in:None,DropBox,Swift,Local',
'public_storage_type' => 'required|string|in:None,DropBox,Swift,Local'
'type_id' => 'required|int',
'presentation_types' => 'sometimes|int_array',

GET api/v1/summits/{id}/media-upload-types/{media_upload_type_id}

PUT api/v1/summits/{id}/media-upload-types/{media_upload_type_id}

payload

'name' => 'sometimes|string|max:255',
'description' => 'sometimes|string|max:255',
'is_mandatory' => 'sometimes|boolean',
'max_size' => 'sometimes|int|megabyte_aligned',
'private_storage_type' => 'sometimes|string|in:None,DropBox,Swift,Local',
'public_storage_type' => 'sometimes|string|in:None,DropBox,Swift,Local'
'type_id' => 'sometimes|int',
'presentation_types' => 'sometimes|int_array',

DELETE api/v1/summits/{id}/media-upload-types/{media_upload_type_id}

PUT api/v1/summits/{id}/media-upload-types/{media_upload_type_id}/presentation-types/{event_type_id}

DELETE api/v1/summits/{id}/media-upload-types/{media_upload_type_id}/presentation-types/{event_type_id}

GET api/v1/summits/{id}/presentations/{presentation_id}/media-uploads

POST api/v1/summits/{id}/presentations/{presentation_id}/media-uploads

payload multiform

file

media_upload_type_id

GET api/v1/summits/{id}/presentations/{presentation_id}/media-uploads/{media_upload_id}

PUT api/v1/summits/{id}/presentations/{presentation_id}/media-uploads/{media_upload_id}

payload multiform

file

DELETE api/v1/summits/{id}/presentations/{presentation_id}/media-uploads/{media_upload_id}

POST /api/v1/summits/{id}/media-upload-types/all/clone/{to_summit_id}

Summit Invitation Only endpoints V2

CRUD invitations

Change-Id: Ia23c247a59c3810f2a738265efdd890fe6f59dfb
Signed-off-by: smarcet <smarcet@gmail.com>
2020-09-21 09:52:07 -03:00

804 lines
22 KiB
PHP

<?php namespace App\Queue\RabbitMQ;
/**
* Copyright 2020 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 ErrorException;
use Exception;
use Illuminate\Contracts\Queue\Queue as QueueContract;
use Illuminate\Queue\Queue;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use PhpAmqpLib\Exception\AMQPProtocolChannelException;
use PhpAmqpLib\Exchange\AMQPExchangeType;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;
/**
* Class RabbitMQQueue
* @package App\Queue\RabbitMQ
*/
class RabbitMQQueue extends Queue implements QueueContract
{
const RECONNECT_WAIT = 10000;
/**
* The RabbitMQ connection instance.
*
* @var AbstractConnection
*/
protected $connection;
/**
* The RabbitMQ channel instance.
*
* @var AMQPChannel
*/
protected $channel;
/**
* The name of the default queue.
*
* @var string
*/
protected $default;
/**
* List of already declared exchanges.
*
* @var array
*/
protected $exchanges = [];
/**
* List of already declared queues.
*
* @var array
*/
protected $queues = [];
/**
* List of already bound queues to exchanges.
*
* @var array
*/
protected $boundQueues = [];
/**
* Current job being processed.
*
* @var RabbitMQJob
*/
protected $currentJob;
/**
* @var array
*/
protected $options;
/**
* RabbitMQQueue constructor.
*
* @param AbstractConnection $connection
* @param string $default
* @param array $options
*/
public function __construct(
AbstractConnection $connection,
string $default,
array $options = []
) {
$this->connection = $connection;
$this->channel = $connection->channel();
$this->default = $default;
$this->options = $options;
}
/**
* {@inheritdoc}
*
* @throws AMQPProtocolChannelException
*/
public function size($queue = null): int
{
$queue = $this->getQueue($queue);
if (! $this->isQueueExists($queue)) {
return 0;
}
// create a temporary channel, so the main channel will not be closed on exception
$channel = $this->connection->channel();
[, $size] = $channel->queue_declare($queue, true);
$channel->close();
return $size;
}
/**
* {@inheritdoc}
*
* @throws AMQPProtocolChannelException
*/
public function push($job, $data = '', $queue = null)
{
return $this->pushRaw($this->createPayload($job, $data), $queue, []);
}
/**
* {@inheritdoc}
*
* @throws AMQPProtocolChannelException
*/
public function pushRaw($payload, $queue = null, array $options = [])
{
[$destination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options);
$this->declareDestination($destination, $exchange, $exchangeType);
[$message, $correlationId] = $this->createMessage($payload, $attempts);
$this->channel->basic_publish($message, $exchange, $destination, true, false);
return $correlationId;
}
/**
* {@inheritdoc}
*
* @throws AMQPProtocolChannelException
*/
public function later($delay, $job, $data = '', $queue = null)
{
return $this->laterRaw(
$delay,
$this->createPayload($job, $data),
$queue
);
}
/**
* @param $delay
* @param $payload
* @param null $queue
* @param int $attempts
* @return mixed
* @throws AMQPProtocolChannelException
*/
public function laterRaw($delay, $payload, $queue = null, $attempts = 0)
{
$ttl = $this->secondsUntil($delay) * 1000;
// When no ttl just publish a new message to the exchange or queue
if ($ttl <= 0) {
return $this->pushRaw($payload, $queue, ['delay' => $delay, 'attempts' => $attempts]);
}
$destination = $this->getQueue($queue).'.delay.'.$ttl;
$this->declareQueue($destination, true, false, $this->getDelayQueueArguments($this->getQueue($queue), $ttl));
[$message, $correlationId] = $this->createMessage($payload, $attempts);
// Publish directly on the delayQueue, no need to publish trough an exchange.
$this->channel->basic_publish($message, null, $destination, true, false);
return $correlationId;
}
/**
* {@inheritdoc}
*
* @throws AMQPProtocolChannelException
*/
public function bulk($jobs, $data = '', $queue = null): void
{
foreach ((array) $jobs as $job) {
$this->bulkRaw($this->createPayload($job, $data), $queue, ['job' => $job]);
}
$this->channel->publish_batch();
}
/**
* @param string $payload
* @param null $queue
* @param array $options
* @return mixed
* @throws AMQPProtocolChannelException
*/
public function bulkRaw(string $payload, $queue = null, array $options = [])
{
[$destination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options);
$this->declareDestination($destination, $exchange, $exchangeType);
[$message, $correlationId] = $this->createMessage($payload, $attempts);
$this->channel->batch_basic_publish($message, $exchange, $destination);
return $correlationId;
}
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function pop($queue = null)
{
try {
$queue = $this->getQueue($queue);
/** @var AMQPMessage|null $message */
if ($message = $this->channel->basic_get($queue)) {
return $this->currentJob = new RabbitMQJob(
$this->container,
$this,
$message,
$this->connectionName,
$queue
);
}
}
catch(AMQPConnectionClosedException $ex){
// recovery from closed connection
Log::warning($ex);
try {
usleep(RabbitMQQueue::RECONNECT_WAIT);
$this->connection->reconnect();
$this->channel = $this->connection->channel();
}
catch (Exception $ex){
Log::warning($ex);
}
return null;
}
catch (AMQPProtocolChannelException $exception) {
// If there is not exchange or queue AMQP will throw exception with code 404
// We need to catch it and return null
Log::warning($exception);
if ($exception->amqp_reply_code === 404) {
Log::warning(sprintf("RabbitMQQueue::pop amqp_reply_code 404 trying reconnect ..."));
// Because of the channel exception the channel was closed and removed.
// We have to open a new channel. Because else the worker(s) are stuck in a loop, without processing.
$this->channel = $this->connection->channel();
return null;
}
throw $exception;
}
return null;
}
/**
* @return AbstractConnection
*/
public function getConnection(): AbstractConnection
{
return $this->connection;
}
/**
* @return AMQPChannel
*/
public function getChannel(): AMQPChannel
{
return $this->channel;
}
/**
* Gets a queue/destination, by default the queue option set on the connection.
*
* @param null $queue
* @return string
*/
public function getQueue($queue = null)
{
return $queue ?: $this->default;
}
/**
* Checks if the given exchange already present/defined in RabbitMQ.
* Returns false when when the exchange is missing.
*
* @param string $exchange
* @return bool
* @throws AMQPProtocolChannelException
*/
public function isExchangeExists(string $exchange): bool
{
try {
// create a temporary channel, so the main channel will not be closed on exception
$channel = $this->connection->channel();
$channel->exchange_declare($exchange, '', true);
$channel->close();
return true;
} catch (AMQPProtocolChannelException $exception) {
if ($exception->amqp_reply_code === 404) {
return false;
}
throw $exception;
}
}
/**
* Declare a exchange in rabbitMQ, when not already declared.
*
* @param string $name
* @param string $type
* @param bool $durable
* @param bool $autoDelete
* @param array $arguments
* @return void
*/
public function declareExchange(string $name, string $type = AMQPExchangeType::DIRECT, bool $durable = true, bool $autoDelete = false, array $arguments = []): void
{
if ($this->isExchangeDeclared($name)) {
return;
}
$this->channel->exchange_declare(
$name,
$type,
false,
$durable,
$autoDelete,
false,
true,
new AMQPTable($arguments)
);
}
/**
* Delete a exchange from rabbitMQ, only when present in RabbitMQ.
*
* @param string $name
* @param bool $unused
* @return void
* @throws AMQPProtocolChannelException
*/
public function deleteExchange(string $name, bool $unused = false): void
{
if (! $this->isExchangeExists($name)) {
return;
}
$this->channel->exchange_delete(
$name,
$unused
);
}
/**
* Checks if the given queue already present/defined in RabbitMQ.
* Returns false when when the queue is missing.
*
* @param string $name
* @return bool
* @throws AMQPProtocolChannelException
*/
public function isQueueExists(string $name = null): bool
{
try {
Log::debug(sprintf("RabbitMQQueue::isQueueExists %s", $name));
// create a temporary channel, so the main channel will not be closed on exception
$channel = $this->connection->channel();
$channel->queue_declare($this->getQueue($name), true);
$channel->close();
Log::debug(sprintf("RabbitMQQueue::isQueueExists %s exists", $name));
return true;
} catch (AMQPProtocolChannelException $exception) {
if ($exception->amqp_reply_code === 404) {
Log::debug(sprintf("RabbitMQQueue::isQueueExists %s not found", $name));
return false;
}
throw $exception;
}
}
/**
* Declare a queue in rabbitMQ, when not already declared.
*
* @param string $name
* @param bool $durable
* @param bool $autoDelete
* @param array $arguments
* @return void
*/
public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false, array $arguments = []): void
{
Log::debug(sprintf("RabbitMQQueue::declareQueue %s ", $name));
if ($this->isQueueDeclared($name)) {
return;
}
$this->channel->queue_declare(
$name,
false,
$durable,
false,
$autoDelete,
false,
new AMQPTable($arguments)
);
}
/**
* Delete a queue from rabbitMQ, only when present in RabbitMQ.
*
* @param string $name
* @param bool $if_unused
* @param bool $if_empty
* @return void
* @throws AMQPProtocolChannelException
*/
public function deleteQueue(string $name, bool $if_unused = false, bool $if_empty = false): void
{
if (! $this->isQueueExists($name)) {
return;
}
$this->channel->queue_delete($name, $if_unused, $if_empty);
}
/**
* Bind a queue to an exchange.
*
* @param string $queue
* @param string $exchange
* @param string $routingKey
* @return void
*/
public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void
{
if (in_array(
implode('', compact('queue', 'exchange', 'routingKey')),
$this->boundQueues,
true
)) {
return;
}
$this->channel->queue_bind($queue, $exchange, $routingKey);
}
/**
* Purge the queue of messages.
*
* @param string $queue
* @return void
*/
public function purge(string $queue = null): void
{
// create a temporary channel, so the main channel will not be closed on exception
$channel = $this->connection->channel();
$channel->queue_purge($this->getQueue($queue));
$channel->close();
}
/**
* Acknowledge the message.
*
* @param RabbitMQJob $job
* @return void
*/
public function ack(RabbitMQJob $job): void
{
$this->channel->basic_ack($job->getRabbitMQMessage()->getDeliveryTag());
}
/**
* Reject the message.
*
* @param RabbitMQJob $job
* @param bool $requeue
*
* @return void
*/
public function reject(RabbitMQJob $job, bool $requeue = false): void
{
$this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue);
}
/**
* Create a AMQP message.
*
* @param $payload
* @param int $attempts
* @return array
*/
protected function createMessage($payload, int $attempts = 0): array
{
$properties = [
'content_type' => 'application/json',
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
];
if ($correlationId = json_decode($payload, true)['id'] ?? null) {
$properties['correlation_id'] = $correlationId;
}
if ($this->isPrioritizeDelayed()) {
$properties['priority'] = $attempts;
}
$message = new AMQPMessage($payload, $properties);
$message->set('application_headers', new AMQPTable([
'laravel' => [
'attempts' => $attempts,
],
]));
return [
$message,
$correlationId,
];
}
/**
* Create a payload array from the given job and data.
*
* @param object|string $job
* @param string $data
* @return array
*/
protected function createPayloadArray($job, $data = '')
{
return array_merge(parent::createPayloadArray($job, $data), [
'id' => $this->getRandomId(),
]);
}
/**
* Get a random ID string.
*
* @return string
*/
protected function getRandomId(): string
{
return Str::random(32);
}
/**
* Close the connection to RabbitMQ.
*
* @return void
* @throws Exception
*/
public function close(): void
{
if ($this->currentJob && ! $this->currentJob->isDeletedOrReleased()) {
$this->reject($this->currentJob, true);
}
try {
$this->connection->close();
} catch (ErrorException $exception) {
// Ignore the exception
}
}
/**
* Get the Queue arguments.
*
* @param string $destination
* @return array
*/
protected function getQueueArguments(string $destination): array
{
$arguments = [];
// Messages without a priority property are treated as if their priority were 0.
// Messages with a priority which is higher than the queue's maximum, are treated as if they were
// published with the maximum priority.
if ($this->isPrioritizeDelayed()) {
$arguments['x-max-priority'] = $this->getQueueMaxPriority();
}
if ($this->isRerouteFailed()) {
$arguments['x-dead-letter-exchange'] = $this->getFailedExchange() ?? '';
$arguments['x-dead-letter-routing-key'] = $this->getFailedRoutingKey($destination);
}
return $arguments;
}
/**
* Get the Delay queue arguments.
*
* @param string $destination
* @param int $ttl
* @return array
*/
protected function getDelayQueueArguments(string $destination, int $ttl): array
{
return [
'x-dead-letter-exchange' => $this->getExchange() ?? '',
'x-dead-letter-routing-key' => $this->getRoutingKey($destination),
'x-message-ttl' => $ttl,
'x-expires' => $ttl * 2,
];
}
/**
* Returns &true;, if delayed messages should be prioritized.
*
* @return bool
*/
protected function isPrioritizeDelayed(): bool
{
return boolval(Arr::get($this->options, 'prioritize_delayed') ?: false);
}
/**
* Returns a integer with a default of '2' for when using prioritization on delayed messages.
* If priority queues are desired, we recommend using between 1 and 10.
* Using more priority layers, will consume more CPU resources and would affect runtimes.
*
* @see https://www.rabbitmq.com/priority.html
* @return int
*/
protected function getQueueMaxPriority(): int
{
return intval(Arr::get($this->options, 'queue_max_priority') ?: 2);
}
/**
* Get the exchange name, or &null; as default value.
*
* @param string $exchange
* @return string|null
*/
protected function getExchange(string $exchange = null): ?string
{
return $exchange ?: Arr::get($this->options, 'exchange') ?: null;
}
/**
* Get the routing-key for when you use exchanges
* The default routing-key is the given destination.
*
* @param string $destination
* @return string
*/
protected function getRoutingKey(string $destination): string
{
return ltrim(sprintf(Arr::get($this->options, 'exchange_routing_key') ?: '%s', $destination), '.');
}
/**
* Get the exchangeType, or AMQPExchangeType::DIRECT as default.
*
* @param string|null $type
* @return string
*/
protected function getExchangeType(?string $type = null): string
{
return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get($this->options, 'exchange_type') ?: 'direct')) ?: AMQPExchangeType::DIRECT;
}
/**
* Returns &true;, if failed messages should be rerouted.
*
* @return bool
*/
protected function isRerouteFailed(): bool
{
return boolval(Arr::get($this->options, 'reroute_failed') ?: false);
}
/**
* Get the exchange for failed messages.
*
* @param string|null $exchange
* @return string|null
*/
protected function getFailedExchange(string $exchange = null): ?string
{
return $exchange ?: Arr::get($this->options, 'failed_exchange') ?: null;
}
/**
* Get the routing-key for failed messages
* The default routing-key is the given destination substituted by '.failed'.
*
* @param string $destination
* @return string
*/
protected function getFailedRoutingKey(string $destination): string
{
return ltrim(sprintf(Arr::get($this->options, 'failed_routing_key') ?: '%s.failed', $destination), '.');
}
/**
* Checks if the exchange was already declared.
*
* @param string $name
* @return bool
*/
protected function isExchangeDeclared(string $name): bool
{
return in_array($name, $this->exchanges, true);
}
/**
* Checks if the queue was already declared.
*
* @param string $name
* @return bool
*/
protected function isQueueDeclared(string $name): bool
{
return in_array($name, $this->queues, true);
}
/**
* Declare the destination when necessary.
*
* @param string $destination
* @param string|null $exchange
* @param string|null $exchangeType
* @return void
* @throws AMQPProtocolChannelException
*/
protected function declareDestination(string $destination, ?string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void
{
// When a exchange is provided and no exchange is present in RabbitMQ, create an exchange.
if ($exchange && ! $this->isExchangeExists($exchange)) {
$this->declareExchange($exchange, $exchangeType);
}
// When a exchange is provided, just return.
if ($exchange) {
return;
}
// When the queue already exists, just return.
if ($this->isQueueExists($destination)) {
return;
}
// Create a queue for amq.direct publishing.
$this->declareQueue($destination, true, false, $this->getQueueArguments($destination));
}
/**
* Determine all publish properties.
*
* @param $queue
* @param array $options
* @return array
*/
protected function publishProperties($queue, array $options = []): array
{
$queue = $this->getQueue($queue);
$attempts = Arr::get($options, 'attempts') ?: 0;
$destination = $this->getRoutingKey($queue);
$exchange = $this->getExchange();
$exchangeType = $this->getExchangeType();
return [$destination, $exchange, $exchangeType, $attempts];
}
}