BEGIN {
$CatalystX::Controller::ExtJS::Direct::API::VERSION
=
'2.0.0'
;
}
__PACKAGE__->config(
action
=> {
end
=> {
ActionClass
=>
'+CatalystX::Action::ExtJS::Serialize'
},
begin
=> {
ActionClass
=>
'+CatalystX::Action::ExtJS::Deserialize'
},
index
=> {
Path
=>
undef
},
router
=> {
Path
=>
'router'
},
src
=> {
Local
=>
undef
},
},
default
=>
'application/json'
);
has
'api'
=> (
is
=>
'rw'
,
lazy_build
=> 1 );
has
'routes'
=> (
is
=>
'rw'
,
isa
=>
'HashRef'
,
default
=>
sub
{ {} } );
has
'namespace'
=> (
is
=>
'rw'
);
sub
index
{ }
sub
src {
my
(
$self
,
$c
) =
@_
;
$c
->res->content_type(
'application/javascript'
);
$c
->res->body(
'Ext.app.REMOTING_API = '
.
$self
->encoded_api(
$c
) .
';'
);
}
sub
_build_api {
my
(
$self
) =
@_
;
my
$c
=
$self
->_app;
my
$data
= {};
foreach
my
$name
(
$c
->controllers ) {
my
$controller
=
$c
->controller(
$name
);
$name
=~ s/^API:://;
$name
=~ s/:://g;
my
$meta
=
$controller
->meta;
next
unless
(
$controller
->can(
'is_direct'
) ||
$meta
->does_role(
'CatalystX::Controller::ExtJS::Direct'
) );
my
@methods
;
foreach
my
$method
(
$controller
->get_action_methods() ) {
next
unless
(
my
$action
=
$controller
->action_for(
$method
->name ) );
next
unless
(
exists
$action
->attributes->{Direct} );
my
@routes
=
CatalystX::Controller::ExtJS::Direct::Route::Factory->build(
$c
->dispatcher,
$action
);
foreach
my
$route
(
@routes
) {
$self
->routes->{
$name
}->{
$route
->name } =
$route
;
push
(
@methods
,
$route
->build_api );
}
}
$data
->{
$name
} = [
@methods
];
}
return
{
url
=>
$c
->dispatcher->uri_for_action(
$self
->action_for(
'router'
) )
->as_string,
type
=>
'remoting'
,
actions
=>
$data
};
}
sub
encoded_api {
my
(
$self
,
$c
) =
@_
;
return
JSON::Any->new->to_json(
$self
->set_namespace(
$self
->api,
$c
?
$c
->req->params->{namespace} : () ) );
}
sub
router {
my
(
$self
,
$c
) =
@_
;
my
$reqs
=
ref
$c
->req->data eq
'ARRAY'
?
$c
->req->data : [
$c
->req->data ];
my
$api
=
$self
->api;
my
$routes
=
$self
->routes;
if
(
keys
%{
$c
->req->body_params }
&& (
my
$params
=
$c
->req->body_params ) )
{
$reqs
= [
{
map
{
my
$orig
=
$_
;
$orig
=~ s/^ext//;
(
lc
(
$orig
) =>
delete
$params
->{
$_
} )
}
qw(extType extAction extMethod extTID extUpload)
}
];
if
(
$params
->{extData} ) {
$reqs
->[0]->{data} = JSON::Any->new->decode(
delete
$params
->{extData} );
}
else
{
$reqs
->[0]->{data} = [{
%$params
}];
}
}
my
@requests
;
foreach
my
$req
(
@$reqs
) {
unless
(
$req
&&
$req
->{action}
&&
exists
$routes
->{
$req
->{action} }
&&
exists
$routes
->{
$req
->{action} }->{
$req
->{method} } )
{
$self
->status_bad_request(
$c
, {
message
=>
sprintf
(
'method %s in action %s does not exist'
,
$req
->{method} ||
''
,
$req
->{action} ||
''
) } );
return
;
}
my
$route
=
$routes
->{
$req
->{action} }->{
$req
->{method} };
push
(
@requests
,
$route
->prepare_request(
$req
));
}
my
@res
;
REQUESTS:
foreach
my
$req
(
@requests
) {
$req
->{data} = [
$req
->{data}]
if
(
ref
$req
->{data} ne
"ARRAY"
);
$c
->stash->{upload} = 1
if
(
$req
->{upload} );
my
$route
=
$routes
->{
$req
->{action} }->{
$req
->{method} };
my
$params
= @{
$req
->{data}} &&
ref
$req
->{data}->[-1] eq
'HASH'
?
$req
->{data}->[-1] :
undef
;
my
$body
;
{
local
$c
->{response} =
$c
->response_class->new({});
local
$c
->{stash} = {};
local
$c
->{request} =
$c
->req;
local
$c
->{error} =
undef
;
$c
->req->parameters(
$params
);
$c
->req->body_parameters(
$params
);
my
%req
=
$route
->request(
$req
);
$c
->req(
$c
->request_class->new(%{
$c
->req},
%req
));
eval
{
$c
->visit(
$route
->build_url(
$req
->{data} ));
my
$response
=
$c
->res;
if
(
$response
->content_type eq
'application/json'
) {
(
my
$res_body
=
$response
->body) =~ s/^\xEF\xBB\xBF//;
my
$json
= JSON::Any->new->decode(
$res_body
);
$body
=
$json
;
}
else
{
$body
=
$response
->body;
}
if
(@{
$c
->error}) { 0 }
elsif
(
$response
->status >= 400) {
$c
->error(
$body
);
0;
}
else
{ 1 }
} or
do
{
my
$msg
;
if
(@{
$c
->error } && List::MoreUtils::all {
ref
$_
} @{
$c
->error }) {
$msg
= @{
$c
->error} == 1 ?
$c
->error->[0] :
$c
->error;
}
elsif
(
scalar
@{
$c
->error }) {
$msg
=
join
"\n"
, @{
$c
->error };
}
else
{
$msg
=
join
(
"\n"
,
"$@"
,
$c
->response->body || ());
}
push
(
@res
, {
type
=>
'exception'
,
tid
=>
$req
->{tid},
message
=>
$msg
,
status
=>
$c
->res->status });
$c
->
log
->debug(
$msg
)
if
(
$c
->debug);
next
REQUESTS;
};
}
my
$res
= {
map
{
$_
=>
$req
->{
$_
} }
qw(action method tid type)
};
push
(
@res
, {
%$res
,
result
=>
$body
} );
}
$c
->stash->{rest} =
@res
!= 1 ? \
@res
:
$res
[0];
}
sub
set_namespace {
my
(
$self
,
$api
,
$namespace
) =
@_
;
return
$api
unless
(
$namespace
&&
$namespace
=~ /^\w+(\.\w+)?$/);
return
{
%$api
,
namespace
=>
$namespace
};
}
sub
end {
my
(
$self
,
$c
) =
@_
;
$c
->stash->{rest} ||=
$self
->set_namespace(
$self
->api,
$c
->req->params->{namespace} );
}
1;