-
Notifications
You must be signed in to change notification settings - Fork 7
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
[Feature] Async generation #84
base: main
Are you sure you want to change the base?
[Feature] Async generation #84
Conversation
After discussing this with @ConstantBqt we have another usecase to take into account : If we generate the PDF but the webhook is not setup in our own app but is external. We should be able to declare such case. WDYT @maelanleborgne ? |
This is already present in the PR : // ...
$this->gotenberg->html()
->webhookUrls('https://external.service/webhook', 'https://external.service/webhook-error')
->webhookExtraHeaders(['X-ExternalService-Secret' => 'a_secret'])
->generateAsync()
; or via the configuration : sensiolabs_gotenberg:
# ...
webhook:
external_service:
success:
url: 'https://external.service/webhook'
error:
url: 'https://external.service/webhook-error'
default_options:
pdf:
html:
webhook: 'external_service' Or maybe I'm missing the point. |
Could this part be done in a dedicated PR ?
|
|
||
public function __construct( | ||
protected readonly GotenbergClientInterface $gotenbergClient, | ||
protected readonly AssetBaseDirFormatter $asset, | ||
protected readonly WebhookConfigurationRegistry|null $webhookConfigurationRegistry = null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of the WebhookConfigurationRegistry
? Could it be handled like other parameters ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It holds the different webhook configs available, so changes can be made at runtime using ->webhookConfiguration('custom_config')
. I'm not sure we can make it work with setConfiguration
because the config of a builder may be one of :
- the name of a webhook config defined above in the config file (so it will still have to access the config registry)
- a new definition : success and error with a route/url each (and then we'd have to make the router available in order to generate urls for the routes)
f7e3db6
to
a2ad358
Compare
a2ad358
to
13eb438
Compare
13eb438
to
075dedd
Compare
075dedd
to
9915a29
Compare
$container->registerForAutoconfiguration(WebhookConfigurationRegistryInterface::class) | ||
->addTag('sensiolabs_gotenberg.webhook_configuration_registry') | ||
; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of this tag ?
@@ -25,6 +25,7 @@ | |||
service('twig')->nullOnInvalid(), | |||
]) | |||
->call('setLogger', [service('logger')->nullOnInvalid()]) | |||
->call('setWebhookConfigurationRegistry', [service('.sensiolabs_gotenberg.webhook_configuration_registry')->nullOnInvalid()]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is ->nullOnInvalid()
needed since we always define this service in services.php
?
@@ -38,6 +39,7 @@ | |||
service('router')->nullOnInvalid(), | |||
]) | |||
->call('setLogger', [service('logger')->nullOnInvalid()]) | |||
->call('setWebhookConfigurationRegistry', [service('.sensiolabs_gotenberg.webhook_configuration_registry')->nullOnInvalid()]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why through a setter ? Why not the constructor ?
'Gotenberg-Webhook-Extra-Http-Headers' => json_encode($this->webhookExtraHeaders, \JSON_THROW_ON_ERROR), | ||
]; | ||
if (null !== $this->fileName) { | ||
$headers['Gotenberg-Output-Filename'] = basename($this->fileName, '.pdf'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the basename call for now. it will be handled correctly in another PR. Plus : the async is not only for PDFs.
return $this; | ||
} | ||
|
||
public function operationIdGenerator(\Closure $operationIdGenerator): static |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
phpstan annotation for closure please.
*/ | ||
public function webhookExtraHeaders(array $extraHeaders): static | ||
{ | ||
$this->webhookExtraHeaders = array_merge($this->webhookExtraHeaders, $extraHeaders); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so how do we reset this ?
|
||
protected static function defaultOperationIdGenerator(): string | ||
{ | ||
return 'gotenberg_'.bin2hex(random_bytes(16)).microtime(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should keep the id stateful per builder ? Reset it only when generating the PDF. Also the ID should be part of any operation I guess. Not only the async one. WDYT ?
@@ -586,6 +586,7 @@ protected function addConfiguration(string $configurationName, mixed $value): vo | |||
'fail_on_console_exceptions' => $this->failOnConsoleExceptions($value), | |||
'skip_network_idle_event' => $this->skipNetworkIdleEvent($value), | |||
'metadata' => $this->metadata($value), | |||
'webhook' => null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why ?
} | ||
$webhookConfiguration = $this->webhookConfigurationRegistry->get($webhook); | ||
|
||
return $this->webhookUrls($webhookConfiguration['success'], $webhookConfiguration['error']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe it can also contain extra headers ? Would that be relevant ? Or authent or anything else ?
@@ -24,7 +25,7 @@ final class TraceableScreenshotBuilder implements ScreenshotBuilderInterface | |||
|
|||
public function __construct( | |||
private readonly ScreenshotBuilderInterface $inner, | |||
private readonly Stopwatch $stopwatch, | |||
private readonly Stopwatch|null $stopwatch, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this change ?
->children() | ||
->scalarNode('name') | ||
->validate() | ||
->ifTrue(static function ($option) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return type + arg type
->append($this->addWebhookConfigurationNode('error')) | ||
->end() | ||
->validate() | ||
->ifTrue(static function ($option): bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arg type
Closes #46
Gotenberg allows async generation of files by passing webhooks urls in dedicated headers. Doing this, the request to Gotenberg server returns a 204 immediately, and once the file is generated, a call will be made by Gotenberg server on the specified endpoint to upload the generated file. This PR aims at enabling easy integration of this feature with the bundle.
The base of the feature is a new interface
AsyncBuilderInterface
that defines agenerateAsync
method. TheAbstractPdfBuilder
andAbstractScreenshotBuilder
now implement this interface.Another important addition is the
webhookUrls(string $successWebhook, string|null $errorWebhook)
that allows directly setting the webhooks url to use with the builders.To make things easier when not working directly with urls (if you'd like to generate the url from a route, or if using the Webhook component), a some new configuration options are available :
TODO: