=begin pod

=head1 NAME

JSON::Webhook - provide webhook logic for processing JSON payloads

=head1 SYNOPSIS

=begin code :lang<raku>
use JSON::Webhook;

JSON::Webhook.new :$host, :$port, :$application;
=end code

=head1 DESCRIPTION

The C<JSON::Webhook> distribution provides a C<JSON::Webhook> role that
provides the logic for running a webservice that accepts JSON payloads
(typically from services such as Github / Codeberg) and collects them
using the logic provided by
L<JSON::Collector|https://raku.land/zef:lizmat/JSON::Collector>.

Customization can either be done by providing extra named arguments
to the C<new> and/or C<application> methods, or by consuming the
C<JSON::Webhook> role into a class that provides customized
C<collector> and C<processor> methods.

=begin code :lang<raku>
use JSON::Webhook;

# Custom class that inserts the value of the "channels" named
# argument from the request into the data structure to be saved
class JSON::Webhook::Channels does JSON::Webhook {
    method processor() {
        -> %data, %nameds {
            %data<channels> := .split(",") with %nameds<channels>;
            %data
        }
    }
}
=end code

=head1 METHODS

=head2 new

=begin code :lang<raku>
say "starting server";
JSON::Webhook.new :$host, :$port, :$application, ...
say "server stopped";
=end code

The C<new> method provides the simplest way to set up an endpoint
for a JSON push service.  It creates a C<JSON::Webhook> object with
the given arguments.

If a C<JSON::Webhook> is sunk, it calls the C<serve> method which
starts listening on the indicated host IP and port and activates
a control-C handler to stop the server in an orderly manner.

It takes the following named arguments:

=head3 :host

The name of the endpoint to set up a listening server on.  Defaults to
calling the C<host> method, which by default returns the value of the
dynamic variable C<$*WEBHOOK-HOST>, the environment variable
C<WEBHOOK_HOST> or C<"localhost">.

=head3 :port

The port number on which the server will be listening on.  Defaults to
calling the C<port> method, which by default returns the value of the
dynamic variable C<$*WEBHOOK-PORT>, the environment variable
C<WEBHOOK_PORT> or C<9999>.

=head3 :application

The C<Cro::HTTP::Router::RouteSet> compatible object that will be used
to run the server with.  If not specified, will call the C<application>
class method with the rest of the named arguments to set up the
application.

=head3 additional named argments

Any additional named arguments are passed on as appropriate.

=head2 application

The C<application> instance method returns whatever was (implicitely)
specified with C<:application> named argument to the C<new> method.

=begin code :lang<raku>
my $application = JSON::Webhook.application :$collector, :$processor, ...
JSON::Webhook.new(:$host, :$port, :$application);
=end code

The C<application> class method returns a C<Cro::HTTP::Router::RouteSet>
compatible object that can be used as the "application" in setting up
a C<JSON::Webhook> object.

It takes the following named arguments:

=head3 :collector

The object with C<JSON::Collector> semantics that will be used to store
any incoming JSON payloads.  If not specified, it will call the
C<collector> method, which by default creates a C<JSON::Collector>
object with any additional named arguments passed.

=head3 :processor

A C<Callable> that will be called with the data structure representing
the JSON payload that was received, and any names arguments that were
part of the request being served.  It should return the data structure
that should be stored by the collector.  If not specified, it will call
the C<processor> method, which by default returns a C<Callable> that
will just return the data structure of the JSON payload verbatim.

=head2 serve

=begin code :lang<raku>
my $webhook = JSON::Webhook.new(:$host, :$port, :$application);
say "starting";
$webhook.serve;
say "stopped";
=end code

The C<serve> method takes an instantiated C<JSON::Webhook> object, starts
listening on the given C<host> and C<port>, and starts serving requests
with the given <application>.  Also installs a control-C handler for an
orderly shutdown when control-C is pressed.

=head2 stop

=begin code :lang<raku>
my $webhook = JSON::Webhook.new(:$host, :$port, :$application);
say "starting";
start {
    $webhook.serve;
    say "stopped";
}
...
$webhook.stop;  # stopped
=end code

The C<stop> method will stop the server from accepting requests, and
thus continue execution B<after> the call to C<serve>.

=head1 OVERRIDABLE METHODS

These methods can be overriden by a consumer of the C<JSON::Webhook>
class to customize behaviour.

=head2 host

Expected to return a value that can be used as a C<:host> argument to
C<Cro::HTTP::Server.new>.  By default returns the value of the
dynamic variable C<$*WEBHOOK-HOST>, the environment variable
C<WEBHOOK_HOST> or C<"localhost">.

=head2 port

Expected to return a value that can be used as a C<:port> argument to
C<Cro::HTTP::Server.new>.  By default returns the value of the
dynamic variable C<$*WEBHOOK-PORT>, the environment variable
C<WEBHOOK_PORT> or C<9999>.

=head2 collector

Expected to return an object with C<JSON::Collector> semantics.
By default creates a C<JSON::Collector> object with any additional
named arguments passed.

=head2 processor

Expected to return a C<Callable> that will take the data structure
parsed from a JSON payload, and any named arguments in the request,
and return a possibly adapted data structure to be stored by the
collector.  By default, this is a simple C<Callable> that returns
the data structure unchanged.

=head1 AUTHOR

Elizabeth Mattijsen <liz@raku.rocks>

=head1 COPYRIGHT AND LICENSE

Copyright 2026 Elizabeth Mattijsen

Source can be located at: https://codeberg.org/lizmat/JSON-Collector .
Comments and Pull Requests are welcome.

If you like this module, or what I'm doing more generally, committing to a
L<small sponsorship|https://github.com/sponsors/lizmat/>  would mean a great
deal to me!

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod

# vim: expandtab shiftwidth=4
