Bivio::Agent::TaskEvent
# Copyright (c) 2008 bivio Software, Inc. All Rights Reserved.
# $Id$
package Bivio::Agent::TaskEvent;
use strict;
use Bivio::Base 'Collection.Attributes';
use Bivio::IO::Trace;
our($_TRACE);
my($_R) = b_use('IO.Ref');
my($_S) = b_use('Type.String');
my($_TI) = b_use('Agent.TaskId');
my($_DC) = b_use('Bivio.DieCode');
my($_FCT) = b_use('FacadeComponent.Task');
my($_PARAMS) = [
@{Bivio::Agent::Request->FORMAT_URI_PARAMETERS},
qw(no_form method form_model_state),
];
my($_DEFAULTS) = {
map(($_ => undef), @$_PARAMS),
method => 'client_redirect',
};
my($_EXPECTED) = {
put_durable_server_redirect_state => [@$_PARAMS, qw()],
method_that_does_nothing => [],
server_redirect => [qw(
task_id realm query form path_info no_context require_context no_form
carry_path_info carry_query facade_uri
)],
client_redirect => [qw(
task_id realm query path_info no_context require_context no_form uri http_status_code
require_absolute carry_path_info carry_query facade_uri
)],
};
my($_IMPLICIT_OVERRIDE) = {
carry_query => 1,
carry_path_info => 1,
};
sub TASK_EXECUTE_STOP {
return {
method => 'method_that_does_nothing',
};
}
sub internal_as_string {
my($self) = @_;
my($a) = $self->get_shallow_copy;
return map(
$_S->compare($a->{$_}, $_DEFAULTS->{$_}) == 0 ? ()
: "$_=" . ($_ eq 'task_id' ? $a->{$_}->get_name
: $_R->to_short_string($a->{$_})),
@$_PARAMS,
);
}
sub call_method {
my($self, $object, $method, $override) = @_;
my($c) = $self->get_shallow_copy;
$method ||= $c->{method} || b_die($self, ': no method');
my($e) = $_EXPECTED->{$method} || b_die($method, ': unconfigured method');
return $object->$method({
map(exists($c->{$_}) ? ($_ => $c->{$_}) : (), @$e),
$override ? %$override : (),
});
}
sub new {
my($proto, $params, $req) = @_;
my($self) = shift->SUPER::new({%$_DEFAULTS, %$params});
$params = $self->internal_get;
foreach my $k (qw(path_info query)) {
next
unless $params->{"carry_$k"} and my $curr = $req->unsafe_get($k);
b_die($params, ": only one of $k and carry_$k ", $req)
if $params->{$k};
$params->{$k} = $req->unsafe_get($k)
}
$params->{task_id} &&= $_TI->from_any($params->{task_id});
return $self->internal_put($params);
}
sub parse_die {
my($proto, $die, $task, $req) = @_;
my($die_code) = $die->get('code');
my($params) = $task->unsafe_params_for_die_code($die_code);
_trace($task, ' ', $die, ' ', $params) if $_TRACE;
unless (defined($params)) {
# Default mapped?
my($n) = 'DEFAULT_ERROR_REDIRECT_' . $die_code->get_name;
my($t) = $_TI->unsafe_from_name($n)
|| $_TI->unsafe_from_name($n = 'DEFAULT_ERROR_REDIRECT');
unless (defined($t)) {
_trace('not a mapped task: ', $die_code) if $_TRACE;
return;
}
$params = _assert_params($n, $t, $req);
}
unless (b_use('UI.Task')->is_defined_for_facade($params->{task_id}, $req)) {
_trace('error redirect not defined in facade: ', $params)
if $_TRACE;
return;
}
if ($req->need_to_toggle_secure_agent_execution($params->{task_id})) {
$req->put_client_redirect_state($params);
$die->set_code($_DC->CLIENT_REDIRECT_TASK);
return;
}
$die->set_code(
$_DC->SERVER_REDIRECT_TASK,
task_id => $params->{task_id},
);
# Leave uri untouched.
$proto->new({%$_IMPLICIT_OVERRIDE, %$params}, $req)
->call_method($req, put_durable_server_redirect_state => {
form => undef,
form_model => undef,
});
return;
}
sub parse_item {
my($proto, $cause, $params) = @_;
return _assert_params($cause, $params);
}
sub parse_item_result {
my($proto, $params, $task, $req, $item) = @_;
my($override) = {};
if (ref($params) eq 'HASH') {
if ($params->{method}) {
return 0
if $params->{method} eq 'next_item_execute';
return 1
if $params->{method} eq 'last_item_execute';
}
$params->{task_id} ||= 'next'
unless $params->{uri};
}
elsif ($params eq '1') {
$params = $proto->TASK_EXECUTE_STOP;
}
else {
b_die('server_redirect.*: invalid form, use a hash')
if $params =~ /^(server_redirect)\./;
$params = {task_id => $params};
$override = $_IMPLICIT_OVERRIDE;
}
unless (
($params->{method} || '') eq 'method_that_does_nothing'
|| $params->{uri}
|| $_TI->is_blesser_of($params->{task_id})
) {
if (($params->{task_id} || '') =~ $task->TASK_ATTR_RE) {
$params = {
%{$task->dep_get_attr(delete($params->{task_id}))},
%$params,
};
}
else {
b_die(
$params,
': invalid task_id returned by ',
_item_as_string($task, $item),
) unless $params->{task_id}
= $_TI->unsafe_from_name($params->{task_id});
}
}
return $proto->new(
_assert_params(
_item_as_string($task, $item),
{%$override, %$params},
$req,
),
$req,
);
}
sub _assert_params {
my($cause, $params, $req) = @_;
$params = {
task_id => $params,
no_context => 0,
method => 'client_redirect',
} unless ref($params) eq 'HASH';
if ($params->{task_id}) {
b_die(
$cause,
': params must have uri OR task_id but not both: ',
$params,
) if exists($params->{uri});
$params->{task_id} = $_TI->from_any($params->{task_id});
$params->{method} = 'server_redirect'
if $req && !$_FCT->has_uri($params->{task_id}, $req);
}
elsif ($params->{uri}) {
my($u) = $params->{uri};
b_die($u, ': uri must begin with a / or a scheme: ', $cause)
unless $u =~ m{^(?:/|\w+:)}s;
b_die($cause, ': uri *query and *path_info are mutually exclusive: ', $params)
if $u =~ /\?/
&& grep(/path_info|query/ && $params->{$_}, keys(%$params));
$params->{carry_query} = 0;
$params->{carry_path_info} = 0;
}
return $params;
}
sub _item_as_string {
my($task, $item) = @_;
my($instance, $method, $args) = @$item;
return $task->get('id')->get_name
. '['
. (defined($instance)
? (ref($instance) || $instance) . '->' . $method
: 'code'
) . ']';
}
1;