MT#3925 API: dynamically render API documentation.

agranig/pbxapi
Andreas Granig 11 years ago
parent 9587330201
commit c799df3ebd

@ -8,7 +8,6 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Form::BillingFee qw();
use Path::Tiny qw(path);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
@ -16,6 +15,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Specifies the fees to be applied for a call if it matches the source or destination number of the call.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::BillingFees';
@ -76,7 +82,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::BillingFee->new;
my $form = $self->get_form($c);
for my $fee ($fees->all) {
push @embedded, $self->hal_from_fee($c, $fee, $form);
push @links, Data::HAL::Link->new(
@ -156,7 +162,7 @@ sub POST :Allow {
$reseller_id = $c->user->contract->contact->reseller_id;
}
my $form = NGCP::Panel::Form::BillingFee->new;
my $form = $self->get_form($c);
my $billing_profile_id = $resource->{billing_profile_id} // undef;
my $profile;

@ -4,7 +4,6 @@ use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::BillingFee qw();
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
@ -105,7 +104,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::BillingFee->new;
my $form = $self->get_form($c);
$fee = $self->update_fee($c, $fee, $old_resource, $resource, $form);
last unless $fee;
@ -145,7 +144,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $fee->get_inflated_columns };
my $form = NGCP::Panel::Form::BillingFee->new;
my $form = $self->get_form($c);
$fee = $self->update_fee($c, $fee, $old_resource, $resource, $form);
last unless $fee;

@ -8,7 +8,6 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Form::BillingProfile::Admin qw();
use Path::Tiny qw(path);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
@ -16,6 +15,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines a collection of <a href="#billingfees">Billing Fees</a> and <a href="#billingzones">Billing Zones</a> and can be assigned to <a href="#customers">Customers</a> and <a href="#contracts">System Contracts</a>.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::BillingProfiles';
@ -62,7 +68,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::BillingProfile::Admin->new;
my $form = $self->get_form($c);
for my $profile ($profiles->all) {
push @embedded, $self->hal_from_profile($c, $profile, $form);
push @links, Data::HAL::Link->new(
@ -141,7 +147,7 @@ sub POST :Allow {
$resource->{reseller_id} = $c->user->contract->contact->reseller_id;
}
my $form = NGCP::Panel::Form::BillingProfile::Admin->new;
my $form = $self->get_form($c);
$resource->{reseller_id} //= undef;
last unless $self->validate_form(
c => $c,

@ -4,7 +4,6 @@ use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::BillingProfile::Admin qw();
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
@ -105,7 +104,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::BillingProfile::Admin->new;
my $form = $self->get_form($c);
$profile = $self->update_profile($c, $profile, $old_resource, $resource, $form);
last unless $profile;
@ -145,7 +144,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $profile->get_inflated_columns };
my $form = NGCP::Panel::Form::BillingProfile::Admin->new;
my $form = $self->get_form($c);
use Data::Printer; p $profile;
$profile = $self->update_profile($c, $profile, $old_resource, $resource, $form);
use Data::Printer; p $profile;

@ -8,7 +8,6 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Form::BillingZone qw();
use Path::Tiny qw(path);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
@ -16,6 +15,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines zones used to group destinations within <a href="#billingprofiles">Billing Profiles</a>. The zones can be used to group customer\'s calls, like calls within his country or any calls to mobile numbers.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::BillingZones';
@ -76,7 +82,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::BillingZone->new;
my $form = $self->get_form($c);
for my $zone ($zones->all) {
push @embedded, $self->hal_from_zone($c, $zone, $form);
push @links, Data::HAL::Link->new(
@ -156,7 +162,7 @@ sub POST :Allow {
$reseller_id = $c->user->contract->contact->reseller_id;
}
my $form = NGCP::Panel::Form::BillingZone->new;
my $form = $self->get_form($c);
my $billing_profile_id = $resource->{billing_profile_id} // undef;
last unless $self->validate_form(
c => $c,

@ -4,7 +4,6 @@ use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::BillingZone qw();
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
@ -105,7 +104,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::BillingZone->new;
my $form = $self->get_form($c);
$zone = $self->update_zone($c, $zone, $old_resource, $resource, $form);
last unless $zone;
@ -145,7 +144,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $zone->get_inflated_columns };
my $form = NGCP::Panel::Form::BillingZone->new;
my $form = $self->get_form($c);
$zone = $self->update_zone($c, $zone, $old_resource, $resource, $form);
last unless $zone;

@ -9,7 +9,6 @@ use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Form::Contract::PeeringReseller qw();
use Path::Tiny qw(path);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
@ -17,6 +16,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines a billing container for peerings and resellers. A <a href="#billingprofiles">Billing Profile</a> is assigned to a contract, and it has <a href="#contractbalances">Contract Balances</a> indicating the saldo of the contract for current and past billing intervals.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::Contracts';
@ -66,7 +72,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::Contract::PeeringReseller->new;
my $form = $self->get_form($c);
for my $contract ($contracts->all) {
push @embedded, $self->hal_from_contract($c, $contract, $form);
push @links, Data::HAL::Link->new(
@ -152,7 +158,7 @@ sub POST :Allow {
$resource->{contact_id} //= undef;
my $form = NGCP::Panel::Form::Contract::PeeringReseller->new;
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,

@ -7,7 +7,6 @@ use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Contract::PeeringReseller qw();
use NGCP::Panel::Utils::ValidateJSON qw();
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
@ -109,7 +108,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::Contract::PeeringReseller->new;
my $form = $self->get_form($c);
$contract = $self->update_contract($c, $contract, $old_resource, $resource, $form);
last unless $contract;
@ -149,7 +148,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $contract->get_inflated_columns };
my $form = NGCP::Panel::Form::Contract::PeeringReseller->new;
my $form = $self->get_form($c);
$contract = $self->update_contract($c, $contract, $old_resource, $resource, $form);
last unless $contract;

@ -7,7 +7,6 @@ use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Contact::Admin qw();
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
@ -17,6 +16,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines a physical or legal person\'s address (postal and/or email) to be used to identify <a href="#customers">Customers</a>.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::CustomerContacts';
@ -58,7 +64,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::Contact::Admin->new;
my $form = $self->get_form($c);
for my $contact ($contacts->search({}, {order_by => {-asc => 'me.id'}, prefetch => ['reseller']})->all) {
push @embedded, $self->hal_from_contact($c, $contact, $form);
push @links, Data::HAL::Link->new(
@ -127,7 +133,7 @@ sub POST :Allow {
);
last unless $resource;
my $form = NGCP::Panel::Form::Contact::Admin->new;
my $form = $self->get_form($c);
$resource->{reseller_id} //= undef;
last unless $self->validate_form(
c => $c,

@ -4,7 +4,6 @@ use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Contact::Admin qw();
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
@ -105,7 +104,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::Contact::Admin->new;
my $form = $self->get_form($c);
$contact = $self->update_contact($c, $contact, $old_resource, $resource, $form);
last unless $contact;
@ -145,7 +144,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $contact->get_inflated_columns };
my $form = NGCP::Panel::Form::Contact::Admin->new;
my $form = $self->get_form($c);
$contact = $self->update_contact($c, $contact, $old_resource, $resource, $form);
last unless $contact;

@ -9,7 +9,6 @@ use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Form::Contract::ProductSelect qw();
use Path::Tiny qw(path);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
@ -17,6 +16,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines a billing container for end customers. Customers usually have one or more <a href="#subscribers">Subscribers</a>. A <a href="#billingprofiles">Billing Profile</a> is assigned to a customer, and it has <a href="#contractbalances">Contract Balances</a> indicating the saldo of the customer for current and past billing intervals.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::Customers';
@ -83,7 +89,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::Contract::ProductSelect->new;
my $form = $self->get_form($c);
for my $customer($customers->all) {
push @embedded, $self->hal_from_customer($c, $customer, $form);
push @links, Data::HAL::Link->new(
@ -175,7 +181,7 @@ sub POST :Allow {
$resource->{product_id} = $product->id;
$resource->{contact_id} //= undef;
my $form = NGCP::Panel::Form::Contract::ProductSelect->new;
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,

@ -7,7 +7,6 @@ use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Contract::ProductSelect qw();
use NGCP::Panel::Utils::ValidateJSON qw();
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
@ -111,7 +110,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::Contract::ProductSelect->new;
my $form = $self->get_form($c);
$customer = $self->update_customer($c, $customer, $old_resource, $resource, $form);
last unless $customer;
@ -151,7 +150,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $customer->get_inflated_columns };
my $form = NGCP::Panel::Form::Contract::ProductSelect->new;
my $form = $self->get_form($c);
$customer = $self->update_customer($c, $customer, $old_resource, $resource, $form);
last unless $customer;

@ -16,6 +16,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Specifies certain settings (preferences) for a <a href="#domains">Domain</a>. The full list of settings can be obtained via <a href="/api/domainpreferencedefs/">DomainPreferenceDefs</a>.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::DomainPreferences';

@ -16,6 +16,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Specifies a SIP Domain to be used as host part for SIP <a href="#subscribers">Subscribers</a>. You need a domain before you can create a subscriber. Multiple domains can be created. A domain could also be an IPv4 or IPv6 address (whereas the latter needs to be enclosed in square brackets, e.g. [::1]).'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::Domains';

@ -8,7 +8,6 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Form::Reseller qw();
use Path::Tiny qw(path);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
@ -16,6 +15,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines a reseller on the system. A reseller can manage his own <a href="#domains">Domains</a> and <a href="#customers">Customers</a>.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::Resellers';
@ -56,7 +62,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::Reseller->new;
my $form = $self->get_form($c);
for my $reseller ($resellers->all) {
push @embedded, $self->hal_from_reseller($c, $reseller, $form);
push @links, Data::HAL::Link->new(
@ -128,7 +134,7 @@ sub POST :Allow {
);
last unless $resource;
my $form = NGCP::Panel::Form::Reseller->new;
my $form = $self->get_form($c);
$resource->{contract_id} //= undef;
last unless $self->validate_form(
c => $c,

@ -4,7 +4,6 @@ use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Reseller qw();
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
@ -105,7 +104,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::Reseller->new;
my $form = $self->get_form($c);
$reseller = $self->update_reseller($c, $reseller, $old_resource, $resource, $form);
last unless $reseller;
@ -145,7 +144,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $reseller->get_inflated_columns };
my $form = NGCP::Panel::Form::Reseller->new;
my $form = $self->get_form($c);
$reseller = $self->update_reseller($c, $reseller, $old_resource, $resource, $form);
last unless $reseller;

@ -40,12 +40,36 @@ sub auto :Private {
sub GET : Allow {
my ($self, $c) = @_;
my @colls = $self->get_collections;
foreach my $coll(@colls) {
my $mod = $coll;
$mod =~ s/^.+\/([a-zA-Z0-9_]+)\.pm$/$1/;
next if($mod eq "DomainPreferenceDefs"); # not a "real" collection
my $rel = lc $mod;
my $full_mod = 'NGCP::Panel::Controller::API::'.$mod;
my $role = $full_mod->config->{action}->{GET}->{AllowedRole};
if(ref $role eq "ARRAY") {
next unless grep @{ $role }, $c->user->roles;
} else {
next unless $role eq $c->user->roles;
}
my $form = $full_mod->get_form($c);
$c->stash->{collections}->{$rel} = {
name => $mod,
description => $full_mod->api_description,
fields => $self->get_collection_properties($form),
};
}
$c->stash(template => 'api/root.tt');
$c->forward($c->view);
$c->response->headers(HTTP::Headers->new(
Content_Language => 'en',
Content_Type => 'application/xhtml+xml',
$self->collections_link_headers,
#$self->collections_link_headers,
));
return;
}
@ -69,7 +93,7 @@ sub OPTIONS : Allow {
return;
}
sub collections_link_headers : Private {
sub get_collections {
my ($self) = @_;
# figure out base path of our api modules
@ -88,6 +112,14 @@ sub collections_link_headers : Private {
->not($itemrule);
my @colls = $rule->in($libpath);
return @colls;
}
sub collections_link_headers : Private {
my ($self) = @_;
my @colls = $self->get_collections;
# create Link header for each of the collections
my @links = ();
foreach my $mod(@colls) {
@ -108,6 +140,81 @@ sub invalid_user : Private {
return;
}
sub field_to_json : Private {
my ($self, $name) = @_;
given($name) {
when(/Float|Integer|Money|PosInteger|Minute|Hour|MonthDay|Year/) {
return "Number";
}
when(/Boolean/) {
return "Boolean";
}
when(/Repeatable/) {
return "Array";
}
when(/\+NGCP::Panel::Field::Regex/) {
return "String";
}
when(/\+NGCP::Panel::Field::EmailList/) {
return "String";
}
when(/\+NGCP::Panel::Field::SubscriberStatusSelect/) {
return "String";
}
when(/\+NGCP::Panel::Field::SubscriberLockSelect/) {
return "Number";
}
when(/\+NGCP::Panel::Field::E164/) {
return "Object";
}
when(/\+NGCP::Panel::Field::AliasNumber/) {
return "Array";
}
# usually {xxx}{id}
when(/\+NGCP::Panel::Field::/) {
return "Number";
}
default {
return "String";
}
}
}
sub get_collection_properties {
my ($self, $form) = @_;
my @props = ();
foreach my $f($form->fields) {
next if (
$f->type eq "Hidden" ||
$f->type eq "Button" ||
$f->type eq "Submit" ||
0);
my @types = ();
push @types, 'null' unless $f->required;
push @types, $self->field_to_json($f->type);
my $name = $f->name;
if($f->type =~ /^\+NGCP::Panel::Field::/) {
if($f->type =~ /E164/) {
$name = 'primary_number';
} elsif($f->type =~ /AliasNumber/) {
$name = 'alias_numbers';
} elsif($f->type !~ /Regex|EmailList|SubscriberStatusSelect|SubscriberLockSelect/) {
$name .= '_id';
}
}
my $desc;
if($f->element_attr) {
$desc = $f->element_attr->{title}->[0];
} else {
$desc = $name;
}
push @props, { name => $name, description => $desc, types => \@types };
}
return \@props;
}
sub end : Private {
my ($self, $c) = @_;

@ -19,6 +19,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines an actual user who can log into the web panel, register devices via SIP and/or XMPP and place and receive calls via SIP. A subscriber always belongs to a <a href="#customers">Customer</a> and is placed inside a <a href="#domains">Domain</a>.'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::Subscribers';

@ -7,7 +7,6 @@ use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Contact::Reseller qw();
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
@ -17,6 +16,13 @@ require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines a physical or legal person\'s address (postal and/or email) to be used in <a href="#contracts">System Contracts</a> (contracts for peerings and resellers).'
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::SystemContacts';
@ -58,7 +64,7 @@ sub GET :Allow {
rows => $rows,
});
my (@embedded, @links);
my $form = NGCP::Panel::Form::Contact::Reseller->new;
my $form = $self->get_form($c);
for my $contact ($contacts->search({}, {order_by => {-asc => 'me.id'}, prefetch => ['reseller']})->all) {
push @embedded, $self->hal_from_contact($c, $contact, $form);
push @links, Data::HAL::Link->new(
@ -127,7 +133,7 @@ sub POST :Allow {
);
last unless $resource;
my $form = NGCP::Panel::Form::Contact::Reseller->new;
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,

@ -4,7 +4,6 @@ use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Contact::Reseller qw();
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
@ -105,7 +104,7 @@ sub PATCH :Allow {
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = NGCP::Panel::Form::Contact::Reseller->new;
my $form = $self->get_form($c);
$contact = $self->update_contact($c, $contact, $old_resource, $resource, $form);
last unless $contact;
@ -145,7 +144,7 @@ sub PUT :Allow {
last unless $resource;
my $old_resource = { $contact->get_inflated_columns };
my $form = NGCP::Panel::Form::Contact::Admin->new;
my $form = $self->get_form($c);
$contact = $self->update_contact($c, $contact, $old_resource, $resource, $form);
last unless $contact;

@ -17,7 +17,7 @@ has_field 'source' => (
maxlength => 255,
element_attr => {
rel => ['tooltip'],
title => ['string, empty or POSIX regular expression, e.g.: ^431.+$']
title => ['A POSIX regular expression to match the calling number (e.g. ^.+$).']
},
);
@ -27,7 +27,7 @@ has_field 'destination' => (
required => 1,
element_attr => {
rel => ['tooltip'],
title => ['string, POSIX regular expression, e.g.: ^431.+$']
title => ['A POSIX regular expression to match the called number (e.g. ^431.+$).']
},
);
@ -39,12 +39,20 @@ has_field 'direction' => (
],
default => 'out',
required => 1,
element_attr => {
rel => ['tooltip'],
title => ['The call direction when to apply this fee (either for inbound or outbound calls).']
},
);
has_field 'billing_zone' => (
type => '+NGCP::Panel::Field::BillingZone',
label => 'Zone',
validate_when_empty => 1,
element_attr => {
rel => ['tooltip'],
title => ['The billing zone id this fee belongs to.']
},
);
has_field 'onpeak_init_rate' => (
@ -52,7 +60,7 @@ has_field 'onpeak_init_rate' => (
precision => 18,
element_attr => {
rel => ['tooltip'],
title => ['The cost of the init interval in cents per second (e.g 0.90)']
title => ['The cost of the first interval in cents per second (e.g. 0.90).']
},
default => 0,
);
@ -61,7 +69,7 @@ has_field 'onpeak_init_interval' => (
type => 'Integer',
element_attr => {
rel => ['tooltip'],
title => ['The length of the first interval']
title => ['The length of the first interval in seconds (e.g. 60).']
},
default => 60,
required => 1,
@ -73,7 +81,7 @@ has_field 'onpeak_follow_rate' => (
precision => 18,
element_attr => {
rel => ['tooltip'],
title => ['The cost of each following interval in cents per second (e.g 0.90)']
title => ['The cost of each following interval in cents per second (e.g. 0.90).']
},
default => 0,
);
@ -82,7 +90,7 @@ has_field 'onpeak_follow_interval' => (
type => 'Integer',
element_attr => {
rel => ['tooltip'],
title => ['The length of the following intervals']
title => ['The length of each following interval in seconds (e.g. 30).']
},
default => 60,
required => 1,
@ -94,7 +102,7 @@ has_field 'offpeak_init_rate' => (
precision => 18,
element_attr => {
rel => ['tooltip'],
title => ['The cost of the init interval in cents per second (e.g 0.90)']
title => ['The cost of the first interval in cents per second (e.g. 0.90).']
},
default => 0,
);
@ -103,7 +111,7 @@ has_field 'offpeak_init_interval' => (
type => 'Integer',
element_attr => {
rel => ['tooltip'],
title => ['The length of the first interval']
title => ['The length of the first interval in seconds (e.g. 60).']
},
default => 60,
required => 1,
@ -115,7 +123,7 @@ has_field 'offpeak_follow_rate' => (
precision => 18,
element_attr => {
rel => ['tooltip'],
title => ['The cost of each following interval in cents per second (e.g 0.90)']
title => ['The cost of each following interval in cents per second (e.g. 0.90).']
},
default => 0,
);
@ -124,7 +132,7 @@ has_field 'offpeak_follow_interval' => (
type => 'Integer',
element_attr => {
rel => ['tooltip'],
title => ['The length of the following intervals']
title => ['The length of each following interval in seconds (e.g. 30).']
},
default => 60,
required => 1,
@ -135,7 +143,7 @@ has_field 'use_free_time' => (
type => 'Boolean',
element_attr => {
rel => ['tooltip'],
title => ['Free minutes may be used when calling this destination']
title => ['Whether free minutes may be used when calling this destination.']
},
default => 0,
);

@ -7,6 +7,10 @@ use Moose::Util::TypeConstraints;
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
validate_when_empty => 1,
element_attr => {
rel => ['tooltip'],
title => ['The reseller id to assign this domain to.']
},
);
has_block 'fields' => (

@ -19,6 +19,10 @@ has_field 'domain' => (
type => 'Text',
required => 1,
label => 'SIP Domain',
element_attr => {
rel => ['tooltip'],
title => ['The fully qualified domain name (e.g. sip.example.org).']
},
);
has_field 'save' => (

@ -11,6 +11,11 @@ use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Form::BillingFee qw();
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::BillingFee->new;
}
sub hal_from_fee {
my ($self, $c, $fee, $form) = @_;
@ -33,7 +38,7 @@ sub hal_from_fee {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::BillingFee->new;
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
@ -79,7 +84,7 @@ sub update_fee {
} else {
$reseller_id = $c->user->contract->contact->reseller_id;
}
$form //= NGCP::Panel::Form::BillingFee->new;
$form //= $self->get_form($c);
# TODO: for some reason, formhandler lets missing profile/zone id
my $billing_profile_id = $resource->{billing_profile_id} // undef;

@ -12,6 +12,11 @@ use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Utils::Preferences;
use NGCP::Panel::Form::BillingProfile::Admin qw();
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::BillingProfile::Admin->new;
}
sub hal_from_profile {
my ($self, $c, $profile, $form) = @_;
@ -38,7 +43,7 @@ sub hal_from_profile {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::BillingProfile::Admin->new;
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
@ -72,7 +77,7 @@ sub profile_by_id {
sub update_profile {
my ($self, $c, $profile, $old_resource, $resource, $form) = @_;
$form //= NGCP::Panel::Form::BillingProfile::Admin->new;
$form //= $self->get_form($c);
# TODO: for some reason, formhandler lets missing reseller slip thru
$resource->{reseller_id} //= undef;
return unless $self->validate_form(

@ -11,6 +11,11 @@ use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Form::BillingZone qw();
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::BillingZone->new;
}
sub hal_from_zone {
my ($self, $c, $zone, $form) = @_;
@ -33,7 +38,7 @@ sub hal_from_zone {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::BillingZone->new;
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
@ -79,7 +84,7 @@ sub update_zone {
} else {
$reseller_id = $c->user->contract->contact->reseller_id;
}
$form //= NGCP::Panel::Form::BillingZone->new;
$form //= $self->get_form($c);
# TODO: for some reason, formhandler lets missing profile id
my $billing_profile_id = $resource->{billing_profile_id} // undef;
return unless $self->validate_form(

@ -11,6 +11,11 @@ use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Form::Contract::PeeringReseller qw();
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Contract::PeeringReseller->new;
}
sub hal_from_contract {
my ($self, $c, $contract, $form) = @_;
@ -61,7 +66,7 @@ sub hal_from_contract {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::Contract::PeeringReseller->new;
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
@ -105,7 +110,7 @@ sub update_contract {
return;
}
$form //= NGCP::Panel::Form::Contract::PeeringReseller->new;
$form //= $self->get_form($c);
# TODO: for some reason, formhandler lets missing contact_id slip thru
$resource->{contact_id} //= undef;
return unless $self->validate_form(

@ -9,6 +9,11 @@ use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Form::Contact::Admin;
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Contact::Admin->new;
}
sub hal_from_contact {
my ($self, $c, $contact, $form) = @_;
my %resource = $contact->get_inflated_columns;
@ -28,7 +33,7 @@ sub hal_from_contact {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::Contact::Admin->new;
$form //= $self->get_form($c);
$self->validate_form(
c => $c,
resource => \%resource,
@ -53,7 +58,7 @@ sub contact_by_id {
sub update_contact {
my ($self, $c, $contact, $old_resource, $resource, $form) = @_;
$form //= NGCP::Panel::Form::Contact::Admin->new;
$form //= $self->get_form($c);
# TODO: for some reason, formhandler lets missing reseller_id slip thru
$resource->{reseller_id} //= undef;
return unless $self->validate_form(

@ -12,6 +12,11 @@ use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Utils::Preferences;
use NGCP::Panel::Form::Contract::ProductSelect qw();
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Contract::PeeringReseller->new;
}
sub hal_from_customer {
my ($self, $c, $customer, $form) = @_;
@ -62,7 +67,7 @@ sub hal_from_customer {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::Contract::ProductSelect->new;
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
@ -126,7 +131,7 @@ sub update_customer {
return;
}
$form //= NGCP::Panel::Form::Contract::ProductSelect->new;
$form //= $self->get_form($c);
# TODO: for some reason, formhandler lets missing contact_id slip thru
$resource->{contact_id} //= undef;
return unless $self->validate_form(

@ -10,6 +10,11 @@ use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Form::Reseller qw();
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Reseller->new;
}
sub hal_from_reseller {
my ($self, $c, $reseller, $form) = @_;
@ -41,7 +46,7 @@ sub hal_from_reseller {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::Reseller->new;
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
@ -65,7 +70,7 @@ sub reseller_by_id {
sub update_reseller {
my ($self, $c, $reseller, $old_resource, $resource, $form) = @_;
$form //= NGCP::Panel::Form::Reseller->new;
$form //= $self->get_form($c);
$resource->{contract_id} //= undef;
return unless $self->validate_form(
c => $c,

@ -9,6 +9,11 @@ use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Form::Contact::Reseller;
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Contact::Reseller->new;
}
sub hal_from_contact {
my ($self, $c, $contact, $form) = @_;
my %resource = $contact->get_inflated_columns;
@ -29,7 +34,7 @@ sub hal_from_contact {
relation => 'ngcp:'.$self->resource_name,
);
$form //= NGCP::Panel::Form::Contact::Reseller->new;
$form //= $self->get_form($c);
# TODO: i'd expect reseller to be removed automatically
delete $resource{reseller_id};
@ -57,7 +62,7 @@ sub contact_by_id {
sub update_contact {
my ($self, $c, $contact, $old_resource, $resource, $form) = @_;
$form //= NGCP::Panel::Form::Contact::Reseller->new;
$form //= $self->get_form($c);
delete $resource->{reseller_id};
return unless $self->validate_form(
c => $c,

@ -0,0 +1,39 @@
#!/usr/bin/perl -w
use strict;
use Sipwise::Base;
use Data::Printer;
use NGCP::Panel::Form::BillingFee;
sub field_to_json {
my $name = shift;
given($name) {
when(/Float|Integer|Money|PosInteger|Minute|Hour|MonthDay|Year/) {
return "Number";
}
when(/Boolean/) {
return "Boolean";
}
when(/Repeatable/) {
return "Array";
}
default {
return "String";
}
}
}
my $form = NGCP::Panel::Form::BillingFee->new;
foreach my $f($form->fields) {
next if (
$f->type eq "Hidden" ||
$f->type eq "Button" ||
$f->type eq "Submit" ||
0);
my @types = ();
push @types, 'null' unless $f->required;
push @types, field_to_json($f->type);
print $f->name . " (" . join(', ', @types) . ")" . "\n";
}

@ -53,16 +53,11 @@
{ level = 2, id = 'intro', title = 'Introduction', },
{ level = 2, id = 'auth', title = 'Authentication', },
{ level = 2, id = 'relations', title = 'Resources', },
{ level = 3, id = 'rel-systemcontacts', title = 'System Contacts', uri = '/api/contacts/' },
# { level = 3, id = 'rel-resellers', title = 'Resellers', uri = '/api/resellers/' },
# { level = 3, id = 'rel-admins', title = 'Admins', uri = '/api/admins/' },
# { level = 3, id = 'rel-customers', title = 'Customers', uri = '/api/customers/' },
# { level = 3, id = 'rel-billingprofiles', title = 'Billing Profiles', uri = '/api/billing/' },
{ level = 2, id = 'definitions', title = 'Definitions', },
];
FOREACH col IN collections.keys.sort;
chapters.push({ level = 3, id = col, title = collections.$col.name, uri = '/api/' _ col _ '/' });
END;
chapters.push({ level = 2, id = 'definitions', title = 'Definitions' });
-%]
<h1>Sipwise NGCP HTTP API Documentation</h1>
<div class="content">
@ -91,8 +86,14 @@
[% END -%]
<div class="chapter">
[%
IF chapter.level != 3;
t = 'api/root/' _ chapter.id _ '.tt';
INCLUDE $t level=chapter.level title=chapter.title id=chapter.id uri=chapter.uri;
ELSE;
t = 'api/root/collection.tt';
colname = chapter.id;
INCLUDE $t level=chapter.level title=chapter.title id=chapter.id uri=chapter.uri col=collections.$colname;
END;
-%]
</div>
[% END -%]

@ -0,0 +1,431 @@
<h[% level %] id="[% id %]">
[% IF uri -%]
<a href="[% uri %]" rel="collection">
[% END -%]
[% title %]
[% IF uri -%]
</a>
[% END -%]
</h[% level %]>
<h[% level + 1 %]>Description</h[% level + 1%]>
[% col.description %]
<h[% level + 1 %]>Properties</h[% level + 1%]>
<ul id="[% id %]-props">
[% FOREACH f IN col.fields -%]
<li><b>[% f.name %] </b>(<i>[% f.types.join(', ') %]</i>): [% f.description %]</li>
[% END -%]
</ul>
<h[% level + 1 %]>Examples</h[% level + 1 %]>
<div class="examples">
<h[% level + 2 %]>Request available HTTP methods on the URI</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X OPTIONS -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem https://example.org:1443/api/' _ id _ '/';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request =
'OPTIONS /api/' _ id _ '/ HTTP/1.1';
response =
'HTTP/1.1 200 OK
Allow: GET, HEAD, OPTIONS, POST
Accept-Post: application/hal+json; profile="http://purl.org/sipwise/ngcp-api/#rel-' _ id _ '"';
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Request the entire <i>[% id %]</i> collection</h[% level + 2 %]>
<p>
<p>
You cannot request the entire collection at once, but instead you can simply page through the results. The response provides <i>prev</i> and <i>next</i> links you can follow to get the next page.
To define the page number and the rows per page to return, you can pass the parameters <i>page</i> and <i>rows</i>. Default values are <i>page=1</i> and <i>rows=10</i>, if you do not provide them.
</p>
[%
cmd = 'curl -i -X GET -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/' _ id _ '/?page=1&rows=2\'';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
props = ' "id" : 1';
FOREACH p IN col.fields;
NEXT IF p.types.0 == 'null';
props = props _ ',
';
IF p.types.0 == "Number";
val = 4;
ELSIF p.types.0 == "String";
val = '"test"';
ELSIF p.types.0 == "Boolean";
val = 'true';
ELSIF p.types.0 == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ ' "' _ p.name _ '" : ' _ val;
END;
props = props _ '
';
request =
'GET /api/' _ id _ '/?page=1&rows=1 HTTP/1.1';
response =
'HTTP/1.1 200 OK
Content-Type: application/hal+json; profile="http://purl.org/sipwise/ngcp-api/"
{
"_embedded" : {
"ngcp:' _ id _ '" : [
{
"_links" : {
"collection" : {
"href" : "/api/' _ id _ '/"
},
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/' _ id _ '/1"
}
},
' _ props _ ' },
]
},
"_links" : {
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"next" : {
"href" : "/api/' _ id _ '/?page=2&rows=1"
},
"ngcp:' _ id _ '" : [
{
"href" : "/api/' _ id _ '/1"
},
],
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/' _ id _ '/?page=1&rows=1"
}
},
"total_count" : 100
}';
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Request a specific <i>[% id %]</i> item</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X GET -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/' _ id _ '/1\'';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
props = ' "id" : 1';
FOREACH p IN col.fields;
NEXT IF p.types.0 == 'null';
props = props _ ',
';
IF p.types.0 == "Number";
val = 4;
ELSIF p.types.0 == "String";
val = '"test"';
ELSIF p.types.0 == "Boolean";
val = 'true';
ELSIF p.types.0 == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ ' "' _ p.name _ '" : ' _ val;
END;
props = props _ '
';
request =
'GET /api/' _ id _ '/1 HTTP/1.1';
response =
'HTTP/1.1 200 OK
Content-Type: application/hal+json; profile="http://purl.org/sipwise/ngcp-api/"
Link: </api/' _ id _ '/>; rel=collection
Link: <http://purl.org/sipwise/ngcp-api/>; rel=profile
Link: </api/' _ id _ '/1>; rel="item self"
{
"_links" : {
"collection" : {
"href" : "/api/' _ id _ '/"
},
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/' _ id _ '/1"
}
},
' _ props _ ' }';
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Create a new <i>[% id %]</i> item</h[% level + 2 %]>
<p>
[%
props = '';
FOREACH p IN col.fields;
NEXT IF p.types.0 == 'null';
IF p.types.0 == "Number";
val = 4;
ELSIF p.types.0 == "String";
val = '"test"';
ELSIF p.types.0 == "Boolean";
val = 'true';
ELSIF p.types.0 == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ '"' _ p.name _ '" : ' _ val;
props = props _ ', ';
END;
props = props.substr(0, props.length - 2);
cmd = 'curl -i -X POST -H \'Connection: close\' -H \'Content-Type: application/json\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/' _ id _ '/\' --data-binary \'{ ' _ props _ ' }\'';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
props = '';
FOREACH p IN col.fields;
NEXT IF p.types.0 == 'null';
IF p.types.0 == "Number";
val = 4;
ELSIF p.types.0 == "String";
val = '"test"';
ELSIF p.types.0 == "Boolean";
val = 'true';
ELSIF p.types.0 == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ ' "' _ p.name _ '" : ' _ val;
props = props _ ',
';
END;
props = props.substr(0, props.length - 2);
request =
'POST /api/' _ id _ '/ HTTP/1.1
Content-Type: application/json
{
' _ props _ '
}';
response =
'HTTP/1.1 201 Created
Location: /api/' _ id _ '/2';
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
%]
</p>
<h[% level + 2 %]>Update an existing <i>[% id %]</i> item</h[% level + 2 %]>
<p>
[%
props = '';
FOREACH p IN col.fields;
NEXT IF p.types.0 == 'null';
IF p.types.0 == "Number";
val = 4;
ELSIF p.types.0 == "String";
val = '"test"';
ELSIF p.types.0 == "Boolean";
val = 'true';
ELSIF p.types.0 == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ '"' _ p.name _ '" : ' _ val;
props = props _ ', ';
END;
props = props.substr(0, props.length - 2);
cmd = 'curl -i -X PUT -H \'Connection: close\' -H \'Content-Type: application/json\' -H \'Prefer: return=minimal\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/' _ id _ '/2\' --data-binary \'{ ' _ props _ ' }\'';
INCLUDE helpers/api_command.tt cmd=cmd extended=1 level=level+3;
props = '';
FOREACH p IN col.fields;
NEXT IF p.types.0 == 'null';
IF p.types.0 == "Number";
val = 4;
ELSIF p.types.0 == "String";
val = '"test"';
ELSIF p.types.0 == "Boolean";
val = 'true';
ELSIF p.types.0 == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ ' "' _ p.name _ '" : ' _ val;
props = props _ ',
';
END;
props = props.substr(0, props.length - 2);
request =
'PUT /api/' _ id _ '/2 HTTP/1.1
Content-Type: application/json
Prefer: return=minimal
{
' _ props _ '
}';
response =
'HTTP/1.1 204 No Content
Preference-Applied: return=minimal';
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Update specific fields of an existing <i>systemcontacts</i> entry</h[% level + 2 %]>
<p>
[%
props = ''; rem = 0; rep = 0;
FOREACH p IN col.fields;
type = p.types.0;
IF type == 'null';
NEXT IF rem >= 1;
op = "remove";
type = p.types.1;
rem = rem + 1;
ELSE;
NEXT IF rep >= 1;
op = "replace";
rep = rep + 1;
END;
IF type == "Number";
val = 5;
ELSIF type == "String";
val = '"other"';
ELSIF type == "Boolean";
val = 'false';
ELSIF type == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ '{ ';
props = props _ '"op" : "' _ op _ '", "path" : "/' _ p.name _ '"';
UNLESS op == "remove";
props = props _ ', "value" : ' _ val;
END;
props = props _ ' }, ';
LAST IF rep >= 1 && rem >= 1;
END;
props = props.substr(0, props.length - 2);
cmd = 'curl -i -X PATCH -H \'Connection: close\' -H \'Content-Type: application/json-patch+json\' -H \'Prefer: return=minimal\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/' _ id _ '/2\' --data-binary \'[ ' _ props _ ' ]\'';
INCLUDE helpers/api_command.tt cmd=cmd extended=1 level=level+3;
%]
<p>
The PATCH method allows to operate on specific fields of an item. The <b>path</b> attribute can point to a normal property (e.g. /something) or to a specific element in an array (e.g. /something/0). Available <b>op</b> values are:
<ul>
<li>
<b>remove</b>
<p>Removes the property pointed to by <b>path</b>. You can point to a normal property (e.g. /something) or to a specific element in an array (e.g. /something/0).</p>
</li>
<li>
<b>replace</b>
<p>Replaces the value pointed to by <b>path</b> by the new value passed via <b>value</b>.</p>
</li>
<li>
<b>add</b>
<p>Adds a new property <b>path</b> by the new value passed via <b>value</b>.</p>
</li>
<li>
<b>copy, move</b>
<p>Copies or moves (copy, then remove) the value from property defined by <b>from</b> to the propery defined by <b>path</b>.</p>
</li>
</ul>
</p>
[%
props = ''; rem = 0; rep = 0;
FOREACH p IN col.fields;
type = p.types.0;
IF type == 'null';
NEXT IF rem >= 1;
op = "remove";
type = p.types.1;
rem = rem + 1;
ELSE;
NEXT IF rep >= 1;
op = "replace";
rep = rep + 1;
END;
IF type == "Number";
val = 5;
ELSIF type == "String";
val = '"other"';
ELSIF type == "Boolean";
val = 'false';
ELSIF type == "Array";
val = '[]';
ELSE;
val = '"missing"';
END;
props = props _ '
{
';
props = props _ ' "op" : "' _ op _ '",
"path" : "/' _ p.name _ '"';
UNLESS op == "remove";
props = props _ ',
"value" : ' _ val;
END;
props = props _ '
}, ';
LAST IF rep >= 1 && rem >= 1;
END;
props = props.substr(0, props.length - 2);
request =
'PATCH /api/' _ id _ '/2 HTTP/1.1
Content-Type: application/json-patch+json
Prefer: return=minimal
[' _ props _ '
]';
response =
'HTTP/1.1 204 No Content
Preference-Applied: return=minimal';
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
</div>
[% # vim: set tabstop=4 syntax=html expandtab: -%]

@ -1,338 +0,0 @@
<h[% level %] id="[% id %]">
[% IF uri -%]
<a href="[% uri %]" rel="collection">
[% END -%]
[% title %]
[% IF uri -%]
</a>
[% END -%]
</h[% level %]>
<h[% level + 1 %]>Description</h[% level + 1%]>
<p>
The <a href="#rel-systemcontacts">systemcontacts</a> item is used to specify the contact information of <a href="#rel-contracts">peering and reseller contracts</a>.
</p>
<h[% level + 1 %]>Properties</h[% level + 1%]>
<ul id="[% id %]-props"></ul>
<script>
<![CDATA[
$(function() {
var items = [];
var props = '[% INSERT 'js/api/properties/systemcontacts-item.json' | replace('\'', '\\\'') | replace("\n", ' ') | replace('\\\"', '\\\\\"') -%]';
var jprops = $.parseJSON(props);
for(var key in jprops.properties) {
var prop = jprops.properties[key];
items.push('<li><b>' + key + '</b> (<i>' + prop.type + '</i>): ' + prop.description + '</li>');
}
$('#[% id %]-props').append(items.join(''));
});
]]>
</script>
<h[% level + 1 %]>Relation Links</h[% level + 1%]>
<p>None</p>
<!--
<ul id="[% id %]-links"></ul>
<script>
<![CDATA[
$(function() {
var items = [];
var links = '[% INSERT 'js/api/links/customercontacts-item.json' | replace('\'', '\\\'') | replace("\n", ' ') | replace('\\\"', '\\\\\"') -%]';
var jlinks = $.parseJSON(links);
for(var key in jlinks._links) {
var link = jlinks._links[key];
items.push('<li><b>' + key + '</b> (<i>href</i>): ' + link.href + '</li>');
}
$('#[% id %]-links').append(items.join(''));
});
]]>
</script>
-->
<h[% level + 1 %]>Examples</h[% level + 1 %]>
<div class="examples">
<h[% level + 2 %]>Request available HTTP methods on the URI</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X OPTIONS -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem https://example.org:1443/api/systemcontacts/';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request = [
'OPTIONS /api/systemcontacts/ HTTP/1.1'
];
response = [
'HTTP/1.1 200 OK',
'Allow: GET, HEAD, OPTIONS, POST',
'<a href="#draft-wilde-accept-post">Accept-Post</a>: application/hal+json; profile="http://purl.org/sipwise/ngcp-api/#rel-systemcontacts"',
];
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Request the entire <i>systemcontacts</i> collection</h[% level + 2 %]>
<p>
<p>
You cannot request the entire collection at once, but instead you can simply page through the results. The response provides <i>prev</i> and <i>next</i> links you can follow to get the next page.
To define the page number and the rows per page to return, you can pass the parameters <i>page</i> and <i>rows</i>. Default values are <i>page=1</i> and <i>rows=10</i>, if you do not provide them.
</p>
[%
cmd = 'curl -i -X GET -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/systemcontacts/?page=1&rows=2\'';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request = [
'GET /api/systemcontacts/ HTTP/1.1',
];
response = [
'HTTP/1.1 200 OK
Content-Type: application/hal+json; profile="http://purl.org/sipwise/ngcp-api/"
Link: </api/systemcontacts/1>; rel="item http://purl.org/sipwise/ngcp-api/#rel-systemcontacts"
Link: </api/systemcontacts/2>; rel="item http://purl.org/sipwise/ngcp-api/#rel-systemcontacts"
Link: <http://purl.org/sipwise/ngcp-api/>; rel=profile
Link: </api/systemcontacts/?page=1&rows=2>; rel="collection self"
Link: </api/systemcontacts/?page=2&rows=2>; rel=next
{
"_embedded" : {
"ngcp:systemcontacts" : [
{
"_links" : {
"collection" : {
"href" : "/api/systemcontacts/"
},
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/systemcontacts/1"
}
},
"city" : null,
"company" : null,
"country" : null,
"email" : "default-peering@default.invalid",
"firstname" : null,
"lastname" : null,
"phonenumber" : null,
"postcode" : null,
"street" : null
},
{
"_links" : {
"collection" : {
"href" : "/api/systemcontacts/"
},
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/systemcontacts/2"
}
},
"city" : null,
"company" : null,
"country" : null,
"email" : "default-reseller@default.invalid",
"firstname" : null,
"lastname" : null,
"phonenumber" : null,
"postcode" : null,
"street" : null
}
]
},
"_links" : {
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"next" : {
"href" : "/api/systemcontacts/?page=2&rows=2"
},
"ngcp:systemcontacts" : [
{
"href" : "/api/systemcontacts/1"
},
{
"href" : "/api/systemcontacts/2"
}
],
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/systemcontacts/?page=1&rows=2"
}
},
"total_count" : 57
}'
];
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Request a specific <i>systemcontacts</i> item</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X GET -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/systemcontacts/3\'';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request = [
'GET /api/systemcontacts/3 HTTP/1.1',
];
response = [
'HTTP/1.1 200 OK
Content-Type: application/hal+json; profile="http://purl.org/sipwise/ngcp-api/"
Link: </api/systemcontacts/>; rel=collection
Link: <http://purl.org/sipwise/ngcp-api/>; rel=profile
Link: </api/systemcontacts/3>; rel="item self"
{
"_links" : {
"collection" : {
"href" : "/api/systemcontacts/"
},
"curies" : {
"href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
"name" : "ngcp",
"templated" : true
},
"profile" : {
"href" : "http://purl.org/sipwise/ngcp-api/"
},
"self" : {
"href" : "/api/systemcontacts/3"
}
},
"city" : null,
"company" : null,
"country" : null,
"email" : "default-reseller@default.invalid",
"firstname" : null,
"lastname" : null,
"phonenumber" : null,
"postcode" : null,
"street" : null
}'
];
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Create a new <i>systemcontacts</i> entry</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X POST -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.com:1443/api/systemcontacts/\' --data-binary \'{ "firstname": "John", "lastname": "Doe", "city": null, "company": null, "country": null, "phonenumber": null, "postcode": null, "street": null, "email": "john.doe@reseller.invalid", "reseller_id": 1 }\' -H \'Content-Type: application/json\'';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request = [
'POST /api/systemcontacts/ HTTP/1.1',
'Content-Type: application/json',
'',
'{
"firstname": "John",
"lastname": "Doe",
"email": "john.doe@reseller.invalid",
"city": null,
"company": null,
"country": null,
"phonenumber": null,
"postcode": null,
"street": null
}',
];
response = [
'HTTP/1.1 201 Created
Location: /api/systemcontacts/280'
];
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Update an existing <i>systemcontacts</i> entry</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X PUT -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/systemcontacts/10\' --data-binary \'{ "firstname": "John", "lastname": "Doe", "email": "something.else@reseller.invalid", "city": null, "company": null, "country": null, "phonenumber": null, "postcode": null, "street": null }\' -H \'Content-Type: application/json\' -H \'Prefer: return=minimal\'';
INCLUDE helpers/api_command.tt cmd=cmd extended=1 level=level+3;
request = [
'PUT /api/contacts/10 HTTP/1.1',
'Content-Type: application/json',
'Prefer: return=minimal',
'',
'{
"firstname": "John",
"lastname": "Doe",
"email": "something.else@reseller.invalid",
"city": null,
"company": null,
"country": null,
"phonenumber": null,
"postcode": null,
"street": null
}',
];
response = [
'HTTP/1.1 204 No Content',
'Preference-Applied: return=minimal',
];
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
</p>
<h[% level + 2 %]>Update specific fields of an existing <i>systemcontacts</i> entry</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X PATCH -H \'Connection: close\' --cert NGCP-API-client-certificate.pem --cacert ca-cert.pem \'https://example.org:1443/api/systemcontacts/10 --data-binary \'[ { "op": "replace", "path": "/email", "value": "other.john.doe@reseller.invalid" }, { "op": "replace", "path": "/street", "value": "Some Street" } ]\' -H \'Content-Type: application/json-patch+json\' -H \'Prefer: return=minimal\'';
INCLUDE helpers/api_command.tt cmd=cmd extended=1 level=level+3;
request = [
'PATCH /api/systemcontacts/10 HTTP/1.1',
'Content-Type: application/json-patch+json',
'Prefer: return=minimal',
'',
'[
{
"op": "replace",
"path": "/email",
"value": "other.john.doe@reseller.invalid"
},
{
"op": "replace",
"path": "/street",
"value": "Some Street"
}
]',
];
response = [
'HTTP/1.1 204 No Content',
'Preference-Applied: return=minimal',
];
INCLUDE helpers/api_req_res.tt request=request response=response level=level+3;
-%]
<p>
Note that for PATCH, the body must be a JSON Array, even if there is only one 'op' entry.
</p>
</p>
</div>
[% # vim: set tabstop=4 syntax=html expandtab: -%]

@ -3,10 +3,10 @@
[% IF extended -%]
<p>
Note that you <b>MUST</b> to pass the following additional headers for this request:
Note that you <b>MUST</b> pass the following additional headers for this request:
<ul>
<li><b>Prefer</b>
<p>This header tells the API which type of response it expects. The following values are possible:
<p>This header tells the API which type of response the client expects. The following values are possible:
<ul>
<li><b>return=minimal</b> tells the API to respond with a <b>204 No Content</b> if the operation has been performed successfully, without returning the updated resource in the body.</li>
<li><b>return=representation</b> will let the API respond with <b>200 OK</b> with the full representation of the updated resource in the body.</li>

Loading…
Cancel
Save