cottagelabs / coar-notifications
A COAR Notification manager that can send and receive COAR Notifications.
Requires
- php: >=7.4
- ext-curl: *
- ext-json: *
- ext-pdo: *
- doctrine/annotations: ^1.13
- doctrine/orm: ^2.14.1
- guzzlehttp/guzzle: ^7.5
- monolog/monolog: ^1.26
- ramsey/uuid: ^4.2
- symfony/cache: ^5.3
Requires (Dev)
- phpunit/phpunit: ^9
README
The COAR Notification (CNs) Manager can both act as an inbox that receives notification as well as send notifications. CNs are Linked Data Notifications that have a Activity Streams 2.0 like structure (see Appendix for an example of a COAR Notification).
CNs do not have a final specification, but notification patterns, further exemplified in example scenarios give good guidance.
It is written in PHP 7.4 with Guzzle (wrapped around
cURL) and
JSON. It uses Doctrine for
persistence in a database, Monolog for logging,
ramsey/uuid to generate v4 UUIDs and Guzzle to send notifications (see
composer.json
for version numbers).
This Notification Manager was originally designed to support scenarios # 1, 2, 3, 4 and 9 that involve three different types of activities: announcing a review, requesting a review and announcing an endorsement. This was later expanded to include: acknowledging and accepting, acknowledging and rejecting, announcing an ingest, announcing a relationship, requesting an endorsement, requesting an ingest and retracting an offer for a total of ten different notication patterns.
The ten supported patterns in alphabetical order:
- Acknowledge and Accept
- Acknowledge and Reject
- Announce Endorsement
- Announce Ingest
- Announce Relationship
- Announce Review
- Request Endorsement
- Request Ingest
- Request Review
- Retract Offer
Installation & setup
The easiest way to install is to use Composer.
$ composer require cottagelabs/coar-notifications
To set up the inbox you need a MySQL/MariaDB database.
Create the database schema by creating the file cli-config.php
in the project's root folder (see
Doctrine documentation
and example in the docker
folder) and running: $ php vendor/bin/doctrine orm:schema-tool:create
to create the
database schema (docker exec coar_notify_php php vendor/bin/doctrine orm:schema-tool:create
from outside the container)
and the Dockerfile will run an Apache 2 web server.
Usage
This module does not address the discovery part of the LDN recommendation. That is up to the developer of the web application.
A few configuration parameters can be passed to COARNotificationManager
:
In the following examples we will assume we have created a COARNotificationManager instances like so:
$conn = array('host' => '127.0.0.1', 'driver' => 'pdo_mysql', 'user' => 'root', 'password' => 'my-secret-pw', 'dbname' => 'coar_notifications', ); $logger = new Logger('NotifyCOARLogger'); $handler = new RotatingFileHandler(__DIR__ . '/log/NotifyCOARLogger.log', 0, Logger::DEBUG, true, 0664); $formatter = new LineFormatter(null, null, false, true); $handler->setFormatter($formatter); $logger->pushHandler($handler); // Initialising a COARNotificationManager $coarNotificationManager = new COARNotificationManager($conn, $logger);
A table named notifications
is assumed to have been created (see Installation & setup above). This table will contain all notifications using a
single table inheritance mapping discriminator column named direction
, to differentiate between INBOUND
and OUTBOUND
notifications.
The COAR Notification manager is not aware of requests, these must be handled by the web application's logic. The manager provides appropriate responses:
Options
As per the LDN recommendation, a sender may may use an OPTIONS request to determine the RDF content types accepted by the server. This method will set the response headers Allow
and Accept-Post
.
It is up to the web application to determine that a OPTIONS request has been made.
Example:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { $coarNotificationManager->setOptionsResponseHeaders(); }
Post
This method attempts to decode a JSON payload and depending on it's success, will respond with one of HTTP codes:
- 400 if JSON is malformed
- 422 if notification is not valid or an error occurs when persisting the notification
- 201 if notification is successfully received and persisted
- 415 if an unsupported media type is used
Example:
if ($_SERVER['REQUEST_METHOD'] === 'POST') { $coarNotificationManager->getPostResponse(); }
Get
As per the LDN recommendation, this method lists the inbox's contents. There is no pagination method available via the COAR Notification Manager. However Doctrine supports pagination.
Example:
if ($_SERVER['REQUEST_METHOD'] === 'GET') { $response = $coarNotificationManager->getGetResponse(); // Optionally manipulate $response // ... echo $response; }
Example output:
{ "@context": "http://www.w3.org/ns/ldp", "@id": "https://overlay-journal.com", "contains": [ "https://overlay-journal.com/inbox/b5df022a-fc6c-4679-8246-d288f5690b17" ] }
Sending
In order to send notifications you first need to initialize a $coarNotificationInbox
object.
This is because outbound notifications are saved to the same database table as described above.
Before creating an OutboundNotification
object the necessary parts are created in isolation:
// This represents the entity sending the notification $actor = new COARNotificationActor("actorId", "actorName", "Person"); // The journal that the actor wishes to publish in $object = new COARNotificationObject("https://overlay-journal.com/reviews/000001/00001", "https://doi.org/10.3214/987654", array("Document", "sorg:Review")); // The url of the context object, see below $url = new COARNotificationURL("https://research-organisation.org/repository/preprint/201203/421/content.pdf", "application/pdf", array("Article", "sorg:ScholarlyArticle")); // The actual content that is to be actioned on $context = new COARNotificationContext("https://research-organisation.org/repository/preprint/201203/421/", "https://doi.org/10.5555/12345680", array("sorg:AboutPage"), $url); // This represents the target system the notification is to be delivered to $target = new COARNotificationTarget("https://research-organisation.org/repository", "http://localhost:81/post"); // Create the notification $notification = $coarNotificationManager->createOutboundNotification($actor, $object, $context, $target);
Put together, these POPOs constitute an almost fully formed COAR Notification, only thing left is to call one of the following:
$coarNotificationManager->announceEndorsement($notification);
or
$coarNotificationManager->announceReview($notification);
or :
$notification->requestReview($notification);
Note that all of the patterns have the optional $inReplyTo
parameter to indicate that the step is in response to an earlier step.
Development
A docker-compose.yml
is including to make experimentation and development easy. It connects
an Apache HTTPD2 container with a MySQL container. Just run:
$ docker-compose up
At http://localhost:8060/
there is an interactive form you can use to semi-manually create a COAR Notification. Note
that three fields; type
, url type
and object type
expect comma separated values that will be split
into an array of strings.
At http://localhost:8060/inbox.php
notifications sent or received will be listed by id and timestamp.
Unit testing
This project uses PHPUnit for unit testing. In order to run the tests bring up the docker containers with
docker compose up -d
and then run docker exec coar_notify_php ./vendor/bin/phpunit tests
.
License
Copyright © 2021
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Funding
Project funded with support from the French National Fund for Open Science
Projet financé avec le soutien du Fonds National pour la Science Ouverte
Appendix
Example of a COAR Notification JSON payload:
{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://purl.org/coar/notify" ], "actor": { "id": "https://overlay-journal.com", "name": "Overlay Journal", "type": "Service" }, "context": { "id": "https://research-organisation.org/repository/preprint/201203/421/", "ietf:cite-as": "https://doi.org/10.5555/12345680", "type": "sorg:AboutPage", "url": { "id": "https://research-organisation.org/repository/preprint/201203/421/content.pdf", "media-type": "application/pdf", "type": [ "Article", "sorg:ScholarlyArticle" ] } }, "id": "urn:uuid:94ecae35-dcfd-4182-8550-22c7164fe23f", "object": { "id": "https://overlay-journal.com/reviews/000001/00001", "ietf:cite-as": "https://doi.org/10.3214/987654", "type": [ "Document", "sorg:Review" ] }, "origin": { "id": "https://overlay-journal.com/system", "inbox": "https://overlay-journal.com/system/inbox/", "type": "Service" }, "target": { "id": "https://research-organisation.org/repository", "inbox": "https://research-organisation.org/repository/inbox/", "type": "Service" }, "type": [ "Announce", "coar-notify:ReviewAction" ] }