SlideShare a Scribd company logo
1 of 37
Mojo – TL;DR
Simple REST Server
Hendrik Van Belleghem
Hendrik.vanbelleghem@gmail.com
2016 Perl Mongers Vlaanderen Meetup
Intro
• REST is typically HTTP based
• Not standardized
• Popularly used for APIs
• Outputs XML, HTML, JSON
• Verbs include GET, POST, DELETE & PUT
•
• https://en.wikipedia.org/wiki/Representational_state_transfer
Intro - REST
 REST typically uses standard HTTP calls:

 Specific entry: GET /User/name/<USER>
 Specific entry: GET /User/id/<ID>
 All Entries: GET /User
 Update entry: PUT /User
 Create entry: POST /User
 Delete entry: DELETE /User/name/<USER>
 Delete entry: DELETE /User/id/<ID>

Demo
• Cisco Secure ACS Server is used for Identity Management
• API access to user accounts, devices..
• Mojolicious does REST just fine
• Non-plugin approach
• Proof of Concept / Users only
Mojo
• Next generation Web: Websockets, non-blocking,
HTTP/1.1
• Web in a box: Development server & production server
included
• Light-weight: No dependencies
• Flexible routing
• … just like Catalyst
• DBIX::Class plugs in easily
• … just like Catalyst
• Done by the original Catalyst people
See http://www.mojolicious.org/
Mojo
Generate application skeleton:
# mojo generate app Net::Cisco::ACS::Mock
Output
[mkdir] /home/hendrik/net_cisco_acsmock/script
[write] /home/hendrik/net_cisco_acsmock/script/net_cisco_acsmock
[chmod] /home/hendrik/net_cisco_acsmock/script/net_cisco_acsmock 744
[mkdir] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS
[write] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS/Mock.pm
[mkdir] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS/Mock/Controller
[write] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS/Mock/Controller/Example.pm
[mkdir] /home/hendrik/net_cisco_acsmock/t
[write] /home/hendrik/net_cisco_acsmock/t/basic.t
[mkdir] /home/hendrik/net_cisco_acsmock/public
[write] /home/hendrik/net_cisco_acsmock/public/index.html
[mkdir] /home/hendrik/net_cisco_acsmock/templates/layouts
[write] /home/hendrik/net_cisco_acsmock/templates/layouts/default.html.ep
[mkdir] /home/hendrik/net_cisco_acsmock/templates/example
[write] /home/hendrik/net_cisco_acsmock/templates/example/welcome.html.ep
Create SQLite DB
# sqlite3 acs.db
sqlite > create table users
(id integer,
description text,
name text,
identitygroupname text,
changepassword text,
enablepassword text,
enabled integer,
password text,
passwordneverexpires integer,
passwordtype text,
dateexceeds text,
dateexceedsenabled integer
);
sqlite > insert into users (id, description, name, identitygroupname, changepassword, enablepassword, enabled, password,
passwordneverexpires, passwordtype, dateexceeds, dateexceedsenabled)
values(1, 'Description for #1', 'Foo', 'All Groups', 'Secret Change','Secret Enable',1,'Secret',1,'Internal','2020-01-01',1);
sqlite > insert into users (id, description, name, identitygroupname, changepassword, enablepassword, enabled, password,
passwordneverexpires, passwordtype, dateexceeds, dateexceedsenabled)
values(2, 'Description for #2', 'Bar', 'All Groups', 'Secret Change','Secret Enable',1,'Secret',1,'Internal','2020-01-01',1);
sqlite > .quit
Generate DBIx::Class Schema
Generate DBIX::Class classes automatically based on SQLite database (or any other
database)
# dbicdump -o dump_directory=./lib 
-o components='["InflateColumn::DateTime"]' 
-o debug=1 
Net::Cisco::ACS::Mock::Schema dbi:SQLite:acs.db
Run this as much as needed.. but it will overwrite the files!
Suggestion: Make sure to prepare your foreign keys properly!
lib/Net/Cisco/ACS/Mock.pm
package Net::Cisco::ACS::Mock;
use Mojo::Base 'Mojolicious';
use Net::Cisco::ACS::Mock::Schema;
sub startup {
my $self = shift;
my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db');
$self->helper(db => sub { return $schema; });
my $r = $self->routes;
$r->get("/Rest/Identity/User/name/:name")->to('User#query');
$r->get('/Rest/Identity/User/id/:id')->to('User#query');
$r->get('/Rest/Identity/User')->to('User#query');
$r->put('/Rest/Identity/User')->to('User#update');
$r->post('/Rest/Identity/User')->to('User#create');
$r->delete('/Rest/Identity/User/name/:name')->to('User#delete');
$r->delete('/Rest/Identity/User/id/:id')->to('User#delete');
}
1;
lib/Net/Cisco/ACS/Mock.pm
package Net::Cisco::ACS::Mock;
use Mojo::Base 'Mojolicious';
use Net::Cisco::ACS::Mock::Schema;
sub startup {
my $self = shift;
my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db');
$self->helper(db => sub { return $schema; });
my $r = $self->routes;
$r->get("/Rest/Identity/User/name/:name")->to('User#query');
$r->get('/Rest/Identity/User/id/:id')->to('User#query');
$r->get('/Rest/Identity/User')->to('User#query');
$r->put('/Rest/Identity/User')->to('User#update');
$r->post('/Rest/Identity/User')->to('User#create');
$r->delete('/Rest/Identity/User/name/:name')->to('User#delete');
$r->delete('/Rest/Identity/User/id/:id')->to('User#delete');
}
1;
Startup is called on Mojo startup
Store DBIx::Class connection in $self->db
lib/Net/Cisco/ACS/Mock.pm
package Net::Cisco::ACS::Mock;
use Mojo::Base 'Mojolicious';
use Net::Cisco::ACS::Mock::Schema;
sub startup {
my $self = shift;
my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db');
$self->helper(db => sub { return $schema; });
my $r = $self->routes;
$r->get("/Rest/Identity/User/name/:name")->to('User#query');
$r->get('/Rest/Identity/User/id/:id')->to('User#query');
$r->get('/Rest/Identity/User')->to('User#query');
$r->put('/Rest/Identity/User')->to('User#update');
$r->post('/Rest/Identity/User')->to('User#create');
$r->delete('/Rest/Identity/User/name/:name')->to('User#delete');
$r->delete('/Rest/Identity/User/id/:id')->to('User#delete');
}
1;
lib/Net/Cisco/ACS/Mock.pm
package Net::Cisco::ACS::Mock;
use Mojo::Base 'Mojolicious';
use Net::Cisco::ACS::Mock::Schema;
sub startup {
my $self = shift;
my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db');
$self->helper(db => sub { return $schema; });
my $r = $self->routes;
$r->get("/Rest/Identity/User/name/:name")->to('User#query');
$r->get('/Rest/Identity/User/id/:id')->to('User#query');
$r->get('/Rest/Identity/User')->to('User#query');
$r->put('/Rest/Identity/User')->to('User#update');
$r->post('/Rest/Identity/User')->to('User#create');
$r->delete('/Rest/Identity/User/name/:name')->to('User#delete');
$r->delete('/Rest/Identity/User/id/:id')->to('User#delete');
}
1;
Send GET request to
/Rest/Identity/User/name/*
to
Net::Cisco::ACS::Mock::Controller::User
Method: query
Argument ‘name’ retrievable through
param
lib/Net/Cisco/ACS/Mock/Schema.pm
package Net::Cisco::ACS::Mock::Schema;
# based on the DBIx::Class Schema base class
use base qw/DBIx::Class::Schema/;
# This will load any classes within
__PACKAGE__->load_namespaces();
1;
Generated by dbicdump
lib/Net/Cisco/ACS/Mock/Schema/Result/User.pm
package Net::Cisco::ACS::Mock::Schema::Result::User;
use base qw/DBIx::Class::Core/;
# Associated table in database
__PACKAGE__->table('users');
# Column definition
__PACKAGE__->add_columns(
id => { data_type => 'integer', is_auto_increment => 1 },
description => { data_type => 'text’ },
identitygroupname => { data_type => 'text‘ },
name => { data_type => 'text‘ },
changepassword => { data_type => 'text‘ },
enablepassword => { data_type => 'text‘ },
enabled => { data_type => 'integer’ },
lib/Net/Cisco/ACS/Mock/Schema/Result/User.pm
package Net::Cisco::ACS::Mock::Schema::Result::User;
use base qw/DBIx::Class::Core/;
# Associated table in database
__PACKAGE__->table('users');
# Column definition
__PACKAGE__->add_columns(
id => { data_type => 'integer', is_auto_increment => 1 },
description => { data_type => 'text’ },
identitygroupname => { data_type => 'text‘ },
name => { data_type => 'text‘ },
changepassword => { data_type => 'text‘ },
enablepassword => { data_type => 'text‘ },
enabled => { data_type => 'integer’ },
Generated by dbicdump
Result package
Connects to DB table Users
lib/Net/Cisco/ACS/Mock/Schema/Result/User.pm
..CONTINUED
password => { data_type => 'text‘ },
passwordneverexpires => { data_type => 'integer’ },
passwordtype => { data_type => 'text‘ },
dateexceeds => { data_type => 'text’ },
dateexceedsenabled => { data_type => 'integer’ },
);
# Tell DBIC that 'id' is the primary key
__PACKAGE__->set_primary_key('id');
1;
lib/Net/Cisco/ACS/Mock/Controller/User.pm
package Net::Cisco::ACS::Mock::Controller::User;
use Mojo::Base 'Mojolicious::Controller';
use XML::Simple;
sub query {
my $self = shift;
my $name = $self->param("name");
my $id = $self->param("id");
my $rs = $self->db->resultset('User');
my $user;
if ($name)
{ my $query_rs = $rs->search({ name => $name });
$user = $query_rs->first;
}
if ($id)
{ my $query_rs = $rs->search({ id => $id });
$user = $query_rs->first;
}
lib/Net/Cisco/ACS/Mock/Controller/User.pm
package Net::Cisco::ACS::Mock::Controller::User;
use Mojo::Base 'Mojolicious::Controller';
use XML::Simple;
sub query {
my $self = shift;
my $name = $self->param("name");
my $id = $self->param("id");
my $rs = $self->db->resultset('User');
my $user;
if ($name)
{ my $query_rs = $rs->search({ name => $name });
$user = $query_rs->first;
}
if ($id)
{ my $query_rs = $rs->search({ id => $id });
$user = $query_rs->first;
}
View Controller for /User
Method query maps to #query earlier
lib/Net/Cisco/ACS/Mock/Controller/User.pm
package Net::Cisco::ACS::Mock::Controller::User;
use Mojo::Base 'Mojolicious::Controller';
use XML::Simple;
sub query {
my $self = shift;
my $name = $self->param("name");
my $id = $self->param("id");
my $rs = $self->db->resultset('User');
my $user;
if ($name)
{ my $query_rs = $rs->search({ name => $name });
$user = $query_rs->first;
}
if ($id)
{ my $query_rs = $rs->search({ id => $id });
$user = $query_rs->first;
}
Map to :id and :name in URI
lib/Net/Cisco/ACS/Mock/Controller/User.pm
package Net::Cisco::ACS::Mock::Controller::User;
use Mojo::Base 'Mojolicious::Controller';
use XML::Simple;
sub query {
my $self = shift;
my $name = $self->param("name");
my $id = $self->param("id");
my $rs = $self->db->resultset('User');
my $user;
if ($name)
{ my $query_rs = $rs->search({ name => $name });
$user = $query_rs->first;
}
if ($id)
{ my $query_rs = $rs->search({ id => $id });
$user = $query_rs->first;
}
Load
Net::Cisco::ACS::Mock::Schema::Result::User
Query table with criteria
lib/Net/Cisco/ACS/Mock/Controller/User.pm
if (!$id && !$name)
{ my $query_rs = $rs->search;
my %users = ();
while (my $account = $query_rs->next)
{ $users{$account->name} =
{ # Set the record
};
}
}
$self->stash("users" => %users);
$self->render(template => 'user/queryall', format => 'xml', layout => 'userall',status => 200);
return;
}
$self->stash("user" => $user);
$self->render(template => 'user/query', format => 'xml', layout => 'user', status => 200);
}
lib/Net/Cisco/ACS/Mock/Controller/User.pm
if (!$id && !$name)
{ my $query_rs = $rs->search;
my %users = ();
while (my $account = $query_rs->next)
{ $users{$account->name} =
{ # Set the record
};
}
}
$self->stash("users" => %users);
$self->render(template => 'user/queryall', format => 'xml', layout => 'userall');
return;
}
$self->stash("user" => $user);
$self->render(template => 'user/query', format => 'xml', layout => 'user');
}
Stash maps variables to template variables
lib/Net/Cisco/ACS/Mock/Controller/User.pm
if (!$id && !$name)
{ my $query_rs = $rs->search;
my %users = ();
while (my $account = $query_rs->next)
{ $users{$account->name} =
{ # Set the record
};
}
}
$self->stash("users" => %users);
$self->render(template => 'user/queryall', format => 'xml', layout => 'userall', status => 200);
return;
}
$self->stash("user" => $user);
$self->render(template => 'user/query', format => 'xml', layout => 'user', status => 200);
}
Render processes templates using
template file,
format prepares HTTP header,
layout wraps around the template
status returns HTTP status 200 (OK)
lib/Net/Cisco/ACS/Mock/Controller/User.pm
..CONTINUED
sub update {
my $self = shift;
my $rs = $self->db->resultset('User');
my $data = $self->req->body;
my $xmlsimple = XML::Simple->new();
my $xmlout = $xmlsimple->XMLin($data);
my $query_rs = $rs->search({ name => $xmlout->{"name"} });
my $account = $query_rs->first;
$account->update({ # Set Record
});
$self->render(template => 'user/userresult',
format => 'xml', layout => 'userresult', status => 200);
}
lib/Net/Cisco/ACS/Mock/Controller/User.pm
..CONTINUED
sub create {
my $self = shift;
my $data = $self->req->body;
my $xmlsimple = XML::Simple->new();
my $xmlout = $xmlsimple->XMLin($data);
my $rsmax = $self->db->resultset('User')->get_column('Id');
my $maxid = $rsmax->max;
$maxid++;
$self->db->resultset('User')->create({
# Set record
id => $maxid
});
$self->render(template => 'user/userresult', format => 'xml', layout => 'userresult', status => 200);
}
lib/Net/Cisco/ACS/Mock/Controller/User.pm
..CONTINUED
sub delete {
my $self = shift;
my $rs = $self->db->resultset('User');
my $name = $self->param("name");
my $id = $self->param("id");
my $user;
if ($name)
{ my $query_rs = $rs->search({ name => $name });
$user = $query_rs->first;
}
if ($id)
{ my $query_rs = $rs->search({ id => $id });
$user = $query_rs->first;
}
$user->delete if $user;
$self->render(template => 'user/userresult', format => 'xml', layout => 'userresult', status =>
200);
}
1;
templates/user/queryall.xml.ep
% foreach my $user (sort keys %{$users}) {
<User>
<id><%= $users->{$user}->{id} %></id>
<description><%= $users->{$user}->{description} %></description>
<identityGroupName><%= $users->{$user}->{identitygroupname} %></identityGroupName>
<name><%= $users->{$user}->{name} %></name>
<changePassword><%= $users->{$user}->{changepassword} %></changePassword>
<enablePassword><%= $users->{$user}->{enablepassword} %></enablePassword>
<enabled><%= $users->{$user}->{enabled} %></enabled>
<password><%= $users->{$user}->{password} %></password>
<passwordNeverExpires><%= $users->{$user}->{passwordneverexpires}
%></passwordNeverExpires>
<passwordType><%= $users->{$user}->{passwordtype} %></passwordType>
<dateExceeds><%= $users->{$user}->{dateexceeds} %></dateExceeds>
<dateExceedsEnabled><%= $users->{$user}->{dateexceedsenabled} %></dateExceedsEnabled>
</User>
% }
templates/user/queryall.xml.ep
% foreach my $user (sort keys %{$users}) {
<User>
<id><%= $users->{$user}->{id} %></id>
<description><%= $users->{$user}->{description} %></description>
<identityGroupName><%= $users->{$user}->{identitygroupname} %></identityGroupName>
<name><%= $users->{$user}->{name} %></name>
<changePassword><%= $users->{$user}->{changepassword} %></changePassword>
<enablePassword><%= $users->{$user}->{enablepassword} %></enablePassword>
<enabled><%= $users->{$user}->{enabled} %></enabled>
<password><%= $users->{$user}->{password} %></password>
<passwordNeverExpires><%= $users->{$user}->{passwordneverexpires}
%></passwordNeverExpires>
<passwordType><%= $users->{$user}->{passwordtype} %></passwordType>
<dateExceeds><%= $users->{$user}->{dateexceeds} %></dateExceeds>
<dateExceedsEnabled><%= $users->{$user}->{dateexceedsenabled} %></dateExceedsEnabled>
</User>
% }
Template can contained embedded Perl,
in this case a for loop, generating elements
templates/layouts/userall.xml.ep
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:users
xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com">
<%= content %>
</ns2:users>
templates/layouts/userall.xml.ep
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:users
xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com">
<%= content %>
</ns2:users>
Layout wraps around template file.
Content is placed in <%= content %>
templates/user/query.xml.ep
<id><%= $user->id %></id>
<description><%= $user->description %></description>
<identityGroupName><%= $user->identitygroupname %></identityGroupName>
<name><%= $user->name %></name>
<changePassword><%= $user->changepassword %></changePassword>
<enablePassword><%= $user->enablepassword %></enablePassword>
<enabled><%= $user->enabled %></enabled>
<password><%= $user->password %></password>
<passwordNeverExpires><%= $user->passwordneverexpires %></passwordNeverExpires>
<passwordType><%= $user->passwordtype %></passwordType>
<dateExceeds><%= $user->dateexceeds %></dateExceeds>
<dateExceedsEnabled><%= $user->dateexceedsenabled %></dateExceedsEnabled>
templates/layouts/user.xml.ep
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:user
xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com">
<%= content %>
</ns2:user>
Morbo
 Development Server
 Single request
 Built using Mojolicious
 Supports all features

# morbo script/net_cisco_acsmock
Server available at http://127.0.0.1:3000
 Production Server
 Multiple requests, scalable
 Built using Mojolicious
 Supports all features

# hypnotoad script/net_cisco_acsmock
[Sun Dec 11 15:19:16 2016] [info] Listening at "http://*:8080"
Server available at http://127.0.0.1:8080
HypnoToad
The proof of the pudding..
#!/usr/bin/perl
use lib qw(Net/Cisco/ACS/lib);
use Net::Cisco::ACS;
use Data::Dumper;
my $acs = Net::Cisco::ACS->new(hostname => '127.0.0.1:8080', ssl=>0, username => 'acsadmin',
password => 'password');
print Dumper $acs->users;
Output
Net::Cisco::ACS:
$VAR1 = {
'Foo' => bless( {
'id' => '1',
'passwordType' => 'Internal',
'name' => 'Foo',
'enablePassword' => 'Secret Enable',
'passwordNeverExpires' => '1',
'password' => 'Secret',
'description' => 'Description for #1',
'changePassword' => 'Secret Change',
'identityGroupName' => 'All Groups',
'dateExceedsEnabled' => '1',
'enabled' => '0',
'dateExceeds' => '2020-01-01'
}, 'Net::Cisco::ACS::User' ),
'Bar' => bless( {
'dateExceedsEnabled' => '1',
'enabled' => '0',
'dateExceeds' => '2020-01-01',
'description' => 'Description for #2',
'passwordNeverExpires' => '1',
'password' => 'Secret',
'identityGroupName' => 'All Groups',
'changePassword' => 'Secret Change',
'enablePassword' => 'Secret Enable',
'name' => 'Bar',
'passwordType' => 'Internal',
'id' => '2'
}, 'Net::Cisco::ACS::User' )
};
Output
http://localhost:3000/Rest/Identity/User/name/Foo
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<ns2:user xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com">
<id>1</id>
<description>Description for #1</description>
<identityGroupName>All Groups</identityGroupName>
<name>Foo</name>
<changePassword>Secret Change</changePassword>
<enablePassword>Secret Enable</enablePassword>
<enabled>1</enabled>
<password>Secret</password>
<passwordNeverExpires>1</passwordNeverExpires>
<passwordType>Internal</passwordType>
<dateExceeds>2020-01-01</dateExceeds>
<dateExceedsEnabled>1</dateExceedsEnabled>
</ns2:user>

More Related Content

What's hot

Dance for the puppet master: G6 Tech Talk
Dance for the puppet master: G6 Tech TalkDance for the puppet master: G6 Tech Talk
Dance for the puppet master: G6 Tech Talk
Michael Peacock
 
Consuming RESTful services in PHP
Consuming RESTful services in PHPConsuming RESTful services in PHP
Consuming RESTful services in PHP
Zoran Jeremic
 

What's hot (20)

HTML5 JavaScript APIs
HTML5 JavaScript APIsHTML5 JavaScript APIs
HTML5 JavaScript APIs
 
PowerShell Technical Overview
PowerShell Technical OverviewPowerShell Technical Overview
PowerShell Technical Overview
 
Multi Tenancy With Python and Django
Multi Tenancy With Python and DjangoMulti Tenancy With Python and Django
Multi Tenancy With Python and Django
 
Resource registries plone conf 2014
Resource registries plone conf 2014Resource registries plone conf 2014
Resource registries plone conf 2014
 
WebSockets wiith Scala and Play! Framework
WebSockets wiith Scala and Play! FrameworkWebSockets wiith Scala and Play! Framework
WebSockets wiith Scala and Play! Framework
 
Intro To Couch Db
Intro To Couch DbIntro To Couch Db
Intro To Couch Db
 
Windows PowerShell
Windows PowerShellWindows PowerShell
Windows PowerShell
 
Salesforce CLI Cheat Sheet
Salesforce CLI Cheat Sheet Salesforce CLI Cheat Sheet
Salesforce CLI Cheat Sheet
 
Java Play RESTful ebean
Java Play RESTful ebeanJava Play RESTful ebean
Java Play RESTful ebean
 
Java Play Restful JPA
Java Play Restful JPAJava Play Restful JPA
Java Play Restful JPA
 
Introduction to PowerShell
Introduction to PowerShellIntroduction to PowerShell
Introduction to PowerShell
 
Introduction to Flask Micro Framework
Introduction to Flask Micro FrameworkIntroduction to Flask Micro Framework
Introduction to Flask Micro Framework
 
When dynamic becomes static : the next step in web caching techniques
When dynamic becomes static : the next step in web caching techniquesWhen dynamic becomes static : the next step in web caching techniques
When dynamic becomes static : the next step in web caching techniques
 
SenchaCon 2016: Modernizing the Ext JS Class System - Don Griffin
SenchaCon 2016: Modernizing the Ext JS Class System - Don GriffinSenchaCon 2016: Modernizing the Ext JS Class System - Don Griffin
SenchaCon 2016: Modernizing the Ext JS Class System - Don Griffin
 
Node.js in action
Node.js in actionNode.js in action
Node.js in action
 
Developing node-mdb: a Node.js - based clone of SimpleDB
Developing node-mdb: a Node.js - based clone of SimpleDBDeveloping node-mdb: a Node.js - based clone of SimpleDB
Developing node-mdb: a Node.js - based clone of SimpleDB
 
Powershell Demo Presentation
Powershell Demo PresentationPowershell Demo Presentation
Powershell Demo Presentation
 
Dance for the puppet master: G6 Tech Talk
Dance for the puppet master: G6 Tech TalkDance for the puppet master: G6 Tech Talk
Dance for the puppet master: G6 Tech Talk
 
Consuming RESTful services in PHP
Consuming RESTful services in PHPConsuming RESTful services in PHP
Consuming RESTful services in PHP
 
Understanding the Node.js Platform
Understanding the Node.js PlatformUnderstanding the Node.js Platform
Understanding the Node.js Platform
 

Viewers also liked

Métodos directos para la solución de sistemas de ecuaciones lineales
Métodos directos para la solución de sistemas de ecuaciones linealesMétodos directos para la solución de sistemas de ecuaciones lineales
Métodos directos para la solución de sistemas de ecuaciones lineales
Mileacre
 
vivian's resume
vivian's resumevivian's resume
vivian's resume
vivian ng
 
RESULTADOS PRIMER TORNEO
RESULTADOS PRIMER TORNEORESULTADOS PRIMER TORNEO
RESULTADOS PRIMER TORNEO
anatael74
 

Viewers also liked (14)

Andres gomez actividad1_2mapac
Andres gomez actividad1_2mapacAndres gomez actividad1_2mapac
Andres gomez actividad1_2mapac
 
Ejercicio 9 de word
Ejercicio 9 de wordEjercicio 9 de word
Ejercicio 9 de word
 
5152522_28566
5152522_285665152522_28566
5152522_28566
 
Métodos directos para la solución de sistemas de ecuaciones lineales
Métodos directos para la solución de sistemas de ecuaciones linealesMétodos directos para la solución de sistemas de ecuaciones lineales
Métodos directos para la solución de sistemas de ecuaciones lineales
 
vivian's resume
vivian's resumevivian's resume
vivian's resume
 
Ogräs och ogräsbekämpning, avsnitt 4 by Sarbast wali
Ogräs och ogräsbekämpning, avsnitt 4 by Sarbast waliOgräs och ogräsbekämpning, avsnitt 4 by Sarbast wali
Ogräs och ogräsbekämpning, avsnitt 4 by Sarbast wali
 
RESULTADOS PRIMER TORNEO
RESULTADOS PRIMER TORNEORESULTADOS PRIMER TORNEO
RESULTADOS PRIMER TORNEO
 
Thyroid Function
Thyroid FunctionThyroid Function
Thyroid Function
 
Dossier de veille 104
Dossier de veille 104Dossier de veille 104
Dossier de veille 104
 
Dv 111-affiche
Dv 111-afficheDv 111-affiche
Dv 111-affiche
 
Harry stack sullivan descripción
Harry stack sullivan descripciónHarry stack sullivan descripción
Harry stack sullivan descripción
 
osvehicle-connected-20160429
osvehicle-connected-20160429osvehicle-connected-20160429
osvehicle-connected-20160429
 
Los planetas interiores rodrigo
Los planetas interiores rodrigoLos planetas interiores rodrigo
Los planetas interiores rodrigo
 
Даниленко Маргатита (География 9 класс)
Даниленко Маргатита (География 9 класс)Даниленко Маргатита (География 9 класс)
Даниленко Маргатита (География 9 класс)
 

Similar to Mojo – Simple REST Server

Install ldap server
Install ldap serverInstall ldap server
Install ldap server
Mawardi 12
 
Install ldap server
Install ldap serverInstall ldap server
Install ldap server
Mawardi 12
 
Ajax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesAjax Performance Tuning and Best Practices
Ajax Performance Tuning and Best Practices
Doris Chen
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 

Similar to Mojo – Simple REST Server (20)

Php summary
Php summaryPhp summary
Php summary
 
Install ldap server
Install ldap serverInstall ldap server
Install ldap server
 
Install ldap server
Install ldap serverInstall ldap server
Install ldap server
 
PHP and MySQL
PHP and MySQLPHP and MySQL
PHP and MySQL
 
DBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たちDBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たち
 
Create a web-app with Cgi Appplication
Create a web-app with Cgi AppplicationCreate a web-app with Cgi Appplication
Create a web-app with Cgi Appplication
 
Service discovery and configuration provisioning
Service discovery and configuration provisioningService discovery and configuration provisioning
Service discovery and configuration provisioning
 
Redis for your boss
Redis for your bossRedis for your boss
Redis for your boss
 
Managing Your Security Logs with Elasticsearch
Managing Your Security Logs with ElasticsearchManaging Your Security Logs with Elasticsearch
Managing Your Security Logs with Elasticsearch
 
Build restful ap is with python and flask
Build restful ap is with python and flaskBuild restful ap is with python and flask
Build restful ap is with python and flask
 
Sah
SahSah
Sah
 
General Principles of Web Security
General Principles of Web SecurityGeneral Principles of Web Security
General Principles of Web Security
 
UEMB200: Next Generation of Endpoint Management Architecture and Discovery Se...
UEMB200: Next Generation of Endpoint Management Architecture and Discovery Se...UEMB200: Next Generation of Endpoint Management Architecture and Discovery Se...
UEMB200: Next Generation of Endpoint Management Architecture and Discovery Se...
 
Ajax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesAjax Performance Tuning and Best Practices
Ajax Performance Tuning and Best Practices
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
Performance Optimization and JavaScript Best Practices
Performance Optimization and JavaScript Best PracticesPerformance Optimization and JavaScript Best Practices
Performance Optimization and JavaScript Best Practices
 
Php frameworks
Php frameworksPhp frameworks
Php frameworks
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
REST with Eve and Python
REST with Eve and PythonREST with Eve and Python
REST with Eve and Python
 

More from hendrikvb (6)

China.z / Trojan.XorDDOS - Analysis of a hack
China.z / Trojan.XorDDOS - Analysis of a hackChina.z / Trojan.XorDDOS - Analysis of a hack
China.z / Trojan.XorDDOS - Analysis of a hack
 
Source Filters in Perl 2010
Source Filters in Perl 2010Source Filters in Perl 2010
Source Filters in Perl 2010
 
Scrabbling Code - Beatnik - YAPC::Eu::2003
Scrabbling Code - Beatnik - YAPC::Eu::2003Scrabbling Code - Beatnik - YAPC::Eu::2003
Scrabbling Code - Beatnik - YAPC::Eu::2003
 
Json In 5 Slices.Key
Json In 5 Slices.KeyJson In 5 Slices.Key
Json In 5 Slices.Key
 
Cleancode
CleancodeCleancode
Cleancode
 
Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101
 

Recently uploaded

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Recently uploaded (20)

ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 

Mojo – Simple REST Server

  • 1. Mojo – TL;DR Simple REST Server Hendrik Van Belleghem Hendrik.vanbelleghem@gmail.com 2016 Perl Mongers Vlaanderen Meetup
  • 2. Intro • REST is typically HTTP based • Not standardized • Popularly used for APIs • Outputs XML, HTML, JSON • Verbs include GET, POST, DELETE & PUT • • https://en.wikipedia.org/wiki/Representational_state_transfer
  • 3. Intro - REST  REST typically uses standard HTTP calls:   Specific entry: GET /User/name/<USER>  Specific entry: GET /User/id/<ID>  All Entries: GET /User  Update entry: PUT /User  Create entry: POST /User  Delete entry: DELETE /User/name/<USER>  Delete entry: DELETE /User/id/<ID> 
  • 4. Demo • Cisco Secure ACS Server is used for Identity Management • API access to user accounts, devices.. • Mojolicious does REST just fine • Non-plugin approach • Proof of Concept / Users only
  • 5. Mojo • Next generation Web: Websockets, non-blocking, HTTP/1.1 • Web in a box: Development server & production server included • Light-weight: No dependencies • Flexible routing • … just like Catalyst • DBIX::Class plugs in easily • … just like Catalyst • Done by the original Catalyst people See http://www.mojolicious.org/
  • 6. Mojo Generate application skeleton: # mojo generate app Net::Cisco::ACS::Mock Output [mkdir] /home/hendrik/net_cisco_acsmock/script [write] /home/hendrik/net_cisco_acsmock/script/net_cisco_acsmock [chmod] /home/hendrik/net_cisco_acsmock/script/net_cisco_acsmock 744 [mkdir] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS [write] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS/Mock.pm [mkdir] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS/Mock/Controller [write] /home/hendrik/net_cisco_acsmock/lib/Net/Cisco/ACS/Mock/Controller/Example.pm [mkdir] /home/hendrik/net_cisco_acsmock/t [write] /home/hendrik/net_cisco_acsmock/t/basic.t [mkdir] /home/hendrik/net_cisco_acsmock/public [write] /home/hendrik/net_cisco_acsmock/public/index.html [mkdir] /home/hendrik/net_cisco_acsmock/templates/layouts [write] /home/hendrik/net_cisco_acsmock/templates/layouts/default.html.ep [mkdir] /home/hendrik/net_cisco_acsmock/templates/example [write] /home/hendrik/net_cisco_acsmock/templates/example/welcome.html.ep
  • 7. Create SQLite DB # sqlite3 acs.db sqlite > create table users (id integer, description text, name text, identitygroupname text, changepassword text, enablepassword text, enabled integer, password text, passwordneverexpires integer, passwordtype text, dateexceeds text, dateexceedsenabled integer ); sqlite > insert into users (id, description, name, identitygroupname, changepassword, enablepassword, enabled, password, passwordneverexpires, passwordtype, dateexceeds, dateexceedsenabled) values(1, 'Description for #1', 'Foo', 'All Groups', 'Secret Change','Secret Enable',1,'Secret',1,'Internal','2020-01-01',1); sqlite > insert into users (id, description, name, identitygroupname, changepassword, enablepassword, enabled, password, passwordneverexpires, passwordtype, dateexceeds, dateexceedsenabled) values(2, 'Description for #2', 'Bar', 'All Groups', 'Secret Change','Secret Enable',1,'Secret',1,'Internal','2020-01-01',1); sqlite > .quit
  • 8. Generate DBIx::Class Schema Generate DBIX::Class classes automatically based on SQLite database (or any other database) # dbicdump -o dump_directory=./lib -o components='["InflateColumn::DateTime"]' -o debug=1 Net::Cisco::ACS::Mock::Schema dbi:SQLite:acs.db Run this as much as needed.. but it will overwrite the files! Suggestion: Make sure to prepare your foreign keys properly!
  • 9. lib/Net/Cisco/ACS/Mock.pm package Net::Cisco::ACS::Mock; use Mojo::Base 'Mojolicious'; use Net::Cisco::ACS::Mock::Schema; sub startup { my $self = shift; my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db'); $self->helper(db => sub { return $schema; }); my $r = $self->routes; $r->get("/Rest/Identity/User/name/:name")->to('User#query'); $r->get('/Rest/Identity/User/id/:id')->to('User#query'); $r->get('/Rest/Identity/User')->to('User#query'); $r->put('/Rest/Identity/User')->to('User#update'); $r->post('/Rest/Identity/User')->to('User#create'); $r->delete('/Rest/Identity/User/name/:name')->to('User#delete'); $r->delete('/Rest/Identity/User/id/:id')->to('User#delete'); } 1;
  • 10. lib/Net/Cisco/ACS/Mock.pm package Net::Cisco::ACS::Mock; use Mojo::Base 'Mojolicious'; use Net::Cisco::ACS::Mock::Schema; sub startup { my $self = shift; my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db'); $self->helper(db => sub { return $schema; }); my $r = $self->routes; $r->get("/Rest/Identity/User/name/:name")->to('User#query'); $r->get('/Rest/Identity/User/id/:id')->to('User#query'); $r->get('/Rest/Identity/User')->to('User#query'); $r->put('/Rest/Identity/User')->to('User#update'); $r->post('/Rest/Identity/User')->to('User#create'); $r->delete('/Rest/Identity/User/name/:name')->to('User#delete'); $r->delete('/Rest/Identity/User/id/:id')->to('User#delete'); } 1; Startup is called on Mojo startup Store DBIx::Class connection in $self->db
  • 11. lib/Net/Cisco/ACS/Mock.pm package Net::Cisco::ACS::Mock; use Mojo::Base 'Mojolicious'; use Net::Cisco::ACS::Mock::Schema; sub startup { my $self = shift; my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db'); $self->helper(db => sub { return $schema; }); my $r = $self->routes; $r->get("/Rest/Identity/User/name/:name")->to('User#query'); $r->get('/Rest/Identity/User/id/:id')->to('User#query'); $r->get('/Rest/Identity/User')->to('User#query'); $r->put('/Rest/Identity/User')->to('User#update'); $r->post('/Rest/Identity/User')->to('User#create'); $r->delete('/Rest/Identity/User/name/:name')->to('User#delete'); $r->delete('/Rest/Identity/User/id/:id')->to('User#delete'); } 1;
  • 12. lib/Net/Cisco/ACS/Mock.pm package Net::Cisco::ACS::Mock; use Mojo::Base 'Mojolicious'; use Net::Cisco::ACS::Mock::Schema; sub startup { my $self = shift; my $schema = Net::Cisco::ACS::Mock::Schema->connect('dbi:SQLite:acs.db'); $self->helper(db => sub { return $schema; }); my $r = $self->routes; $r->get("/Rest/Identity/User/name/:name")->to('User#query'); $r->get('/Rest/Identity/User/id/:id')->to('User#query'); $r->get('/Rest/Identity/User')->to('User#query'); $r->put('/Rest/Identity/User')->to('User#update'); $r->post('/Rest/Identity/User')->to('User#create'); $r->delete('/Rest/Identity/User/name/:name')->to('User#delete'); $r->delete('/Rest/Identity/User/id/:id')->to('User#delete'); } 1; Send GET request to /Rest/Identity/User/name/* to Net::Cisco::ACS::Mock::Controller::User Method: query Argument ‘name’ retrievable through param
  • 13. lib/Net/Cisco/ACS/Mock/Schema.pm package Net::Cisco::ACS::Mock::Schema; # based on the DBIx::Class Schema base class use base qw/DBIx::Class::Schema/; # This will load any classes within __PACKAGE__->load_namespaces(); 1; Generated by dbicdump
  • 14. lib/Net/Cisco/ACS/Mock/Schema/Result/User.pm package Net::Cisco::ACS::Mock::Schema::Result::User; use base qw/DBIx::Class::Core/; # Associated table in database __PACKAGE__->table('users'); # Column definition __PACKAGE__->add_columns( id => { data_type => 'integer', is_auto_increment => 1 }, description => { data_type => 'text’ }, identitygroupname => { data_type => 'text‘ }, name => { data_type => 'text‘ }, changepassword => { data_type => 'text‘ }, enablepassword => { data_type => 'text‘ }, enabled => { data_type => 'integer’ },
  • 15. lib/Net/Cisco/ACS/Mock/Schema/Result/User.pm package Net::Cisco::ACS::Mock::Schema::Result::User; use base qw/DBIx::Class::Core/; # Associated table in database __PACKAGE__->table('users'); # Column definition __PACKAGE__->add_columns( id => { data_type => 'integer', is_auto_increment => 1 }, description => { data_type => 'text’ }, identitygroupname => { data_type => 'text‘ }, name => { data_type => 'text‘ }, changepassword => { data_type => 'text‘ }, enablepassword => { data_type => 'text‘ }, enabled => { data_type => 'integer’ }, Generated by dbicdump Result package Connects to DB table Users
  • 16. lib/Net/Cisco/ACS/Mock/Schema/Result/User.pm ..CONTINUED password => { data_type => 'text‘ }, passwordneverexpires => { data_type => 'integer’ }, passwordtype => { data_type => 'text‘ }, dateexceeds => { data_type => 'text’ }, dateexceedsenabled => { data_type => 'integer’ }, ); # Tell DBIC that 'id' is the primary key __PACKAGE__->set_primary_key('id'); 1;
  • 17. lib/Net/Cisco/ACS/Mock/Controller/User.pm package Net::Cisco::ACS::Mock::Controller::User; use Mojo::Base 'Mojolicious::Controller'; use XML::Simple; sub query { my $self = shift; my $name = $self->param("name"); my $id = $self->param("id"); my $rs = $self->db->resultset('User'); my $user; if ($name) { my $query_rs = $rs->search({ name => $name }); $user = $query_rs->first; } if ($id) { my $query_rs = $rs->search({ id => $id }); $user = $query_rs->first; }
  • 18. lib/Net/Cisco/ACS/Mock/Controller/User.pm package Net::Cisco::ACS::Mock::Controller::User; use Mojo::Base 'Mojolicious::Controller'; use XML::Simple; sub query { my $self = shift; my $name = $self->param("name"); my $id = $self->param("id"); my $rs = $self->db->resultset('User'); my $user; if ($name) { my $query_rs = $rs->search({ name => $name }); $user = $query_rs->first; } if ($id) { my $query_rs = $rs->search({ id => $id }); $user = $query_rs->first; } View Controller for /User Method query maps to #query earlier
  • 19. lib/Net/Cisco/ACS/Mock/Controller/User.pm package Net::Cisco::ACS::Mock::Controller::User; use Mojo::Base 'Mojolicious::Controller'; use XML::Simple; sub query { my $self = shift; my $name = $self->param("name"); my $id = $self->param("id"); my $rs = $self->db->resultset('User'); my $user; if ($name) { my $query_rs = $rs->search({ name => $name }); $user = $query_rs->first; } if ($id) { my $query_rs = $rs->search({ id => $id }); $user = $query_rs->first; } Map to :id and :name in URI
  • 20. lib/Net/Cisco/ACS/Mock/Controller/User.pm package Net::Cisco::ACS::Mock::Controller::User; use Mojo::Base 'Mojolicious::Controller'; use XML::Simple; sub query { my $self = shift; my $name = $self->param("name"); my $id = $self->param("id"); my $rs = $self->db->resultset('User'); my $user; if ($name) { my $query_rs = $rs->search({ name => $name }); $user = $query_rs->first; } if ($id) { my $query_rs = $rs->search({ id => $id }); $user = $query_rs->first; } Load Net::Cisco::ACS::Mock::Schema::Result::User Query table with criteria
  • 21. lib/Net/Cisco/ACS/Mock/Controller/User.pm if (!$id && !$name) { my $query_rs = $rs->search; my %users = (); while (my $account = $query_rs->next) { $users{$account->name} = { # Set the record }; } } $self->stash("users" => %users); $self->render(template => 'user/queryall', format => 'xml', layout => 'userall',status => 200); return; } $self->stash("user" => $user); $self->render(template => 'user/query', format => 'xml', layout => 'user', status => 200); }
  • 22. lib/Net/Cisco/ACS/Mock/Controller/User.pm if (!$id && !$name) { my $query_rs = $rs->search; my %users = (); while (my $account = $query_rs->next) { $users{$account->name} = { # Set the record }; } } $self->stash("users" => %users); $self->render(template => 'user/queryall', format => 'xml', layout => 'userall'); return; } $self->stash("user" => $user); $self->render(template => 'user/query', format => 'xml', layout => 'user'); } Stash maps variables to template variables
  • 23. lib/Net/Cisco/ACS/Mock/Controller/User.pm if (!$id && !$name) { my $query_rs = $rs->search; my %users = (); while (my $account = $query_rs->next) { $users{$account->name} = { # Set the record }; } } $self->stash("users" => %users); $self->render(template => 'user/queryall', format => 'xml', layout => 'userall', status => 200); return; } $self->stash("user" => $user); $self->render(template => 'user/query', format => 'xml', layout => 'user', status => 200); } Render processes templates using template file, format prepares HTTP header, layout wraps around the template status returns HTTP status 200 (OK)
  • 24. lib/Net/Cisco/ACS/Mock/Controller/User.pm ..CONTINUED sub update { my $self = shift; my $rs = $self->db->resultset('User'); my $data = $self->req->body; my $xmlsimple = XML::Simple->new(); my $xmlout = $xmlsimple->XMLin($data); my $query_rs = $rs->search({ name => $xmlout->{"name"} }); my $account = $query_rs->first; $account->update({ # Set Record }); $self->render(template => 'user/userresult', format => 'xml', layout => 'userresult', status => 200); }
  • 25. lib/Net/Cisco/ACS/Mock/Controller/User.pm ..CONTINUED sub create { my $self = shift; my $data = $self->req->body; my $xmlsimple = XML::Simple->new(); my $xmlout = $xmlsimple->XMLin($data); my $rsmax = $self->db->resultset('User')->get_column('Id'); my $maxid = $rsmax->max; $maxid++; $self->db->resultset('User')->create({ # Set record id => $maxid }); $self->render(template => 'user/userresult', format => 'xml', layout => 'userresult', status => 200); }
  • 26. lib/Net/Cisco/ACS/Mock/Controller/User.pm ..CONTINUED sub delete { my $self = shift; my $rs = $self->db->resultset('User'); my $name = $self->param("name"); my $id = $self->param("id"); my $user; if ($name) { my $query_rs = $rs->search({ name => $name }); $user = $query_rs->first; } if ($id) { my $query_rs = $rs->search({ id => $id }); $user = $query_rs->first; } $user->delete if $user; $self->render(template => 'user/userresult', format => 'xml', layout => 'userresult', status => 200); } 1;
  • 27. templates/user/queryall.xml.ep % foreach my $user (sort keys %{$users}) { <User> <id><%= $users->{$user}->{id} %></id> <description><%= $users->{$user}->{description} %></description> <identityGroupName><%= $users->{$user}->{identitygroupname} %></identityGroupName> <name><%= $users->{$user}->{name} %></name> <changePassword><%= $users->{$user}->{changepassword} %></changePassword> <enablePassword><%= $users->{$user}->{enablepassword} %></enablePassword> <enabled><%= $users->{$user}->{enabled} %></enabled> <password><%= $users->{$user}->{password} %></password> <passwordNeverExpires><%= $users->{$user}->{passwordneverexpires} %></passwordNeverExpires> <passwordType><%= $users->{$user}->{passwordtype} %></passwordType> <dateExceeds><%= $users->{$user}->{dateexceeds} %></dateExceeds> <dateExceedsEnabled><%= $users->{$user}->{dateexceedsenabled} %></dateExceedsEnabled> </User> % }
  • 28. templates/user/queryall.xml.ep % foreach my $user (sort keys %{$users}) { <User> <id><%= $users->{$user}->{id} %></id> <description><%= $users->{$user}->{description} %></description> <identityGroupName><%= $users->{$user}->{identitygroupname} %></identityGroupName> <name><%= $users->{$user}->{name} %></name> <changePassword><%= $users->{$user}->{changepassword} %></changePassword> <enablePassword><%= $users->{$user}->{enablepassword} %></enablePassword> <enabled><%= $users->{$user}->{enabled} %></enabled> <password><%= $users->{$user}->{password} %></password> <passwordNeverExpires><%= $users->{$user}->{passwordneverexpires} %></passwordNeverExpires> <passwordType><%= $users->{$user}->{passwordtype} %></passwordType> <dateExceeds><%= $users->{$user}->{dateexceeds} %></dateExceeds> <dateExceedsEnabled><%= $users->{$user}->{dateexceedsenabled} %></dateExceedsEnabled> </User> % } Template can contained embedded Perl, in this case a for loop, generating elements
  • 29. templates/layouts/userall.xml.ep <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:users xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com"> <%= content %> </ns2:users>
  • 30. templates/layouts/userall.xml.ep <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:users xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com"> <%= content %> </ns2:users> Layout wraps around template file. Content is placed in <%= content %>
  • 31. templates/user/query.xml.ep <id><%= $user->id %></id> <description><%= $user->description %></description> <identityGroupName><%= $user->identitygroupname %></identityGroupName> <name><%= $user->name %></name> <changePassword><%= $user->changepassword %></changePassword> <enablePassword><%= $user->enablepassword %></enablePassword> <enabled><%= $user->enabled %></enabled> <password><%= $user->password %></password> <passwordNeverExpires><%= $user->passwordneverexpires %></passwordNeverExpires> <passwordType><%= $user->passwordtype %></passwordType> <dateExceeds><%= $user->dateexceeds %></dateExceeds> <dateExceedsEnabled><%= $user->dateexceedsenabled %></dateExceedsEnabled>
  • 32. templates/layouts/user.xml.ep <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:user xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com"> <%= content %> </ns2:user>
  • 33. Morbo  Development Server  Single request  Built using Mojolicious  Supports all features  # morbo script/net_cisco_acsmock Server available at http://127.0.0.1:3000
  • 34.  Production Server  Multiple requests, scalable  Built using Mojolicious  Supports all features  # hypnotoad script/net_cisco_acsmock [Sun Dec 11 15:19:16 2016] [info] Listening at "http://*:8080" Server available at http://127.0.0.1:8080 HypnoToad
  • 35. The proof of the pudding.. #!/usr/bin/perl use lib qw(Net/Cisco/ACS/lib); use Net::Cisco::ACS; use Data::Dumper; my $acs = Net::Cisco::ACS->new(hostname => '127.0.0.1:8080', ssl=>0, username => 'acsadmin', password => 'password'); print Dumper $acs->users;
  • 36. Output Net::Cisco::ACS: $VAR1 = { 'Foo' => bless( { 'id' => '1', 'passwordType' => 'Internal', 'name' => 'Foo', 'enablePassword' => 'Secret Enable', 'passwordNeverExpires' => '1', 'password' => 'Secret', 'description' => 'Description for #1', 'changePassword' => 'Secret Change', 'identityGroupName' => 'All Groups', 'dateExceedsEnabled' => '1', 'enabled' => '0', 'dateExceeds' => '2020-01-01' }, 'Net::Cisco::ACS::User' ), 'Bar' => bless( { 'dateExceedsEnabled' => '1', 'enabled' => '0', 'dateExceeds' => '2020-01-01', 'description' => 'Description for #2', 'passwordNeverExpires' => '1', 'password' => 'Secret', 'identityGroupName' => 'All Groups', 'changePassword' => 'Secret Change', 'enablePassword' => 'Secret Enable', 'name' => 'Bar', 'passwordType' => 'Internal', 'id' => '2' }, 'Net::Cisco::ACS::User' ) };
  • 37. Output http://localhost:3000/Rest/Identity/User/name/Foo <?xml version="1.0" encoding="UTF-8" standalone="true"?> <ns2:user xmlns:ns2="identity.rest.mgmt.acs.nm.cisco.com"> <id>1</id> <description>Description for #1</description> <identityGroupName>All Groups</identityGroupName> <name>Foo</name> <changePassword>Secret Change</changePassword> <enablePassword>Secret Enable</enablePassword> <enabled>1</enabled> <password>Secret</password> <passwordNeverExpires>1</passwordNeverExpires> <passwordType>Internal</passwordType> <dateExceeds>2020-01-01</dateExceeds> <dateExceedsEnabled>1</dateExceedsEnabled> </ns2:user>