MT#3933 API: tighten subscriber creation.

agranig/pbxapi
Andreas Granig 12 years ago
parent aef3a499a3
commit d508a519c8

@ -121,7 +121,8 @@ sub OPTIONS :Allow {
sub POST :Allow {
my ($self, $c) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
my $schema = $c->model('DB');
my $guard = $schema->txn_scope_guard;
{
my $resource = $self->get_valid_post_data(
c => $c,
@ -179,37 +180,8 @@ sub POST :Allow {
}
}
my $customer = NGCP::Panel::Utils::Contract::get_contract_rs(
schema => $c->model('DB'),
);
$customer = $customer->search({
'contact.reseller_id' => { '-not' => undef },
'me.id' => $resource->{contract_id},
},{
join => 'contact'
});
$customer = $customer->search({
'-or' => [
'product.class' => 'sipaccount',
'product.class' => 'pbxaccount',
],
},{
join => {'billing_mappings' => 'product' },
'+select' => 'billing_mappings.id',
'+as' => 'bmid',
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$customer = $customer->search({
'contact.reseller_id' => $c->user->reseller_id,
});
}
$customer = $customer->first;
unless($customer) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'customer_id', doesn't exist.");
last;
}
my $customer = $self->get_customer($c, $resource->{contract_id});
last unless($customer);
if(defined $customer->max_subscribers && $customer->voip_subscribers->search({
status => { '!=' => 'terminated' }
})->count >= $customer->max_subscribers) {
@ -218,7 +190,76 @@ sub POST :Allow {
last;
}
# TODO: check if number is already taken
my $preferences = {};
my $admin = 0;
unless($customer->get_column('product_class') eq 'pbxaccount') {
delete $resource->{is_pbx_group};
delete $resource->{pbx_group_id};
$admin = $resource->{admin} // 0;
} elsif($c->config->{features}->{cloudpbx}) {
my $subs = NGCP::Panel::Utils::Subscriber::get_custom_subscriber_struct(
c => $c,
contract => $customer,
show_locked => 1,
);
use Data::Printer; say ">>>>>>>>>>>>>>>>>>>> subs"; p $subs;
my $admin_subscribers = NGCP::Panel::Utils::Subscriber::get_admin_subscribers(
voip_subscribers => $subs->{subscribers});
unless(@{ $admin_subscribers }) {
$admin = $resource->{admin} // 1;
} else {
$admin = $resource->{admin} // 0;
}
$preferences->{shared_buddylist_visibility} = 1;
$preferences->{display_name} = $resource->{display_name}
if(defined $resource->{display_name});
my $default_sound_set = $customer->voip_sound_sets
->search({ contract_default => 1 })->first;
if($default_sound_set) {
$preferences->{contract_sound_set} = $default_sound_set->id;
}
my $admin_subscriber = $admin_subscribers->[0];
my $base_number = $admin_subscriber->{primary_number};
if($base_number) {
$preferences->{cloud_pbx_base_cli} = $base_number->{cc} . $base_number->{ac} . $base_number->{sn};
}
}
my $billing_profile = $self->get_billing_profile($c, $customer);
last unless($billing_profile);
if($billing_profile->prepaid) {
$preferences->{prepaid} = 1;
}
my $subscriber = $c->model('DB')->resultset('voip_subscribers')->find({
username => $resource->{username},
domain_id => $resource->{domain_id},
status => { '!=' => 'terminated' },
});
if($subscriber) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Subscriber already exists.");
last;
}
my $alias_numbers = [];
if(ref $resource->{alias_numbers} eq "ARRAY") {
foreach my $num(@{ $resource->{alias_numbers} }) {
unless(ref $num eq "HASH") {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid parameter 'alias_numbers', must be hash or array of hashes.");
last;
}
push @{ $alias_numbers }, { e164 => $num };
}
} elsif(ref $resource->{alias_numbers} eq "HASH") {
push @{ $alias_numbers }, { e164 => $resource->{alias_numbers} };
} else {
use Data::Printer; p $resource->{alias_numbers}; say ">>>>>>>>>>> '".(ref $resource->{alias_numbers})."'";
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid parameter 'alias_numbers', must be hash or array of hashes.");
last;
}
# TODO: handle pbx subscribers:
# extension
@ -227,79 +268,35 @@ sub POST :Allow {
# TODO: handle status != active
my $subscriber;
try {
my ($uuid_bin, $uuid_string);
UUID::generate($uuid_bin);
UUID::unparse($uuid_bin, $uuid_string);
my $rs = $self->item_rs($c);
$subscriber = $rs->create({
contract_id => $customer->id,
uuid => $uuid_string,
username => $resource->{username},
domain_id => $domain->id,
status => $resource->{status},
});
my $prov_subscriber = $c->model('DB')->resultset('provisioning_voip_subscribers')->create({
uuid => $uuid_string,
username => $resource->{username},
password => $resource->{password},
webusername => $resource->{webusername},
webpassword => $resource->{webpassword},
admin => $resource->{administrative},
account_id => $customer->id,
domain_id => $domain->provisioning_voip_domain->id,
create_timestamp => NGCP::Panel::Utils::DateTime::current_local,
});
$subscriber = NGCP::Panel::Utils::Subscriber::create_subscriber(
c => $c,
schema => $schema,
contract => $customer,
params => $resource,
admin_default => $admin,
preferences => $preferences,
);
NGCP::Panel::Utils::Subscriber::update_subscriber_numbers(
schema => $c->model('DB'),
primary_number => $resource->{e164},
alias_numbers => $alias_numbers,
reseller_id => $customer->contact->reseller_id,
subscriber_id => $subscriber->id,
);
$subscriber->discard_changes; # reload row because of new number
my $voip_preferences = $c->model('DB')->resultset('voip_preferences')->search({
'usr_pref' => 1,
});
$voip_preferences->find({ 'attribute' => 'account_id' })
->voip_usr_preferences->create({
'subscriber_id' => $prov_subscriber->id,
'value' => $customer->id,
});
my $cli;
if($subscriber->primary_number) {
$voip_preferences->find({ 'attribute' => 'ac' })
->voip_usr_preferences->create({
'subscriber_id' => $prov_subscriber->id,
'value' => $subscriber->primary_number->ac,
});
$voip_preferences->find({ 'attribute' => 'cc' })
->voip_usr_preferences->create({
'subscriber_id' => $prov_subscriber->id,
'value' => $subscriber->primary_number->cc,
});
$cli = $subscriber->primary_number->cc .
($subscriber->primary_number->ac // '').
$subscriber->primary_number->sn;
$voip_preferences->find({ 'attribute' => 'cli' })
->voip_usr_preferences->create({
'subscriber_id' => $prov_subscriber->id,
'value' => $cli,
});
}
$c->model('DB')->resultset('voicemail_users')->create({
customer_id => $uuid_string,
mailbox => ($cli // 0),
password => sprintf("%04d", int(rand 10000)),
email => '',
});
# TODO: pbx prefs (group handling, display name, extension etc)
} catch(DBIx::Class::Exception $e where { /Duplicate entry '([^']+)' for key 'number_idx'/ }) {
$e =~ /Duplicate entry '([^']+)' for key 'number_idx'/;
$c->log->error("failed to create subscribere, number $1 already exists"); # TODO: user, message, trace, ...
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number '$1' already exists.");
last;
} catch($e) {
$c->log->error("failed to create subscriber: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create subscriber.");

@ -573,6 +573,9 @@ sub subscriber_create :Chained('base') :PathPart('subscriber/create') :Args(0) {
$preferences->{display_name} = $form->params->{display_name}
if($form->params->{display_name});
}
if($c->stash->{billing_mapping}->billing_profile->prepaid) {
$preferences->{prepaid} = 1;
}
$billing_subscriber = NGCP::Panel::Utils::Subscriber::create_subscriber(
c => $c,
schema => $schema,
@ -582,16 +585,6 @@ sub subscriber_create :Chained('base') :PathPart('subscriber/create') :Args(0) {
preferences => $preferences,
);
if($c->stash->{billing_mapping}->billing_profile->prepaid) {
my $pref = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
c => $c, attribute => 'prepaid', prov_subscriber => $billing_subscriber->provisioning_voip_subscriber);
if($pref->first) {
$pref->first->update({ 'value' => 1 });
} else {
$pref->create({ 'value' => 1 });
}
}
NGCP::Panel::Utils::Subscriber::update_pbx_group_prefs(
c => $c,
schema => $schema,

@ -2496,6 +2496,7 @@ sub create_registered :Chained('master') :PathPart('registered/create') :Args(0)
my $s = $c->stash->{subscriber}->provisioning_voip_subscriber;
my $aor = $s->username . '@' . $s->domain->domain;
my $contact = $form->field('contact')->value;
my $q = $form->field('q')->value;
my $path = $c->config->{sip}->{path} || '<sip:127.0.0.1:5060;lr>';
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
$ret = $dispatcher->dispatch("proxy-ng", 1, 1, <<EOF );
@ -2507,7 +2508,7 @@ sub create_registered :Chained('master') :PathPart('registered/create') :Args(0)
<param><value><string>$aor</string></value></param>
<param><value><string>$contact</string></value></param>
<param><value><int>0</int></value></param>
<param><value><double>1.00</double></value></param>
<param><value><double>$q</double></value></param>
<param><value><string><![CDATA[$path]]></string></value></param>
<param><value><int>0</int></value></param>
<param><value><int>0</int></value></param>

@ -10,7 +10,7 @@ for (qw(is_superuser lawful_intercept)) {
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
label => 'Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_block 'fields' => (
tag => 'div',

@ -44,7 +44,7 @@ has_field 'direction' => (
has_field 'billing_zone' => (
type => '+NGCP::Panel::Field::BillingZone',
label => 'Zone',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'onpeak_init_rate' => (

@ -5,7 +5,7 @@ extends 'NGCP::Panel::Form::BillingProfile::Reseller';
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_block 'fields' => (

@ -6,7 +6,7 @@ use Moose::Util::TypeConstraints;
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_block 'fields' => (

@ -14,12 +14,12 @@ sub build_form_element_class { [qw/form-horizontal/] }
has_field 'contact' => (
type => '+NGCP::Panel::Field::Contact',
label => 'Contact',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'billing_profile' => (
type => '+NGCP::Panel::Field::BillingProfile',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'status' => (

@ -6,7 +6,7 @@ extends 'NGCP::Panel::Form::Contract::Basic';
has_field 'product' => (
type => '+NGCP::Panel::Field::Product',
label => 'Product',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'max_subscribers' => (

@ -16,7 +16,7 @@ has_field 'e164' => (
has_field 'domain' => (
type => '+NGCP::Panel::Field::Domain',
label => 'SIP Domain',
not_nullable => 1,
validate_when_empty => 1,
);
has_block 'fields' => (

@ -7,7 +7,7 @@ extends 'NGCP::Panel::Form::Customer::PbxSubscriber';
has_field 'group' => (
type => '+NGCP::Panel::Field::PbxGroup',
label => 'Group',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'extension' => (

@ -7,7 +7,7 @@ extends 'NGCP::Panel::Form::Customer::PbxSubscriber';
has_field 'group' => (
type => '+NGCP::Panel::Field::PbxGroup',
label => 'Group',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'extension' => (

@ -68,7 +68,7 @@ has_field 'password' => (
has_field 'status' => (
type => '+NGCP::Panel::Field::SubscriberStatusSelect',
label => 'Status',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'external_id' => (

@ -58,7 +58,7 @@ has_field 'username' => (
has_field 'domain' => (
type => '+NGCP::Panel::Field::Domain',
label => 'SIP Domain',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'password' => (
@ -74,7 +74,7 @@ has_field 'password' => (
has_field 'status' => (
type => '+NGCP::Panel::Field::SubscriberStatusSelect',
label => 'Status',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'administrative' => (

@ -13,7 +13,7 @@ sub build_form_element_class {[qw(form-horizontal)]}
has_field 'device' => (
type => '+NGCP::Panel::Field::Device',
not_nullable => 1,
validate_when_empty => 1,
label => 'Device Model',
);

@ -14,7 +14,7 @@ sub build_form_element_class {[qw(form-horizontal)]}
has_field 'device' => (
type => '+NGCP::Panel::Field::Device',
not_nullable => 1,
validate_when_empty => 1,
label => 'Device Model',
);

@ -13,7 +13,7 @@ sub build_form_element_class {[qw(form-horizontal)]}
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'save' => (

@ -15,7 +15,7 @@ sub build_form_element_class {[qw(form-horizontal)]}
has_field 'config' => (
type => '+NGCP::Panel::Field::DeviceConfig',
not_nullable => 1,
validate_when_empty => 1,
label => 'Device Configuration',
);

@ -6,7 +6,7 @@ use Moose::Util::TypeConstraints;
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_block 'fields' => (

@ -6,7 +6,7 @@ use Moose::Util::TypeConstraints;
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_block 'fields' => (

@ -19,7 +19,7 @@ has_field 'id' => (
has_field 'contract' => (
type => '+NGCP::Panel::Field::Contract',
label => 'Contract',
not_nullable => 1,
validate_when_empty => 1,
);

@ -14,7 +14,7 @@ sub build_form_element_class { [qw/form-horizontal/] }
has_field 'contract' => (
type => '+NGCP::Panel::Field::ResellerContract',
label => 'Contract',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'name' => (

@ -6,7 +6,7 @@ use Moose::Util::TypeConstraints;
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_block 'fields' => (

@ -6,13 +6,13 @@ use Moose::Util::TypeConstraints;
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'contract' => (
type => '+NGCP::Panel::Field::CustomerContract',
label => 'Customer',
not_nullable => 0,
validate_when_empty => 0,
);
has_block 'fields' => (

@ -14,7 +14,7 @@ sub build_form_element_class {[qw(form-horizontal)]}
has_field 'contract' => (
type => '+NGCP::Panel::Field::CustomerContract',
label => 'Customer',
not_nullable => 0,
validate_when_empty => 0,
);
has_field 'name' => (

@ -19,7 +19,7 @@ sub build_form_element_class {[qw(form-horizontal)]}
has_field 'contract' => (
type => '+NGCP::Panel::Field::CustomerContract',
label => 'Customer',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'webusername' => (
@ -66,7 +66,7 @@ has_field 'username' => (
has_field 'domain' => (
type => '+NGCP::Panel::Field::Domain',
label => 'SIP Domain',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'password' => (
@ -83,7 +83,6 @@ has_field 'password' => (
has_field 'status' => (
type => '+NGCP::Panel::Field::SubscriberStatusSelect',
label => 'Status',
not_nullable => 1,
);
has_field 'administrative' => (

@ -15,6 +15,24 @@ has_field 'contact' => (
type => 'Text',
label => 'Contact URI',
required => 1,
element_attr => {
rel => ['tooltip'],
title => ['A full SIP URI like sip:user@ip:port']
},
);
has_field 'q' => (
type => 'Float',
label => 'Priority (q-value)',
required => 1,
range_start => -1,
range_end => 1,
decimal_symbol => '.',
default => 1,
element_attr => {
rel => ['tooltip'],
title => ['The contact priority for serial forking (float value, higher is stronger) between -1.00 to 1.00']
},
);
has_field 'save' => (
@ -27,7 +45,7 @@ has_field 'save' => (
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/contact/],
render_list => [qw/contact q/],
);
has_block 'actions' => (

@ -21,7 +21,7 @@ sub build_form_element_class {[qw(form-horizontal)]}
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
label => 'Reseller',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'webusername' => (
@ -87,13 +87,13 @@ has_field 'password' => (
has_field 'lock' => (
type => '+NGCP::Panel::Field::SubscriberLockSelect',
label => 'Lock Level',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'status' => (
type => '+NGCP::Panel::Field::SubscriberStatusSelect',
label => 'Status',
not_nullable => 1,
validate_when_empty => 1,
);
has_field 'administrative' => (

@ -8,20 +8,28 @@ use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use JSON::Types;
use NGCP::Panel::Form::Subscriber;
use NGCP::Panel::Form::Subscriber::SubscriberAPI;
use NGCP::Panel::Utils::XMLDispatcher;
use NGCP::Panel::Utils::Prosody;
sub get_form {
my ($self, $c) = @_;
# TODO: should be according to customer type (pbx?)
return NGCP::Panel::Form::Subscriber->new;
return NGCP::Panel::Form::Subscriber::SubscriberAPI->new;
}
sub hal_from_item {
my ($self, $c, $item, $form) = @_;
my %resource = $item->get_inflated_columns;
my $bill_resource = { $item->get_inflated_columns };
my $prov_resource = { $item->provisioning_voip_subscriber->get_inflated_columns };
my $customer = $self->get_customer($c, $item->contract_id);
delete $prov_resource->{domain_id};
delete $prov_resource->{account_id};
my %resource = %{ $bill_resource->merge($prov_resource) };
unless($customer->get_column('product_class') eq 'pbxaccount') {
delete $resource{is_pbx_group};
delete $resource{pbx_group_id};
}
my $hal = Data::HAL->new(
links => [
@ -36,7 +44,7 @@ sub hal_from_item {
Data::HAL::Link->new(relation => 'self', href => sprintf("%s%d", $self->dispatch_path, $item->id)),
Data::HAL::Link->new(relation => 'ngcp:subscriberpreferences', href => sprintf("/api/subscriberpreferences/%d", $item->id)),
Data::HAL::Link->new(relation => 'ngcp:domains', href => sprintf("/api/domains/%d", $item->domain->id)),
Data::HAL::Link->new(relation => 'ngcp:customers', href => sprintf("/api/customers/%d", $item->contract->id)),
Data::HAL::Link->new(relation => 'ngcp:customers', href => sprintf("/api/customers/%d", $item->contract_id)),
#Data::HAL::Link->new(relation => 'ngcp:registrations', href => sprintf("/api/registrations/%d", $item->contract->id)),
#Data::HAL::Link->new(relation => 'ngcp:trustedsources', href => sprintf("/api/trustedsources/%d", $item->contract->id)),
],
@ -56,6 +64,8 @@ sub hal_from_item {
$resource{id} = int($item->id);
$resource{domain} = $item->domain->domain;
$hal->resource({%resource});
return $hal;
}
@ -84,6 +94,54 @@ sub item_by_id {
return $item_rs->find($id);
}
sub get_customer {
my ($self, $c, $customer_id) = @_;
my $customer = NGCP::Panel::Utils::Contract::get_contract_rs(
schema => $c->model('DB'),
);
$customer = $customer->search({
'contact.reseller_id' => { '-not' => undef },
'me.id' => $customer_id,
},{
join => 'contact'
});
$customer = $customer->search({
'-or' => [
'product.class' => 'sipaccount',
'product.class' => 'pbxaccount',
],
},{
join => {'billing_mappings' => 'product' },
'+select' => [ 'billing_mappings.id', 'product.class' ],
'+as' => [ 'bmid', 'product_class' ],
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$customer = $customer->search({
'contact.reseller_id' => $c->user->reseller_id,
});
}
$customer = $customer->first;
unless($customer) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'customer_id', doesn't exist.");
return;
}
return $customer;
}
sub get_billing_profile {
my ($self, $c, $customer) = @_;
my $mapping = $customer->billing_mappings->find($customer->get_column('bmid'));
if($mapping) {
return $mapping->billing_profile;
} else {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'customer_id', doesn't have a valid billing mapping.");
return;
}
}
=pod
# you can't update a domain per se, only its preferences!
sub update_item {

@ -137,7 +137,8 @@ sub create_subscriber {
my $schema = $params{schema} // $c->model('DB');
my $reseller = $contract->contact->reseller;
my $billing_domain = $schema->resultset('domains')
->find($params->{domain}{id});
->find($params->{domain}{id} // $params->{domain_id});
use Data::Printer; print ">>>>>>>>>>>>>>>>>>>>>>>>>>> billing_dom\n"; p $billing_domain;
my $prov_domain = $schema->resultset('voip_domains')
->find({domain => $billing_domain->domain});

Loading…
Cancel
Save