Skip to content

Commit

Permalink
Merge pull request #1549 from yeti-switch/1539-phonesystems-account-s…
Browse files Browse the repository at this point in the history
…ervice-provisioner

1539, Phone.Systems account Service provisioner
  • Loading branch information
dmitry-sinina authored Sep 19, 2024
2 parents 39cfde9 + 3ba0507 commit 35b05c6
Show file tree
Hide file tree
Showing 11 changed files with 535 additions and 0 deletions.
9 changes: 9 additions & 0 deletions app/admin/billing/services.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
ActiveAdmin.register Billing::Service, as: 'Services' do
menu parent: 'Billing', label: 'Services', priority: 30

controller do
def create_resource(object)
object.save
rescue Billing::Provisioning::Errors::Error => e
flash[:warning] = e.message
false
end
end

acts_as_audit
acts_as_clone
acts_as_safe_destroy
Expand Down
31 changes: 31 additions & 0 deletions app/models/billing/provisioning/phone_systems.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Billing
module Provisioning
class PhoneSystems < Base
def verify_service_variables!
contract = ServiceVariablesContract.new
result = contract.call(service.variables)
raise Billing::Provisioning::Errors::InvalidVariablesError, result.errors.to_h unless result.success?

service.variables
end

def after_create
CustomerService.create_customer(service)
end

def before_destroy
CustomerService.delete_customer(service)
end

def self.verify_service_type_variables!(service_type)
contract = ServiceTypeVariablesContract.new
result = contract.call(service_type.variables)
raise Billing::Provisioning::Errors::InvalidVariablesError, result.errors.to_h unless result.success?

service_type.variables
end
end
end
end
43 changes: 43 additions & 0 deletions app/models/billing/provisioning/phone_systems/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Billing::Provisioning::PhoneSystems

This module is responsible for provisioning customers and services through the phone.systems platform.
It provides an interface for creating and deleting customers in an external VoIP or telecom system through
REST API interactions. The code follows clean architecture principles, making it easy to extend and maintain.

## Overview

The `Billing::Provisioning::PhoneSystems` namespace handles communication with the external `phone.systems` API.
This includes managing customer data such as creating new customers, deleting customers, and sending necessary service
variables for the telecom center.

The main classes and schemas in this module ensure data validation, API interaction,
and seamless handling of customer records.

## Key Components

- `CustomerService`: Manages operations for creating and deleting customers on phone.systems.
It sends payloads to the API and processes the response.
- `PhoneSystemsApiClient`: Handles HTTP requests to the phone.systems API. It sends POST and DELETE requests for
creating and deleting customers with the appropriate authentication and headers.
- `ServiceTypeSchema` and `ServiceVariablesSchema`: These define validation rules for the service data
(like endpoint, username, password, and customer attributes). These schemas ensure only valid data is sent to the
phone.systems API.
- `ServiceTypeVariablesContract` and `ServiceVariablesContract`: These contracts apply the validation logic from the
schemas, ensuring consistency in data formats and preventing invalid data from being sent to the API.

## ServiceTypeSchema Example

```json
{
endpoint: 'https://api.sandbox.telecom.center',
username: 'test',
password: 'test',
attributes: {
name: 'Customer Name',
language: 'EN',
capacity_limit: 100,
"trm_mode": 'operator',
sip_account_limit: 50
}
}
```
67 changes: 67 additions & 0 deletions app/models/billing/provisioning/phone_systems/customer_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module Billing
module Provisioning
class PhoneSystems
class CustomerService
def initialize(service)
@service = service
@service_variables = service.type.variables.merge(service.variables)
@api_client = PhoneSystemsApiClient.new(@service_variables)
end

def create_customer
payload = {
data: {
id: @service.id,
type: 'customers',
attributes: @service_variables['attributes']
}
}

response = @api_client.create_customer(payload)
process_response(response, 'create') do |response_body|
@service.update(id: response_body.dig('data', 'id'))
end
end

def delete_customer
response = @api_client.delete_customer(@service.id)
process_response(response, 'delete')
end

def self.delete_customer(service)
new(service).delete_customer
end

def self.create_customer(service)
new(service).create_customer
end

private

def process_response(response, action)
if response.success?
Rails.logger.info "Customer #{action}d successfully on telecom.center"
yield JSON.parse(response.body) if block_given?
else
handle_error(response)
end
end

def handle_error(response)
error_message = retrieve_validation_error(response)
Rails.logger.error error_message
raise Billing::Provisioning::Errors::Error, error_message
end

def retrieve_validation_error(response)
response_body = JSON.parse(response.body)
response_body.dig('errors', 0, 'detail')
rescue StandardError
'Unknown error'
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

module Billing
module Provisioning
class PhoneSystems
class PhoneSystemsApiClient
def initialize(service_variables)
@debug = service_variables.fetch(:debug, true)
@api_endpoint = service_variables['endpoint'].chomp('/')
@api_credentials = {
username: service_variables['username'],
password: service_variables['password']
}
end

def create_customer(payload)
post_request('/api/rest/public/operator/customers', payload)
end

def delete_customer(customer_id)
delete_request("/api/rest/public/operator/customers/#{customer_id}")
end

private

def post_request(path, payload)
HTTParty.post(
"#{@api_endpoint}#{path}",
body: payload.to_json,
basic_auth: @api_credentials,
headers: { 'Content-Type' => 'application/vnd.api+json' },
debug_output: @debug ? $stdout : false
)
end

def delete_request(path)
HTTParty.delete(
"#{@api_endpoint}#{path}",
basic_auth: @api_credentials,
headers: { 'Content-Type' => 'application/vnd.api+json' },
debug_output: @debug ? $stdout : false
)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Billing
module Provisioning
class PhoneSystems
ServiceTypeSchema = Dry::Schema.JSON do
required(:endpoint).filled(:string)
required(:username).filled(:string)
required(:password).filled(:string)

required(:attributes).filled(:hash).schema do
required(:name).filled(:string)
optional(:language).filled(:string)
optional(:trm_mode).filled(:string)
optional(:capacity_limit).value(:integer)
optional(:sip_account_limit).value(:integer)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Billing
module Provisioning
class PhoneSystems
class ServiceTypeVariablesContract < Dry::Validation::Contract
json(ServiceTypeSchema)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Billing
module Provisioning
class PhoneSystems
class ServiceVariablesContract < Dry::Validation::Contract
json(ServiceVariablesSchema)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Billing
module Provisioning
class PhoneSystems
ServiceVariablesSchema = Dry::Schema.JSON do
optional(:endpoint).filled(:string)
optional(:username).filled(:string)
optional(:password).filled(:string)

required(:attributes).filled(:hash).schema do
required(:name).filled(:string)
optional(:language).filled(:string)
optional(:trm_mode).filled(:string)
optional(:capacity_limit).value(:integer)
optional(:sip_account_limit).value(:integer)
end
end
end
end
end
Loading

0 comments on commit 35b05c6

Please sign in to comment.