Skip to content

Commit

Permalink
#1592, Customer API controller to open phone.systems UI according to …
Browse files Browse the repository at this point in the history
…provisioned service (#1600)
  • Loading branch information
Ivanov-Anton authored Nov 18, 2024
1 parent f9a75d1 commit 2b8a943
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Api
module Rest
module Customer
module V1
PhoneSystemsSessionsController = Class.new(Api::Rest::Customer::V1::BaseController)
end
end
end
end
46 changes: 46 additions & 0 deletions app/forms/phone_systems_session_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

class PhoneSystemsSessionForm < ApplicationForm
attr_reader :uuid
attr_accessor :customer
attr_accessor :api_access

attribute :service
attribute :phone_systems_url

def persisted?
false
end

validate do
errors.add(:base, 'service not found') if service_relation.nil? || customer_id_from_current_service != customer_id_from_session

if service_relation && api_access.account_ids.any? && api_access.account_ids.exclude?(service_relation.account_id)
errors.add(:service, 'Account of current Service is not related to current API Access')
end
end

private

def _save
self.phone_systems_url = Billing::Provisioning::PhoneSystems::SessionCreationService.call!(service_relation)

@uuid = SecureRandom.uuid
rescue Billing::Provisioning::Errors::Error => e
errors.add(:base, 'failed to create Phone Systems session')
end

def service_relation
Billing::Service.find_by(uuid: service)
end

def customer_id_from_current_service
return if service_relation.nil?

service_relation.account.contractor_id
end

def customer_id_from_session
customer.id
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def create_route(payload)
post_request('/api/rest/public/operator/termination_routes', payload)
end

def create_session(payload)
post_request('/api/rest/public/operator/sessions', payload)
end

def process_response(response, action)
if response.success?
Rails.logger.info "#{action} successfully on telecom.center"
Expand Down Expand Up @@ -82,6 +86,8 @@ def post_request(path, payload)
headers: { 'Content-Type' => 'application/vnd.api+json' },
debug_output: @debug ? $stdout : false
)
rescue Socket::ResolutionError => e
raise Billing::Provisioning::Errors::Error, e.message
end

def delete_request(path)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

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

def self.call!(service)
new(service).call!
end

def call!
url = ''
payload = {
data: {
type: 'sessions',
relationships: {
customer: {
data: {
type: 'customers',
id: @service.id
}
}
}
}
}

response = @api_client.create_session(payload)
@api_client.process_response(response, 'created the "Customer sessions" on the phone.systems server') do |response_body|
url = response_body.dig('data', 'attributes', 'uri')
end

url
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

module Api
module Rest
module Customer
module V1
class PhoneSystemsSessionResource < Api::Rest::Customer::V1::BaseResource
exclude_links [:self]
model_name 'PhoneSystemsSessionForm'

attribute :service
attribute :phone_systems_url

before_replace_fields do
_model.customer = context[:current_customer].customer
_model.api_access = context[:current_customer]
end

def self.creatable_fields(_context)
[:service]
end

def fetchable_fields
[:phone_systems_url]
end
end
end
end
end
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def dasherized_resources(name, options = {}, &block)
jsonapi_resources :countries, only: %i[index show]
jsonapi_resources :services, only: %i[index show]
jsonapi_resources :transactions, only: %i[index show]
jsonapi_resource :phone_systems_sessions, only: %i[create]
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

require 'rspec_api_documentation/dsl'

RSpec.resource 'Phone Systems session', document: :customer_v1 do
header 'Accept', 'application/vnd.api+json'
header 'Content-Type', 'application/vnd.api+json'
header 'Authorization', :auth_token

let(:api_access) { create :api_access }
let(:customer) { api_access.customer }
let!(:account) { create(:account, contractor: customer) }
include_context :customer_v1_cookie_helpers
include_context :stub_request_to_create_phone_systems_session
let(:service_id) { service_relation.id }
let(:auth_token) { build_customer_token(api_access.id, expiration: 1.minute.from_now) }
let(:type) { 'phone-systems-sessions' }

post '/api/rest/customer/v1/phone-systems-sessions' do
parameter :type, scope: :data, required: true
jsonapi_attribute :service

let(:service) { service_relation.uuid }
let(:service_type_attrs) do
{
variables: {
endpoint: phone_systems_base_url,
username: 'test',
password: 'test',
# Attributes for Customer on the phone.systems side
attributes: {
name: 'John Johnson',
language: 'EN',
trm_mode: 'operator',
capacity_limit: 10,
sip_account_limit: 5
}
}
}
end
let(:service_type) { FactoryBot.create(:service_type, service_type_attrs) }
let(:service_relation_attrs) { { type: service_type, account:, variables: { attributes: { name: 'John Doe' } } } }
let(:service_relation) { FactoryBot.create(:service, service_relation_attrs) }

before { stub_request_to_create_phone_systems_session }

example_request 'create new phone.systems session' do
expect(status).to eq(201)
expect(stub_request_to_create_phone_systems_session).to have_been_requested
end
end
end
94 changes: 94 additions & 0 deletions spec/forms/phone_systems_session_form_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# frozen_string_literal: true

RSpec.describe PhoneSystemsSessionForm do
subject { form.save }

include_context :stub_request_to_create_phone_systems_session

let(:customer) { create(:customer) }
let(:account) { create(:account, contractor: customer) }
let(:service_type_attrs) { { variables: { endpoint: phone_systems_base_url } } }
let(:service_type) { FactoryBot.create(:service_type, service_type_attrs) }
let(:service_attrs) { { type: service_type, account: } }
let(:service) { FactoryBot.create(:service, service_attrs) }
let(:api_access_attrs) { { account_ids: [account.id], customer: } }
let(:api_access) { create :api_access, api_access_attrs }
let(:form_attributes) { { service: service.uuid } }
let(:form) do
f = described_class.new(form_attributes)
f.customer = service.account.contractor
f.api_access = api_access
f
end

context 'when valid data' do
before { stub_request_to_create_phone_systems_session }

it 'should create session on the phone.systems side and URL should be received' do
subject

expect(form.errors.messages).to eq({})
expect(form.phone_systems_url).to eq phone_systems_redirect_url
expect(stub_request_to_create_phone_systems_session).to have_been_requested
end
end

context 'when service not found' do
let(:form_attributes) { { service: SecureRandom.uuid } }

it 'should return validation error message' do
subject

expect(form.errors.messages).to eq base: ['service not found']
end
end

context 'when service from another Customer' do
let!(:another_customer) { create(:customer) }
let!(:another_account) { create(:account, contractor: another_customer) }
let(:api_access_attrs) { { account_ids: [another_account.id], customer: another_customer } }
let(:form_attributes) { { service: service.uuid } }

let(:form) do
f = described_class.new(form_attributes)
f.customer = another_customer
f.api_access = api_access
f
end

it 'should return validation error message' do
subject

expect(form.errors.messages).to match_array(
base: ['service not found'],
service: ['Account of current Service is not related to current API Access']
)
end
end

context 'when current API Access with account_ids = []' do
let(:api_access_attrs) { super().merge account_ids: [] }

before { stub_request_to_create_phone_systems_session }

it 'should create session on the phone.systems side and URL should be received' do
subject

expect(form.errors.messages).to eq({})
expect(form.phone_systems_url).to eq phone_systems_redirect_url
expect(stub_request_to_create_phone_systems_session).to have_been_requested
end
end

context 'when Account of Service is NOT included in list of allowed account IDs ' do
let(:second_account) { create(:account, contractor: customer, name: 'Second Account') }
let(:api_access_attrs) { super().merge account_ids: [account.id] }
let(:service_attrs) { super().merge account: second_account }

it 'should return validation error' do
subject

expect(form.errors.messages).to eq service: ['Account of current Service is not related to current API Access']
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

RSpec.shared_context :stub_request_to_create_phone_systems_session do
let(:phone_systems_base_url) { 'https://api.telecom.center' }
let(:phone_systems_query_params) { { token: 'asdf.asdf.asdf', redirect_to: :phone_systems, from: :rspec } }
let(:phone_systems_redirect_url) { "https://sandbox.phone.systems/auth#{phone_systems_query_params.to_query}" }
# the Customer ID from Phone Systems and The service ID from Yeti WEB should be the same
let(:service_id) { service.id }
let(:response_from_phone_systems) do
{
data: {
id: 99,
type: :sessions,
attributes: {
uri: phone_systems_redirect_url
}
}
}
end
let(:stub_request_to_create_phone_systems_session) do
stub_request(:post, "#{phone_systems_base_url}/api/rest/public/operator/sessions")
.with(
body: { data: { type: 'sessions', relationships: { customer: { data: { type: :customers, id: service_id } } } } }.to_json
)
.to_return(
status: 200, body: response_from_phone_systems.to_json, headers: {}
)
end
end

0 comments on commit 2b8a943

Please sign in to comment.