WebService-Xential/lib/WebService/Xential.pm
package WebService::Xential;
our $VERSION = '0.004';
use v5.26;
use Object::Pad;
# ABSTRACT: A Xential REST API module
class WebService::Xential;
use Carp qw(croak);
use OpenAPI::Client;
use Try::Tiny;
use Mojo::Content::Single;
use Mojo::Asset::Memory;
use JSON::XS qw(encode_json);
use Types::Serialiser qw();
field $api_key :param;
field $api_user :param;
field $api_host :param :accessor;
field $api_port :param = undef;
field $api_path :param = '/xential/modpages/next.oas/api';
field $client :accessor;
ADJUST {
my $definition = sprintf('data://%s/xential.json', ref $self);
$client = OpenAPI::Client->new($definition);
my $host = Mojo::URL->new();
$host->scheme('https');
$host->host($api_host);
$host->path($api_path);
$client->base_url($host);
$client->ua->on(
start => sub ($ua, $tx) {
$tx->req->headers->add("Accept" => "application/json");
unless ($tx->req->headers->header('XSessionID')) {
$tx->req->url->userinfo("$api_user:$api_key");
}
}
);
$client->ua->transactor->add_generator(
'createData' => \&create_ticket_data);
}
sub create_ticket_data {
my $t = shift;
my $tx = shift;
$tx->req->headers->content_type('multipart/form-data');
my $headers = $tx->req->headers;
my $data = shift;
my $xml = Mojo::Content::Single->new();
my $options = Mojo::Content::Single->new();
$options->asset(
Mojo::Asset::Memory->new->add_chunk(
encode_json($data->{options})
)
);
$xml->asset(Mojo::Asset::Memory->new->add_chunk($data->{xml}));
$options->headers->content_disposition('form-data; name="options"');
$xml->headers->content_disposition(
'form-data; name="ticketData"; filename="ticketData.xml"');
$xml->headers->content_type("text/xml");
my @parts = ($options, $xml);
$tx->req->content(
Mojo::Content::MultiPart->new(
headers => $headers,
parts => \@parts
)
);
}
method has_api_host {
return $api_host ? 1 : 0;
}
method whoami($session_id = undef) {
return $self->api_call('op_auth_whoami',
{ $session_id ? (XSessionID => $session_id) : () });
}
method logout($session_id = undef) {
return $self->api_call('op_auth_logout',
{ $session_id ? (XSessionID => $session_id) : () });
}
method impersonate($username = undef, $uuid = undef, $session_id = undef) {
return $self->api_call(
'op_auth_impersonate',
{
$username ? (userName => $username) : (),
$uuid ? (userUuid => $uuid) : (),
$session_id ? (XSessionID => $session_id) : (),
}
);
}
method create_ticket($xml, $options, $session_id = undef) {
return $self->api_call(
'op_createTicket',
{
$session_id ? (XSessionID => $session_id) : (),
},
{
createData => {
options => $options,
xml => $xml,
}
}
);
}
method start_document($url = undef, $uuid = undef, $session_id = undef) {
return $self->api_call(
'op_document_startDocument',
{
$session_id ? (XSessionID => $session_id) : (),
$uuid ? (ticketUuid => $uuid) : (),
$url ? (xmldataurl => $url) : (),
}
);
}
method build_document($close, $uuid, $session_id = undef) {
return $self->api_call(
'op_document_buildDocument',
{
close => $close ? $Types::Serialiser::true : $Types::Serialiser::false,
documentUuid => $uuid,
$session_id ? (XSessionID => $session_id) : (),
}
);
}
method api_call($operation, $query, $content = {}) {
my $tx = try {
$client->call($operation => $query, %$content);
}
catch {
die("Died calling Xential API with operation '$operation': $_", $/);
};
if ($tx->error) {
# Not found, no result
return if $tx->res->code == 404;
# Any other error
croak(
sprintf(
"Error calling Xential API with operation '%s': '%s' (%s)",
$operation, $tx->result->body, $tx->error->{message}
),
);
}
return $tx->res->json;
}
1;
=pod
=encoding UTF-8
=head1 NAME
WebService::Xential - A Xential REST API module
=head1 VERSION
version 0.004
=head1 SYNOPSIS
my $xential = WebService::Xential->new(
api_user => 'foo',
api_key => 'foo',
api_host => '127.0.0.1',
);
my $who = $xential->whoami();
my $other = $xential->impersonate(..., $who->{XSessionId});
my $session_id = $other{XSessionID};
my $ticket = $xential->create_ticket($xml, \%options, $session_id);
my $start = $xential->start_document(
$ticket->{startDocumentUrl},
$ticket->{ticketUuid},
$session_id
);
# Status is either INVALID or VALID
if ($start->{status} eq 'VALID') {
my $build = $xential->build_document(
1,
$start->{documentUuid},
$session_id
);
if ($build->{status} eq 'done') {
# build succeeded
}
else {
# build failed
}
}
else {
use URI;
$uri = URI->new($start{resumeUrl});
$uri->scheme('https');
$uri->query_form($uri->query_form, afterOpenAction => 'close');
$uri->host($xential->api_host);
# redirect user to $uri
}
=head1 DESCRIPTION
This module implements the REST API of Xential.
=head1 ATTRIBUTES
=head2 api_host
The API host of the Xential WebService
=head2 client
The L<OpenAPI::Client>
=head1 METHODS
=head2 new()
my $xential = WebService::Xential->new(
api_user => 'foo',
api_key => 'foo',
api_host => '127.0.0.1',
);
=head2 has_api_host()
Tells you if you have a custom API host defined
=head2 whoami($session_id)
Implements the whoami call from Xential
=head2 logout($session_id)
Implements the logout call from Xential
=head2 impersonate($username, $user_uuid, $session_id)
Implements the impersonate call from Xential
=head2 create_ticket($xml, $options, $session_id)
Implements the create_ticket call from Xential
=head2 start_document($username, $user_uuid, $session_id)
Implements the start_document call from Xential
=head2 build_document($username, $user_uuid, $session_id)
Implements the build_document call from Xential
=head2 api_call($operation, $query, $content)
A wrapper around the L<OpenAPI::Client::call> function. Returns the JSON from
the endpoint.
=head1 AUTHOR
Wesley Schwengle <waterkip@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2024 by Wesley Schwengle.
This is free software, licensed under:
The (three-clause) BSD License
=cut
__DATA__
@@ xential.json
{"openapi":"3.0.0","servers":[{"url":"https://zaaksysteem.labs.xential.nl/xential/modpages/next.oas/api"}],"info":{"title":"interaction|next oas Api service","version":"1.0"},"components":{"securitySchemes":{"x_basic":{"type":"http","scheme":"basic"},"x_apikey":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"NameValuePair":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"StorableObject":{"type":"object","properties":{"uuid":{"type":"string"},"objectTypeId":{"type":"string"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}}}},"Address":{"type":"object","properties":{"properties":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}}}},"WebHook":{"type":"object","properties":{"hooks":{"type":"array","items":{"type":"object","properties":{"event":{"type":"string","enum":["document.built","document.builtSingle","document.deleted"]},"retries":{"type":"object","properties":{"count":{"type":"integer","minimum":1,"maximum":10},"delayMs":{"type":"integer","minimum":100,"maximum":3600000}}},"request":{"type":"object","properties":{"url":{"type":"string","format":"url","example":"https://www.myApp.org/regDoc"},"method":{"type":"string","enum":["POST","GET","UPDATE","DELETE"]},"headers":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}},"query":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}},"contentType":{"type":"string","enum":["application/json","text/xml"]},"requestBody":{"type":"string","example":"{documentUuid: \"{$document.uuid}\", documentSetUuid: \"{$documentSet.uuid}\"}"},"clientCertificateId":{"type":"string"}}}}}}}},"ticketOptionsSchema":{"type":"object","properties":{"printOption":{"type":"object"},"mailOption":{"type":"object"},"documentPropertiesOption":{"type":"object"},"valuesOption":{"type":"object"},"storageOption":{"type":"object","properties":{"fixed":{"type":"boolean"},"None":{"type":"object"},"Temp":{"$ref":"#/components/schemas/StorageTempFileTarget"}}},"attachmentsOption":{"type":"object"},"ttlOption":{"type":"object"},"selectionOption":{"type":"object","properties":{"templateUuid":{"type":"string"},"templatePath":{"type":"string"}}},"mergeOption":{"type":"object","properties":{"recipients":{"type":"array","items":{"$ref":"#/components/schemas/Address"}},"mode":{"type":"string","enum":["SINGLE_DOCUMENT","MULTIPLE_DOCUMENTS"]},"modeFixed":{"type":"boolean"}}},"webhooksOption":{"$ref":"#/components/schemas/WebHook"}}},"Crud_document":{"type":"object","required":["documentUuid"],"properties":{"documentUuid":{"type":"string"},"title":{"type":"string"},"buildStatus":{"type":"string","enum":["NONE","TODO","ACTIVE","DONE","ERROR","CANCELED"]}}},"StorageTempFileTarget":{"type":"object","properties":{"mimeTypes":{"type":"array","items":{"type":"string","enum":["application/pdf","application/msword","application/vnd.ms-excel","application/vnd.ms-powerpoint","application/vnd.oasis.opendocument.text","application/vnd.oasis.opendocument.spreadsheet","application/vnd.oasis.opendocument.presentation","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application/vnd.openxmlformats-officedocument.presentationml.presentation"]}}}},"StorageCorsaTarget":{"type":"object","properties":{"properties":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}}}},"StorageVerseonTarget":{"type":"object","properties":{"properties":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}}}},"StorageESuiteTarget":{"type":"object","properties":{"properties":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}}}},"StorageZaaksysteem_nlTarget":{"type":"object","properties":{"properties":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}}}},"StorageCMISTarget":{"type":"object","properties":{"properties":{"type":"array","items":{"$ref":"#/components/schemas/NameValuePair"}}}}}},"security":[{"x_basic":[],"x_apikey":[]}],"paths":{"/auth/impersonate":{"post":{"summary":"Impersonate a user","description":"Impersonating a user is usefull for applications who need to lookup or create objects as if they we're a certain user. Please note that this requires you to pass the XSessionId cookie or header to the next call.","tags":["auth"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","properties":{"XSessionID":{"type":"string"}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"This is returned when the current user is not privileged to impersonate the passed user."},"404":{"description":"This is returned when there is no user found for the passed uuid or userName."},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_auth_impersonate","parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"userName","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"userUuid","description":"","required":false}]}},"/auth/logout":{"post":{"summary":"logout a user","description":"Logging out invalidates the session of the current user.","tags":["auth"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","properties":{"XSessionID":{"type":"string"}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_auth_logout","parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false}]}},"/auth/whoami":{"post":{"summary":"whoami","description":"See which user you current are / impersonating.","tags":["auth"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","properties":{"XSessionId":{"type":"string"},"user":{"type":"object","properties":{"uuid":{"type":"string"},"userName":{"type":"string"}}}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_auth_whoami","parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false}]}},"/template_utils/getUsableTemplates":{"post":{"summary":"get usable templates","description":"getUsableTemplates returns an array of objectReferences which represent a group, a link or a template which the user may use for starting a document or documentSet","tags":["template_utils"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","properties":{"objects":{"type":"array","items":{"$ref":"#/components/schemas/StorableObject"}}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_template_utils_getUsableTemplates","parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"parentGroupUuid","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"fieldName","description":"","required":false}]}},"/createTicket":{"post":{"summary":"prepare a new ticket","description":"prepare a new ticket","tags":["tickets"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","properties":{"ticketUuid":{"type":"string"},"startDocumentUrl":{"type":"string","format":"uri"}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_createTicket","requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"options":{"$ref":"#/components/schemas/ticketOptionsSchema"},"ticketData":{"type":"string","format":"binary"}}}}}},"parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false}]}},"/document_actions/addRecipient":{"post":{"tags":["document_actions","document"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","properties":{"addressId":{"type":"string"}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_document_actions_addRecipient","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Address"}}}},"parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"documentId","description":"","required":false}]}},"/document/startDocument":{"post":{"summary":"create a new document based on a ticket","description":"create a new document based on a ticket","tags":["document"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","required":["documentUuid"],"properties":{"documentUuid":{"type":"string"},"resumeUrl":{"type":"string","format":"uri"},"status":{"type":"string","enum":["VALID","INVALID"]}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_document_startDocument","parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"ticketUuid","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"xmldataurl","description":"","required":false}]}},"/document":{"post":{"summary":"Create new instance of document","description":"These are documents to be used in /xential","tags":["document","document"],"responses":{"200":{"description":"Ok"},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Crud_document"}}}},"parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false}]}},"/document/{document_id}":{"get":{"summary":"Get instance of document","description":"These are documents to be used in /xential","tags":["document","document"],"responses":{"200":{"description":"Ok"},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false},{"in":"path","schema":{"type":"string"},"name":"document_id","description":"","required":true}]},"delete":{"summary":"Delete instance of document","description":"These are documents to be used in /xential","tags":["document","document"],"responses":{"200":{"description":"Ok"},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false},{"in":"path","schema":{"type":"string"},"name":"document_id","description":"","required":true}]}},"/document/buildDocument":{"post":{"summary":"build a document","tags":["document"],"responses":{"200":{"description":"Ok","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"400":{"description":"Bad Request"},"401":{"description":"Authentication required"},"403":{"description":"Forbidden"},"404":{"description":"Not found"},"405":{"description":"Invalid input"},"500":{"description":"Error"}},"operationId":"op_document_buildDocument","parameters":[{"in":"header","schema":{"type":"string"},"name":"XSessionID","description":"","required":false},{"in":"query","schema":{"type":"string"},"name":"documentUuid","description":"","required":true},{"in":"query","schema":{"type":"boolean"},"name":"close","description":"","required":true}]}}},"tags":[{"name":"document","description":"Documents","externalDocs":{"description":"learn more about legacy docs","url":"http://localhost/help/legacydocs"}}]}